diff --git a/README.md b/README.md
index 7c996db04..fe380e5ab 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,10 @@ cd stack_wallet
git submodule update --init --recursive
```
-You will need to install all dependencies listed in each of the plugins in the crypto_plugins folder. (eg. [Monero](https://github.com/cypherstack/flutter_libmonero), [Epic Cash](https://github.com/cypherstack/flutter_libepiccash) ) as of Sep 8th 2022 that is:
+Install all dependencies listed in each of the plugins in the crypto_plugins folder (eg. [flutter_libmonero](https://github.com/cypherstack/flutter_libmonero/blob/main/howto-build-android.md), [flutter_libepiccash](https://github.com/cypherstack/flutter_libepiccash) ) as of Oct 3rd 2022 that is:
+```
+sudo apt-get install unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake openjdk-8-jre-headless libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev llvm sudo apt-get install debhelper libclang-dev cargo rustc opencl-headers libssl-dev ocl-icd-opencl-dev
+```
Install [Rust](https://www.rust-lang.org/tools/install)
```
@@ -45,6 +48,26 @@ sudo apt install build-essential debhelper cmake libclang-dev libncurses5-dev cl
sudo apt install unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake openjdk-8-jre-headless
```
+Run prebuild script
+
+```
+cd scripts
+./prebuild.sh
+// when finished go back to the root directory
+cd ..
+```
+
+
+Remove pre-installed system libraries for the following packages built by cryptography plugins in the crypto_plugins folder: `boost iconv libjson-dev libsecret openssl sodium unbound zmq`. You can use
+```
+sudo apt list --installed | grep boost
+```
+for example to find which pre-installed packages you may need to remove with `sudo apt remove`. Be careful, as some packages (especially boost) are linked to GNOME (GUI) packages: when in doubt, remove `-dev` packages first like with
+```
+sudo apt-get remove '^libboost.*-dev.*'
+```
+
+
Building plugins for Android
```
cd scripts/android/
diff --git a/assets/svg/Polygon.svg b/assets/svg/Polygon.svg
new file mode 100644
index 000000000..b79ac9bc3
--- /dev/null
+++ b/assets/svg/Polygon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/svg/box-auto.svg b/assets/svg/box-auto.svg
new file mode 100644
index 000000000..1dd771fb1
--- /dev/null
+++ b/assets/svg/box-auto.svg
@@ -0,0 +1,11 @@
+
diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero
index 8e3afd002..f74f31e2f 160000
--- a/crypto_plugins/flutter_libmonero
+++ b/crypto_plugins/flutter_libmonero
@@ -1 +1 @@
-Subproject commit 8e3afd002968d21a3de788569356587a70818022
+Subproject commit f74f31e2f3b4a7c11907ae5df6cd38505cd25897
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 6756e40c4..1ffbb9415 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -42,6 +42,25 @@ PODS:
- Flutter
- cw_shared_external/Sodium (0.0.1):
- Flutter
+ - cw_wownero (0.0.2):
+ - cw_shared_external
+ - cw_wownero/Boost (= 0.0.2)
+ - cw_wownero/OpenSSL (= 0.0.2)
+ - cw_wownero/Sodium (= 0.0.2)
+ - cw_wownero/Wownero (= 0.0.2)
+ - Flutter
+ - cw_wownero/Boost (0.0.2):
+ - cw_shared_external
+ - Flutter
+ - cw_wownero/OpenSSL (0.0.2):
+ - cw_shared_external
+ - Flutter
+ - cw_wownero/Sodium (0.0.2):
+ - cw_shared_external
+ - Flutter
+ - cw_wownero/Wownero (0.0.2):
+ - cw_shared_external
+ - Flutter
- devicelocale (0.0.1):
- Flutter
- DKImagePickerController/Core (4.3.4):
@@ -127,6 +146,7 @@ DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- cw_monero (from `.symlinks/plugins/cw_monero/ios`)
- cw_shared_external (from `.symlinks/plugins/cw_shared_external/ios`)
+ - cw_wownero (from `.symlinks/plugins/cw_wownero/ios`)
- devicelocale (from `.symlinks/plugins/devicelocale/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`)
@@ -169,6 +189,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/cw_monero/ios"
cw_shared_external:
:path: ".symlinks/plugins/cw_shared_external/ios"
+ cw_wownero:
+ :path: ".symlinks/plugins/cw_wownero/ios"
devicelocale:
:path: ".symlinks/plugins/devicelocale/ios"
file_picker:
@@ -216,6 +238,7 @@ SPEC CHECKSUMS:
connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
cw_monero: 9816991daff0e3ad0a8be140e31933b5526babd4
cw_shared_external: 2972d872b8917603478117c9957dfca611845a92
+ cw_wownero: 08e5713fe311a3be95efd7f3c1bf9d47d9cfafde
devicelocale: b22617f40038496deffba44747101255cee005b0
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 1bfb53e8d..98c80851c 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 46;
+ objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
@@ -253,6 +253,7 @@
"${BUILT_PRODUCTS_DIR}/connectivity_plus/connectivity_plus.framework",
"${BUILT_PRODUCTS_DIR}/cw_monero/cw_monero.framework",
"${BUILT_PRODUCTS_DIR}/cw_shared_external/cw_shared_external.framework",
+ "${BUILT_PRODUCTS_DIR}/cw_wownero/cw_wownero.framework",
"${BUILT_PRODUCTS_DIR}/devicelocale/devicelocale.framework",
"${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework",
"${BUILT_PRODUCTS_DIR}/flutter_libmonero/flutter_libmonero.framework",
@@ -285,6 +286,7 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity_plus.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cw_monero.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cw_shared_external.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cw_wownero.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/devicelocale.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_libmonero.framework",
diff --git a/lib/main.dart b/lib/main.dart
index 0be26c39e..da08c6765 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -84,6 +84,10 @@ void main() async {
if (Platform.isIOS) {
appDirectory = (await getLibraryDirectory());
}
+ if (Platform.isLinux || Logging.isArmLinux) {
+ appDirectory = Directory("${appDirectory.path}/.stackwallet");
+ await appDirectory.create();
+ }
// FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
if (!(Logging.isArmLinux || Logging.isTestEnv)) {
final isar = await Isar.open(
diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_from_date_picker.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_from_date_picker.dart
index 0207a4c61..8a24e95bb 100644
--- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_from_date_picker.dart
+++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_from_date_picker.dart
@@ -42,6 +42,11 @@ class _RestoreFromDatePickerState extends State {
style: STextStyles.field(context),
decoration: InputDecoration(
hintText: "Restore from...",
+ hintStyle: STextStyles.fieldLabel(context).copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textFieldDefaultSearchIconLeft,
+ ),
suffixIcon: UnconstrainedBox(
child: Row(
children: [
diff --git a/lib/pages/address_book_views/subviews/contact_details_view.dart b/lib/pages/address_book_views/subviews/contact_details_view.dart
index 6538af0dc..c0c10b3b1 100644
--- a/lib/pages/address_book_views/subviews/contact_details_view.dart
+++ b/lib/pages/address_book_views/subviews/contact_details_view.dart
@@ -393,13 +393,15 @@ class _ContactDetailsViewState extends ConsumerState {
color: Theme.of(context)
.extension()!
.textFieldDefaultBG,
- padding: const EdgeInsets.all(4),
- child: SvgPicture.asset(Assets.svg.pencil,
- width: 12,
- height: 12,
- color: Theme.of(context)
- .extension()!
- .accentColorDark),
+ padding: const EdgeInsets.all(6),
+ child: SvgPicture.asset(
+ Assets.svg.pencil,
+ width: 14,
+ height: 14,
+ color: Theme.of(context)
+ .extension()!
+ .accentColorDark,
+ ),
),
),
const SizedBox(
@@ -421,13 +423,15 @@ class _ContactDetailsViewState extends ConsumerState {
color: Theme.of(context)
.extension()!
.textFieldDefaultBG,
- padding: const EdgeInsets.all(4),
- child: SvgPicture.asset(Assets.svg.copy,
- width: 12,
- height: 12,
- color: Theme.of(context)
- .extension()!
- .accentColorDark),
+ padding: const EdgeInsets.all(6),
+ child: SvgPicture.asset(
+ Assets.svg.copy,
+ width: 16,
+ height: 16,
+ color: Theme.of(context)
+ .extension()!
+ .accentColorDark,
+ ),
),
),
],
diff --git a/lib/pages/address_book_views/subviews/contact_popup.dart b/lib/pages/address_book_views/subviews/contact_popup.dart
index a14581c0a..67ab32cda 100644
--- a/lib/pages/address_book_views/subviews/contact_popup.dart
+++ b/lib/pages/address_book_views/subviews/contact_popup.dart
@@ -5,6 +5,7 @@ import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/address_book_views/subviews/contact_details_view.dart';
+import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_2_view.dart';
import 'package:stackwallet/pages/send_view/send_view.dart';
import 'package:stackwallet/providers/exchange/exchange_flow_is_active_state_provider.dart';
import 'package:stackwallet/providers/global/address_book_service_provider.dart';
@@ -19,6 +20,9 @@ import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:tuple/tuple.dart';
+final exchangeFromAddressBookAddressStateProvider =
+ StateProvider((ref) => "");
+
class ContactPopUp extends ConsumerWidget {
const ContactPopUp({
Key? key,
@@ -268,11 +272,11 @@ class ContactPopUp extends ConsumerWidget {
color: Theme.of(context)
.extension()!
.textFieldDefaultBG,
- padding: const EdgeInsets.all(4),
+ padding: const EdgeInsets.all(6),
child: SvgPicture.asset(
Assets.svg.copy,
- width: 12,
- height: 12,
+ width: 16,
+ height: 16,
color: Theme.of(context)
.extension()!
.accentColorDark),
@@ -280,6 +284,45 @@ class ContactPopUp extends ConsumerWidget {
),
],
),
+ if (isExchangeFlow)
+ const SizedBox(
+ width: 6,
+ ),
+ if (isExchangeFlow)
+ Column(
+ children: [
+ const SizedBox(
+ height: 2,
+ ),
+ GestureDetector(
+ onTap: () {
+ ref
+ .read(
+ exchangeFromAddressBookAddressStateProvider
+ .state)
+ .state = e.address;
+ Navigator.of(context).popUntil(
+ ModalRoute.withName(
+ Step2View.routeName));
+ },
+ child: RoundedContainer(
+ color: Theme.of(context)
+ .extension()!
+ .textFieldDefaultBG,
+ padding:
+ const EdgeInsets.all(6),
+ child: SvgPicture.asset(
+ Assets.svg.chevronRight,
+ width: 16,
+ height: 16,
+ color: Theme.of(context)
+ .extension<
+ StackColors>()!
+ .accentColorDark),
+ ),
+ ),
+ ],
+ ),
if (contact.id != "default" &&
hasActiveWallet &&
!isExchangeFlow)
diff --git a/lib/pages/exchange_view/choose_from_stack_view.dart b/lib/pages/exchange_view/choose_from_stack_view.dart
new file mode 100644
index 000000000..f54a7552c
--- /dev/null
+++ b/lib/pages/exchange_view/choose_from_stack_view.dart
@@ -0,0 +1,128 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:stackwallet/providers/providers.dart';
+import 'package:stackwallet/utilities/constants.dart';
+import 'package:stackwallet/utilities/enums/coin_enum.dart';
+import 'package:stackwallet/utilities/text_styles.dart';
+import 'package:stackwallet/utilities/theme/stack_colors.dart';
+import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
+import 'package:stackwallet/widgets/rounded_white_container.dart';
+import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance_future.dart';
+import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_coin_icon.dart';
+
+class ChooseFromStackView extends ConsumerStatefulWidget {
+ const ChooseFromStackView({
+ Key? key,
+ required this.coin,
+ }) : super(key: key);
+
+ final Coin coin;
+
+ static const String routeName = "/chooseFromStack";
+
+ @override
+ ConsumerState createState() =>
+ _ChooseFromStackViewState();
+}
+
+class _ChooseFromStackViewState extends ConsumerState {
+ late final Coin coin;
+
+ @override
+ void initState() {
+ coin = widget.coin;
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final walletIds = ref.watch(walletsChangeNotifierProvider
+ .select((value) => value.getWalletIdsFor(coin: coin)));
+
+ return Scaffold(
+ backgroundColor: Theme.of(context).extension()!.background,
+ appBar: AppBar(
+ leading: const AppBarBackButton(),
+ title: Text(
+ "Choose your ${coin.ticker.toUpperCase()} wallet",
+ style: STextStyles.navBarTitle(context),
+ ),
+ ),
+ body: Padding(
+ padding: const EdgeInsets.all(16),
+ child: walletIds.isEmpty
+ ? Column(
+ children: [
+ RoundedWhiteContainer(
+ child: Center(
+ child: Text(
+ "No ${coin.ticker.toUpperCase()} wallets",
+ style: STextStyles.itemSubtitle(context),
+ ),
+ ),
+ ),
+ ],
+ )
+ : ListView.builder(
+ itemCount: walletIds.length,
+ itemBuilder: (context, index) {
+ final manager = ref.watch(walletsChangeNotifierProvider
+ .select((value) => value.getManager(walletIds[index])));
+
+ return Padding(
+ padding: const EdgeInsets.symmetric(vertical: 5.0),
+ child: RawMaterialButton(
+ splashColor:
+ Theme.of(context).extension()!.highlight,
+ materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ ),
+ padding: const EdgeInsets.all(0),
+ // color: Theme.of(context).extension()!.popupBG,
+ elevation: 0,
+ onPressed: () async {
+ if (mounted) {
+ Navigator.of(context).pop(manager.walletId);
+ }
+ },
+ child: RoundedWhiteContainer(
+ // color: Colors.transparent,
+ child: Row(
+ children: [
+ WalletInfoCoinIcon(coin: coin),
+ const SizedBox(
+ width: 12,
+ ),
+ Expanded(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ manager.walletName,
+ style: STextStyles.titleBold12(context),
+ overflow: TextOverflow.ellipsis,
+ ),
+ const SizedBox(
+ height: 2,
+ ),
+ WalletInfoRowBalanceFuture(
+ walletId: walletIds[index],
+ ),
+ ],
+ ),
+ )
+ ],
+ ),
+ ),
+ ),
+ );
+ },
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/pages/exchange_view/confirm_change_now_send.dart b/lib/pages/exchange_view/confirm_change_now_send.dart
index 6fd6c8a08..3ddf8932a 100644
--- a/lib/pages/exchange_view/confirm_change_now_send.dart
+++ b/lib/pages/exchange_view/confirm_change_now_send.dart
@@ -132,6 +132,7 @@ class _ConfirmChangeNowSendViewState
final managerProvider = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManagerProvider(walletId)));
return Scaffold(
+ backgroundColor: Theme.of(context).extension()!.background,
appBar: AppBar(
backgroundColor: Theme.of(context).extension()!.background,
leading: AppBarBackButton(
@@ -327,7 +328,12 @@ class _ConfirmChangeNowSendViewState
children: [
Text(
"Total amount",
- style: STextStyles.titleBold12(context),
+ style:
+ STextStyles.titleBold12(context).copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textConfirmTotalAmount,
+ ),
),
Text(
"${Format.satoshiAmountToPrettyString(
@@ -341,7 +347,12 @@ class _ConfirmChangeNowSendViewState
managerProvider
.select((value) => value.coin),
).ticker}",
- style: STextStyles.itemSubtitle12(context),
+ style: STextStyles.itemSubtitle12(context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textConfirmTotalAmount,
+ ),
textAlign: TextAlign.right,
),
],
diff --git a/lib/pages/exchange_view/exchange_step_views/step_2_view.dart b/lib/pages/exchange_view/exchange_step_views/step_2_view.dart
index 3ac7fd8ec..5681018de 100644
--- a/lib/pages/exchange_view/exchange_step_views/step_2_view.dart
+++ b/lib/pages/exchange_view/exchange_step_views/step_2_view.dart
@@ -3,6 +3,8 @@ import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
import 'package:stackwallet/pages/address_book_views/address_book_view.dart';
+import 'package:stackwallet/pages/address_book_views/subviews/contact_popup.dart';
+import 'package:stackwallet/pages/exchange_view/choose_from_stack_view.dart';
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_3_view.dart';
import 'package:stackwallet/pages/exchange_view/sub_widgets/step_row.dart';
import 'package:stackwallet/providers/exchange/exchange_flow_is_active_state_provider.dart';
@@ -17,6 +19,7 @@ import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
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/icon_widgets/addressbook_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
@@ -54,6 +57,15 @@ class _Step2ViewState extends ConsumerState {
late final FocusNode _toFocusNode;
late final FocusNode _refundFocusNode;
+ bool isStackCoin(String ticker) {
+ try {
+ coinFromTickerCaseInsensitive(ticker);
+ return true;
+ } on ArgumentError catch (_) {
+ return false;
+ }
+ }
+
@override
void initState() {
model = widget.model;
@@ -74,7 +86,22 @@ class _Step2ViewState extends ConsumerState {
.read(walletsChangeNotifierProvider)
.getManager(tuple.item1)
.currentReceivingAddress
- .then((value) => _toController.text = value);
+ .then((value) {
+ _toController.text = value;
+ model.recipientAddress = _toController.text;
+ });
+ } else {
+ if (model.sendTicker.toUpperCase() ==
+ tuple.item2.ticker.toUpperCase()) {
+ ref
+ .read(walletsChangeNotifierProvider)
+ .getManager(tuple.item1)
+ .currentReceivingAddress
+ .then((value) {
+ _refundController.text = value;
+ model.refundAddress = _refundController.text;
+ });
+ }
}
}
@@ -158,15 +185,36 @@ class _Step2ViewState extends ConsumerState {
"Recipient Wallet",
style: STextStyles.smallMed12(context),
),
- // GestureDetector(
- // onTap: () {
- // // TODO: choose from stack?
- // },
- // child: Text(
- // "Choose from Stack",
- // style: STextStyles.link2(context),
- // ),
- // ),
+ if (isStackCoin(model.receiveTicker))
+ BlueTextButton(
+ text: "Choose from stack",
+ onTap: () {
+ try {
+ final coin = coinFromTickerCaseInsensitive(
+ model.receiveTicker,
+ );
+ Navigator.of(context)
+ .pushNamed(
+ ChooseFromStackView.routeName,
+ arguments: coin,
+ )
+ .then((value) async {
+ if (value is String) {
+ final manager = ref
+ .read(walletsChangeNotifierProvider)
+ .getManager(value);
+
+ _toController.text = manager.walletName;
+ model.recipientAddress = await manager
+ .currentReceivingAddress;
+ }
+ });
+ } catch (e, s) {
+ Logging.instance
+ .log("$e\n$s", level: LogLevel.Info);
+ }
+ },
+ ),
],
),
const SizedBox(
@@ -195,6 +243,9 @@ class _Step2ViewState extends ConsumerState {
),
focusNode: _toFocusNode,
style: STextStyles.field(context),
+ onChanged: (value) {
+ setState(() {});
+ },
decoration: standardInputDecoration(
"Enter the ${model.receiveTicker.toUpperCase()} payout address",
_toFocusNode,
@@ -221,6 +272,8 @@ class _Step2ViewState extends ConsumerState {
"sendViewClearAddressFieldButtonKey"),
onTap: () {
_toController.text = "";
+ model.recipientAddress =
+ _toController.text;
setState(() {});
},
@@ -239,6 +292,8 @@ class _Step2ViewState extends ConsumerState {
data.text!.trim();
_toController.text = content;
+ model.recipientAddress =
+ _toController.text;
setState(() {});
}
@@ -259,13 +314,31 @@ class _Step2ViewState extends ConsumerState {
.state = true;
Navigator.of(context)
.pushNamed(
- AddressBookView.routeName,
- )
- .then((_) => ref
+ AddressBookView.routeName,
+ )
+ .then((_) {
+ ref
+ .read(
+ exchangeFlowIsActiveStateProvider
+ .state)
+ .state = false;
+
+ final address = ref
+ .read(
+ exchangeFromAddressBookAddressStateProvider
+ .state)
+ .state;
+ if (address.isNotEmpty) {
+ _toController.text = address;
+ model.recipientAddress =
+ _toController.text;
+ ref
.read(
- exchangeFlowIsActiveStateProvider
+ exchangeFromAddressBookAddressStateProvider
.state)
- .state = false);
+ .state = "";
+ }
+ });
},
child: const AddressBookIcon(),
),
@@ -299,11 +372,15 @@ class _Step2ViewState extends ConsumerState {
// auto fill address
_toController.text =
results["address"] ?? "";
+ model.recipientAddress =
+ _toController.text;
setState(() {});
} else {
_toController.text =
qrResult.rawContent;
+ model.recipientAddress =
+ _toController.text;
setState(() {});
}
@@ -348,15 +425,37 @@ class _Step2ViewState extends ConsumerState {
"Refund Wallet (required)",
style: STextStyles.smallMed12(context),
),
- // GestureDetector(
- // onTap: () {
- // // TODO: choose from stack?
- // },
- // child: Text(
- // "Choose from Stack",
- // style: STextStyles.link2(context),
- // ),
- // ),
+ if (isStackCoin(model.sendTicker))
+ BlueTextButton(
+ text: "Choose from stack",
+ onTap: () {
+ try {
+ final coin = coinFromTickerCaseInsensitive(
+ model.sendTicker,
+ );
+ Navigator.of(context)
+ .pushNamed(
+ ChooseFromStackView.routeName,
+ arguments: coin,
+ )
+ .then((value) async {
+ if (value is String) {
+ final manager = ref
+ .read(walletsChangeNotifierProvider)
+ .getManager(value);
+
+ _refundController.text =
+ manager.walletName;
+ model.refundAddress = await manager
+ .currentReceivingAddress;
+ }
+ });
+ } catch (e, s) {
+ Logging.instance
+ .log("$e\n$s", level: LogLevel.Info);
+ }
+ },
+ ),
],
),
const SizedBox(
@@ -384,6 +483,9 @@ class _Step2ViewState extends ConsumerState {
),
focusNode: _refundFocusNode,
style: STextStyles.field(context),
+ onChanged: (value) {
+ setState(() {});
+ },
decoration: standardInputDecoration(
"Enter ${model.sendTicker.toUpperCase()} refund address",
_refundFocusNode,
@@ -410,6 +512,8 @@ class _Step2ViewState extends ConsumerState {
"sendViewClearAddressFieldButtonKey"),
onTap: () {
_refundController.text = "";
+ model.refundAddress =
+ _refundController.text;
setState(() {});
},
@@ -429,6 +533,8 @@ class _Step2ViewState extends ConsumerState {
_refundController.text =
content;
+ model.refundAddress =
+ _refundController.text;
setState(() {});
}
@@ -450,13 +556,26 @@ class _Step2ViewState extends ConsumerState {
.state = true;
Navigator.of(context)
.pushNamed(
- AddressBookView.routeName,
- )
- .then((_) => ref
- .read(
- exchangeFlowIsActiveStateProvider
- .state)
- .state = false);
+ AddressBookView.routeName,
+ )
+ .then((_) {
+ ref
+ .read(
+ exchangeFlowIsActiveStateProvider
+ .state)
+ .state = false;
+ final address = ref
+ .read(
+ exchangeFromAddressBookAddressStateProvider
+ .state)
+ .state;
+ if (address.isNotEmpty) {
+ _refundController.text =
+ address;
+ model.refundAddress =
+ _refundController.text;
+ }
+ });
},
child: const AddressBookIcon(),
),
@@ -490,11 +609,15 @@ class _Step2ViewState extends ConsumerState {
// auto fill address
_refundController.text =
results["address"] ?? "";
+ model.refundAddress =
+ _refundController.text;
setState(() {});
} else {
_refundController.text =
qrResult.rawContent;
+ model.refundAddress =
+ _refundController.text;
setState(() {});
}
@@ -556,9 +679,6 @@ class _Step2ViewState extends ConsumerState {
Expanded(
child: TextButton(
onPressed: () {
- model.recipientAddress = _toController.text;
- model.refundAddress = _refundController.text;
-
Navigator.of(context).pushNamed(
Step3View.routeName,
arguments: model);
diff --git a/lib/pages/exchange_view/exchange_step_views/step_3_view.dart b/lib/pages/exchange_view/exchange_step_views/step_3_view.dart
index 604e3707f..a7b34571b 100644
--- a/lib/pages/exchange_view/exchange_step_views/step_3_view.dart
+++ b/lib/pages/exchange_view/exchange_step_views/step_3_view.dart
@@ -16,6 +16,7 @@ import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
+import 'package:stackwallet/widgets/custom_loading_overlay.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
@@ -222,6 +223,26 @@ class _Step3ViewState extends ConsumerState {
Expanded(
child: TextButton(
onPressed: () async {
+ unawaited(
+ showDialog(
+ context: context,
+ barrierDismissible: false,
+ builder: (_) => WillPopScope(
+ onWillPop: () async => false,
+ child: Container(
+ color: Theme.of(context)
+ .extension()!
+ .overlay
+ .withOpacity(0.6),
+ child: const CustomLoadingOverlay(
+ message: "Creating a trade",
+ eventBus: null,
+ ),
+ ),
+ ),
+ ),
+ );
+
ChangeNowResponse
response;
if (model.rateType ==
@@ -251,6 +272,10 @@ class _Step3ViewState extends ConsumerState {
}
if (response.value == null) {
+ if (mounted) {
+ Navigator.of(context).pop();
+ }
+
unawaited(showDialog(
context: context,
barrierDismissible: true,
@@ -273,8 +298,6 @@ class _Step3ViewState extends ConsumerState {
.getTransactionStatus(
id: response.value!.id);
- debugPrint("WTF: $statusResponse");
-
String status = "Waiting";
if (statusResponse.value != null) {
status = statusResponse.value!.status.name;
@@ -290,6 +313,10 @@ class _Step3ViewState extends ConsumerState {
status += " for deposit";
}
+ if (mounted) {
+ Navigator.of(context).pop();
+ }
+
unawaited(NotificationApi.showNotification(
changeNowId: model.trade!.id,
title: status,
diff --git a/lib/pages/exchange_view/exchange_view.dart b/lib/pages/exchange_view/exchange_view.dart
index 1487f5f6a..ad618dd0c 100644
--- a/lib/pages/exchange_view/exchange_view.dart
+++ b/lib/pages/exchange_view/exchange_view.dart
@@ -232,7 +232,7 @@ class _ExchangeViewState extends ConsumerState {
? ref.read(estimatedRateExchangeFormProvider).fromAmountString
: ref.read(fixedRateExchangeFormProvider).fromAmountString;
_receiveController.text = isEstimated
- ? ref.read(estimatedRateExchangeFormProvider).toAmountString
+ ? "-" //ref.read(estimatedRateExchangeFormProvider).toAmountString
: ref.read(fixedRateExchangeFormProvider).toAmountString;
_sendFocusNode.addListener(() async {
@@ -260,7 +260,11 @@ class _ExchangeViewState extends ConsumerState {
.read(fixedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount(Decimal.zero, true);
}
- _receiveController.text = "";
+ _receiveController.text =
+ ref.read(prefsChangeNotifierProvider).exchangeRateType ==
+ ExchangeRateType.estimated
+ ? "-"
+ : "";
}
}
});
@@ -325,7 +329,7 @@ class _ExchangeViewState extends ConsumerState {
: fixedRateExchangeFormProvider.select(
(value) => value.toAmountString), (previous, String next) {
if (!_receiveFocusNode.hasFocus) {
- _receiveController.text = next;
+ _receiveController.text = isEstimated && next.isEmpty ? "-" : next;
debugPrint("RECEIVE AMOUNT LISTENER ACTIVATED");
if (_swapLock) {
_sendController.text = isEstimated
@@ -345,7 +349,12 @@ class _ExchangeViewState extends ConsumerState {
debugPrint("SEND AMOUNT LISTENER ACTIVATED");
if (_swapLock) {
_receiveController.text = isEstimated
- ? ref.read(estimatedRateExchangeFormProvider).toAmountString
+ ? ref
+ .read(estimatedRateExchangeFormProvider)
+ .toAmountString
+ .isEmpty
+ ? "-"
+ : ref.read(estimatedRateExchangeFormProvider).toAmountString
: ref.read(fixedRateExchangeFormProvider).toAmountString;
}
}
@@ -424,7 +433,7 @@ class _ExchangeViewState extends ConsumerState {
.setFromAmountAndCalculateToAmount(
Decimal.zero, false);
}
- _receiveController.text = "";
+ _receiveController.text = isEstimated ? "-" : "";
}
},
keyboardType: const TextInputType.numberWithOptions(
@@ -737,7 +746,7 @@ class _ExchangeViewState extends ConsumerState {
.exchangeRateType ==
ExchangeRateType.estimated,
onTap: () {
- if (_receiveController.text == "-") {
+ if (!isEstimated && _receiveController.text == "-") {
_receiveController.text = "";
}
},
diff --git a/lib/pages/exchange_view/send_from_view.dart b/lib/pages/exchange_view/send_from_view.dart
index 586ffc0da..74a1f62d4 100644
--- a/lib/pages/exchange_view/send_from_view.dart
+++ b/lib/pages/exchange_view/send_from_view.dart
@@ -48,6 +48,26 @@ class _SendFromViewState extends ConsumerState {
late final String address;
late final ExchangeTransaction trade;
+ String formatAmount(Decimal amount, Coin coin) {
+ switch (coin) {
+ case Coin.bitcoin:
+ case Coin.bitcoincash:
+ case Coin.dogecoin:
+ case Coin.epicCash:
+ case Coin.firo:
+ case Coin.namecoin:
+ case Coin.bitcoinTestNet:
+ case Coin.bitcoincashTestnet:
+ case Coin.dogecoinTestNet:
+ case Coin.firoTestNet:
+ return amount.toStringAsFixed(Constants.decimalPlaces);
+ case Coin.monero:
+ return amount.toStringAsFixed(Constants.decimalPlacesMonero);
+ case Coin.wownero:
+ return amount.toStringAsFixed(Constants.decimalPlacesWownero);
+ }
+ }
+
@override
void initState() {
coin = widget.coin;
@@ -59,6 +79,11 @@ class _SendFromViewState extends ConsumerState {
@override
Widget build(BuildContext context) {
+ debugPrint("BUILD: $runtimeType");
+
+ final walletIds = ref.watch(walletsChangeNotifierProvider
+ .select((value) => value.getWalletIdsFor(coin: coin)));
+
return Scaffold(
backgroundColor: Theme.of(context).extension()!.background,
appBar: AppBar(
@@ -68,44 +93,41 @@ class _SendFromViewState extends ConsumerState {
},
),
title: Text(
- "Send ",
+ "Send from",
style: STextStyles.navBarTitle(context),
),
),
body: Padding(
padding: const EdgeInsets.all(16),
- child: Wrap(
- // crossAxisAlignment: CrossAxisAlignment.stretch,
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.start,
children: [
- Text(
- "Choose your ${coin.ticker} wallet",
- style: STextStyles.pageTitleH1(context),
- ),
- const SizedBox(
- height: 8,
- ),
- Text(
- "You need to send ${amount.toStringAsFixed(coin == Coin.monero ? Constants.satsPerCoinMonero : coin == Coin.wownero ? Constants.satsPerCoinWownero : Constants.satsPerCoin)} ${coin.ticker}",
- style: STextStyles.itemSubtitle(context),
+ Row(
+ children: [
+ Text(
+ "You need to send ${formatAmount(amount, coin)} ${coin.ticker}",
+ style: STextStyles.itemSubtitle(context),
+ ),
+ ],
),
const SizedBox(
height: 16,
),
- ListView(
- shrinkWrap: true,
- children: [
- ...ref
- .watch(walletsChangeNotifierProvider
- .select((value) => value.managers))
- .where((element) => element.coin == coin)
- .map((e) => SendFromCard(
- walletId: e.walletId,
- amount: amount,
- address: address,
- trade: trade,
- ))
- .toList(growable: false)
- ],
+ Expanded(
+ child: ListView.builder(
+ itemCount: walletIds.length,
+ itemBuilder: (context, index) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(vertical: 4),
+ child: SendFromCard(
+ walletId: walletIds[index],
+ amount: amount,
+ address: address,
+ trade: trade,
+ ),
+ );
+ },
+ ),
),
],
),
@@ -163,7 +185,7 @@ class _SendFromCardState extends ConsumerState {
child: MaterialButton(
splashColor: Theme.of(context).extension()!.highlight,
key: Key("walletsSheetItemButtonKey_$walletId"),
- padding: const EdgeInsets.all(5),
+ padding: const EdgeInsets.all(8),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
@@ -276,59 +298,61 @@ class _SendFromCardState extends ConsumerState {
),
),
child: Padding(
- padding: const EdgeInsets.all(4),
+ padding: const EdgeInsets.all(6),
child: SvgPicture.asset(
Assets.svg.iconFor(coin: coin),
- width: 20,
- height: 20,
+ width: 24,
+ height: 24,
),
),
),
const SizedBox(
width: 12,
),
- Column(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- manager.walletName,
- style: STextStyles.titleBold12(context),
- ),
- const SizedBox(
- height: 2,
- ),
- FutureBuilder(
- future: manager.totalBalance,
- builder: (builderContext, AsyncSnapshot snapshot) {
- if (snapshot.connectionState == ConnectionState.done &&
- snapshot.hasData) {
- return Text(
- "${Format.localizedStringAsFixed(
- value: snapshot.data!,
- locale: locale,
- decimalPlaces: coin == Coin.monero
- ? Constants.satsPerCoinMonero
- : coin == Coin.wownero
- ? Constants.satsPerCoinWownero
- : Constants.satsPerCoin,
- )} ${coin.ticker}",
- style: STextStyles.itemSubtitle(context),
- );
- } else {
- return AnimatedText(
- stringsToLoopThrough: const [
- "Loading balance",
- "Loading balance.",
- "Loading balance..",
- "Loading balance..."
- ],
- style: STextStyles.itemSubtitle(context),
- );
- }
- },
- ),
- ],
+ Expanded(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ manager.walletName,
+ style: STextStyles.titleBold12(context),
+ ),
+ const SizedBox(
+ height: 2,
+ ),
+ FutureBuilder(
+ future: manager.totalBalance,
+ builder: (builderContext, AsyncSnapshot snapshot) {
+ if (snapshot.connectionState == ConnectionState.done &&
+ snapshot.hasData) {
+ return Text(
+ "${Format.localizedStringAsFixed(
+ value: snapshot.data!,
+ locale: locale,
+ decimalPlaces: coin == Coin.monero
+ ? Constants.decimalPlacesMonero
+ : coin == Coin.wownero
+ ? Constants.decimalPlacesWownero
+ : Constants.decimalPlaces,
+ )} ${coin.ticker}",
+ style: STextStyles.itemSubtitle(context),
+ );
+ } else {
+ return AnimatedText(
+ stringsToLoopThrough: const [
+ "Loading balance",
+ "Loading balance.",
+ "Loading balance..",
+ "Loading balance..."
+ ],
+ style: STextStyles.itemSubtitle(context),
+ );
+ }
+ },
+ ),
+ ],
+ ),
),
],
),
diff --git a/lib/pages/exchange_view/trade_details_view.dart b/lib/pages/exchange_view/trade_details_view.dart
index 6c299ed24..ae44faeef 100644
--- a/lib/pages/exchange_view/trade_details_view.dart
+++ b/lib/pages/exchange_view/trade_details_view.dart
@@ -10,6 +10,7 @@ import 'package:stackwallet/models/exchange/change_now/exchange_transaction_stat
import 'package:stackwallet/models/paymint/transactions_model.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/exchange_view/edit_trade_note_view.dart';
+import 'package:stackwallet/pages/exchange_view/send_from_view.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/edit_note_view.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/providers/exchange/change_now_provider.dart';
@@ -24,6 +25,7 @@ import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
+import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
@@ -60,6 +62,15 @@ class _TradeDetailsViewState extends ConsumerState {
String _note = "";
+ bool isStackCoin(String ticker) {
+ try {
+ coinFromTickerCaseInsensitive(ticker);
+ return true;
+ } on ArgumentError catch (_) {
+ return false;
+ }
+ }
+
@override
initState() {
tradeId = widget.tradeId;
@@ -345,9 +356,48 @@ class _TradeDetailsViewState extends ConsumerState {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Text(
- "Send ${trade.fromCurrency.toUpperCase()} to this address",
- style: STextStyles.itemSubtitle(context),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ "Send ${trade.fromCurrency.toUpperCase()} to this address",
+ style: STextStyles.itemSubtitle(context),
+ ),
+ GestureDetector(
+ onTap: () async {
+ final address = trade.payinAddress;
+ await Clipboard.setData(
+ ClipboardData(
+ text: address,
+ ),
+ );
+ unawaited(showFloatingFlushBar(
+ type: FlushBarType.info,
+ message: "Copied to clipboard",
+ context: context,
+ ));
+ },
+ child: Row(
+ children: [
+ SvgPicture.asset(
+ Assets.svg.copy,
+ width: 12,
+ height: 12,
+ color: Theme.of(context)
+ .extension()!
+ .infoItemIcons,
+ ),
+ const SizedBox(
+ width: 4,
+ ),
+ Text(
+ "Copy",
+ style: STextStyles.link2(context),
+ ),
+ ],
+ ),
+ ),
+ ],
),
const SizedBox(
height: 4,
@@ -717,6 +767,32 @@ class _TradeDetailsViewState extends ConsumerState {
const SizedBox(
height: 12,
),
+ if (isStackCoin(trade.fromCurrency) &&
+ trade.statusObject != null &&
+ (trade.statusObject!.status ==
+ ChangeNowTransactionStatus.New ||
+ trade.statusObject!.status ==
+ ChangeNowTransactionStatus.Waiting))
+ SecondaryButton(
+ label: "Send from Stack",
+ onPressed: () {
+ final amount = sendAmount;
+ final address = trade.payinAddress;
+
+ final coin =
+ coinFromTickerCaseInsensitive(trade.fromCurrency);
+
+ Navigator.of(context).pushNamed(
+ SendFromView.routeName,
+ arguments: Tuple4(
+ coin,
+ amount,
+ address,
+ trade,
+ ),
+ );
+ },
+ ),
],
),
),
diff --git a/lib/pages/exchange_view/wallet_initiated_exchange_view.dart b/lib/pages/exchange_view/wallet_initiated_exchange_view.dart
index 179555ba6..ea45c24d3 100644
--- a/lib/pages/exchange_view/wallet_initiated_exchange_view.dart
+++ b/lib/pages/exchange_view/wallet_initiated_exchange_view.dart
@@ -83,8 +83,8 @@ class _WalletInitiatedExchangeViewState
child: Container(
color: Theme.of(context)
.extension()!
- .accentColorDark
- .withOpacity(0.8),
+ .overlay
+ .withOpacity(0.6),
child: const CustomLoadingOverlay(
message: "Updating exchange rate",
eventBus: null,
@@ -271,7 +271,11 @@ class _WalletInitiatedExchangeViewState
.read(fixedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount(Decimal.zero, true);
}
- _receiveController.text = "";
+ _receiveController.text =
+ ref.read(prefsChangeNotifierProvider).exchangeRateType ==
+ ExchangeRateType.estimated
+ ? "-"
+ : "";
}
}
});
@@ -329,7 +333,7 @@ class _WalletInitiatedExchangeViewState
: fixedRateExchangeFormProvider.select(
(value) => value.toAmountString), (previous, String next) {
if (!_receiveFocusNode.hasFocus) {
- _receiveController.text = next;
+ _receiveController.text = isEstimated && next.isEmpty ? "-" : next;
debugPrint("RECEIVE AMOUNT LISTENER ACTIVATED");
if (_swapLock) {
_sendController.text = isEstimated
@@ -349,7 +353,12 @@ class _WalletInitiatedExchangeViewState
debugPrint("SEND AMOUNT LISTENER ACTIVATED");
if (_swapLock) {
_receiveController.text = isEstimated
- ? ref.read(estimatedRateExchangeFormProvider).toAmountString
+ ? ref
+ .read(estimatedRateExchangeFormProvider)
+ .toAmountString
+ .isEmpty
+ ? "-"
+ : ref.read(estimatedRateExchangeFormProvider).toAmountString
: ref.read(fixedRateExchangeFormProvider).toAmountString;
}
}
@@ -469,7 +478,7 @@ class _WalletInitiatedExchangeViewState
.setFromAmountAndCalculateToAmount(
Decimal.zero, false);
}
- _receiveController.text = "";
+ _receiveController.text = isEstimated ? "-" : "";
}
},
keyboardType: const TextInputType.numberWithOptions(
@@ -808,7 +817,8 @@ class _WalletInitiatedExchangeViewState
.exchangeRateType ==
ExchangeRateType.estimated,
onTap: () {
- if (_receiveController.text == "-") {
+ if (!isEstimated &&
+ _receiveController.text == "-") {
_receiveController.text = "";
}
},
@@ -1280,23 +1290,23 @@ class _WalletInitiatedExchangeViewState
.exchangeRateType ==
ExchangeRateType.estimated;
- final ft = isEstimated
- ? ref
- .read(
- estimatedRateExchangeFormProvider)
- .from
- ?.ticker ??
- ""
- : ref
- .read(
- fixedRateExchangeFormProvider)
- .market
- ?.from ??
- "";
-
- final manager = ref
- .read(walletsChangeNotifierProvider)
- .getManager(walletId);
+ // final ft = isEstimated
+ // ? ref
+ // .read(
+ // estimatedRateExchangeFormProvider)
+ // .from
+ // ?.ticker ??
+ // ""
+ // : ref
+ // .read(
+ // fixedRateExchangeFormProvider)
+ // .market
+ // ?.from ??
+ // "";
+ //
+ // final manager = ref
+ // .read(walletsChangeNotifierProvider)
+ // .getManager(walletId);
final sendAmount = Decimal.parse(ref
.read(estimatedRateExchangeFormProvider)
.fromAmountString);
diff --git a/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart b/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart
index 6b073ea69..29baad1c6 100644
--- a/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart
+++ b/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart
@@ -303,13 +303,17 @@ class _DebugViewState extends ConsumerState {
Logging.instance
.log("$e\n$s", level: LogLevel.Error);
}
-
- final String? path =
- await FilePicker.platform.getDirectoryPath(
- dialogTitle: "Choose Backup location",
- initialDirectory: dir.path,
- lockParentWindow: true,
- );
+ String? path;
+ if (Platform.isAndroid) {
+ path = dir.path;
+ } else {
+ path = await FilePicker.platform
+ .getDirectoryPath(
+ dialogTitle: "Choose Backup location",
+ initialDirectory: dir.path,
+ lockParentWindow: true,
+ );
+ }
if (path != null) {
final eventBus = EventBus();
@@ -328,7 +332,7 @@ class _DebugViewState extends ConsumerState {
),
));
- await ref
+ final filename = await ref
.read(debugServiceProvider)
.exportToFile(path, eventBus);
@@ -336,10 +340,26 @@ class _DebugViewState extends ConsumerState {
if (mounted) {
Navigator.pop(context);
- unawaited(showFloatingFlushBar(
- type: FlushBarType.info,
- context: context,
- message: 'Logs file saved'));
+
+ if (Platform.isAndroid) {
+ unawaited(
+ showDialog(
+ context: context,
+ builder: (context) => StackOkDialog(
+ title: "Logs saved to",
+ message: "${path!}/$filename",
+ ),
+ ),
+ );
+ } else {
+ unawaited(
+ showFloatingFlushBar(
+ type: FlushBarType.info,
+ context: context,
+ message: 'Logs file saved',
+ ),
+ );
+ }
}
}
},
diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/create_auto_backup_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/create_auto_backup_view.dart
index 22ace0a8e..b44a473b4 100644
--- a/lib/pages/settings_views/global_settings_view/stack_backup_views/create_auto_backup_view.dart
+++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/create_auto_backup_view.dart
@@ -82,6 +82,17 @@ class _EnableAutoBackupViewState extends ConsumerState {
passwordFocusNode = FocusNode();
passwordRepeatFocusNode = FocusNode();
+ if (Platform.isAndroid) {
+ WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
+ final dir = await stackFileSystem.prepareStorage();
+ if (mounted) {
+ setState(() {
+ fileLocationController.text = dir.path;
+ });
+ }
+ });
+ }
+
super.initState();
}
@@ -133,64 +144,70 @@ class _EnableAutoBackupViewState extends ConsumerState {
const SizedBox(
height: 10,
),
- TextField(
- onTap: () async {
- try {
- await stackFileSystem.prepareStorage();
+ if (!Platform.isAndroid)
+ TextField(
+ onTap: Platform.isAndroid
+ ? null
+ : () async {
+ try {
+ await stackFileSystem.prepareStorage();
- if (mounted) {
- await stackFileSystem.pickDir(context);
- }
+ if (mounted) {
+ await stackFileSystem.pickDir(context);
+ }
- if (mounted) {
- setState(() {
- fileLocationController.text =
- stackFileSystem.dirPath ?? "";
- });
- }
- } catch (e, s) {
- Logging.instance.log("$e\n$s", level: LogLevel.Error);
- }
- },
- controller: fileLocationController,
- style: STextStyles.field(context),
- decoration: InputDecoration(
- hintText: "Save to...",
- suffixIcon: UnconstrainedBox(
- child: Row(
- children: [
- const SizedBox(
- width: 16,
- ),
- SvgPicture.asset(
- Assets.svg.folder,
- color: Theme.of(context)
- .extension()!
- .textDark3,
- width: 16,
- height: 16,
- ),
- const SizedBox(
- width: 12,
- ),
- ],
+ if (mounted) {
+ setState(() {
+ fileLocationController.text =
+ stackFileSystem.dirPath ?? "";
+ });
+ }
+ } catch (e, s) {
+ Logging.instance
+ .log("$e\n$s", level: LogLevel.Error);
+ }
+ },
+ controller: fileLocationController,
+ style: STextStyles.field(context),
+ decoration: InputDecoration(
+ hintText: "Save to...",
+ hintStyle: STextStyles.fieldLabel(context),
+ suffixIcon: UnconstrainedBox(
+ child: Row(
+ children: [
+ const SizedBox(
+ width: 16,
+ ),
+ SvgPicture.asset(
+ Assets.svg.folder,
+ color: Theme.of(context)
+ .extension()!
+ .textDark3,
+ width: 16,
+ height: 16,
+ ),
+ const SizedBox(
+ width: 12,
+ ),
+ ],
+ ),
),
),
+ key: const Key(
+ "createBackupSaveToFileLocationTextFieldKey"),
+ readOnly: true,
+ toolbarOptions: const ToolbarOptions(
+ copy: true,
+ cut: false,
+ paste: false,
+ selectAll: false,
+ ),
+ onChanged: (newValue) {},
),
- key: const Key(
- "createBackupSaveToFileLocationTextFieldKey"),
- readOnly: true,
- toolbarOptions: const ToolbarOptions(
- copy: true,
- cut: false,
- paste: false,
- selectAll: false,
+ if (!Platform.isAndroid)
+ const SizedBox(
+ height: 10,
),
- onChanged: (newValue) {},
- ),
- const SizedBox(
- height: 10,
- ),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
@@ -593,8 +610,15 @@ class _EnableAutoBackupViewState extends ConsumerState {
await showDialog(
context: context,
barrierDismissible: false,
- builder: (_) => const StackOkDialog(
- title: "Stack Auto Backup enabled!"),
+ builder: (_) => Platform.isAndroid
+ ? StackOkDialog(
+ title:
+ "Stack Auto Backup enabled and saved to:",
+ message: fileToSave,
+ )
+ : const StackOkDialog(
+ title:
+ "Stack Auto Backup enabled!"),
);
if (mounted) {
passwordController.text = "";
diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/create_backup_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/create_backup_view.dart
index 964111cd3..8dfc7588c 100644
--- a/lib/pages/settings_views/global_settings_view/stack_backup_views/create_backup_view.dart
+++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/create_backup_view.dart
@@ -64,6 +64,17 @@ class _RestoreFromFileViewState extends State {
passwordFocusNode = FocusNode();
passwordRepeatFocusNode = FocusNode();
+ if (Platform.isAndroid) {
+ WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
+ final dir = await stackFileSystem.prepareStorage();
+ if (mounted) {
+ setState(() {
+ fileLocationController.text = dir.path;
+ });
+ }
+ });
+ }
+
super.initState();
}
@@ -113,88 +124,78 @@ class _RestoreFromFileViewState extends State {
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
- Consumer(builder: (context, ref, __) {
- return Container(
- color: Colors.transparent,
- child: TextField(
- onTap: () async {
- try {
- await stackFileSystem.prepareStorage();
- // ref
- // .read(
- // shouldShowLockscreenOnResumeStateProvider
- // .state)
- // .state = false;
- if (mounted) {
- await stackFileSystem.pickDir(context);
- }
+ if (!Platform.isAndroid)
+ Consumer(builder: (context, ref, __) {
+ return Container(
+ color: Colors.transparent,
+ child: TextField(
+ onTap: Platform.isAndroid
+ ? null
+ : () async {
+ try {
+ await stackFileSystem.prepareStorage();
- // Future.delayed(
- // const Duration(seconds: 2),
- // () => ref
- // .read(
- // shouldShowLockscreenOnResumeStateProvider
- // .state)
- // .state = true,
- // );
-
- setState(() {
- fileLocationController.text =
- stackFileSystem.dirPath ?? "";
- });
- } catch (e, s) {
- // ref
- // .read(
- // shouldShowLockscreenOnResumeStateProvider
- // .state)
- // .state = true;
- Logging.instance
- .log("$e\n$s", level: LogLevel.Error);
- }
- },
- controller: fileLocationController,
- style: STextStyles.field(context),
- decoration: InputDecoration(
- hintText: "Save to...",
- suffixIcon: UnconstrainedBox(
- child: Row(
- children: [
- const SizedBox(
- width: 16,
- ),
- SvgPicture.asset(
- Assets.svg.folder,
- color: Theme.of(context)
- .extension()!
- .textDark3,
- width: 16,
- height: 16,
- ),
- const SizedBox(
- width: 12,
- ),
- ],
+ if (mounted) {
+ await stackFileSystem
+ .pickDir(context);
+ }
+
+ if (mounted) {
+ setState(() {
+ fileLocationController.text =
+ stackFileSystem.dirPath ?? "";
+ });
+ }
+ } catch (e, s) {
+ Logging.instance.log("$e\n$s",
+ level: LogLevel.Error);
+ }
+ },
+ controller: fileLocationController,
+ style: STextStyles.field(context),
+ decoration: InputDecoration(
+ hintText: "Save to...",
+ hintStyle: STextStyles.fieldLabel(context),
+ suffixIcon: UnconstrainedBox(
+ child: Row(
+ children: [
+ const SizedBox(
+ width: 16,
+ ),
+ SvgPicture.asset(
+ Assets.svg.folder,
+ color: Theme.of(context)
+ .extension()!
+ .textDark3,
+ width: 16,
+ height: 16,
+ ),
+ const SizedBox(
+ width: 12,
+ ),
+ ],
+ ),
),
),
+ key: const Key(
+ "createBackupSaveToFileLocationTextFieldKey"),
+ readOnly: true,
+ toolbarOptions: const ToolbarOptions(
+ copy: true,
+ cut: false,
+ paste: false,
+ selectAll: false,
+ ),
+ onChanged: (newValue) {
+ // ref.read(addressEntryDataProvider(widget.id)).address = newValue;
+ },
),
- key: const Key(
- "createBackupSaveToFileLocationTextFieldKey"),
- readOnly: true,
- toolbarOptions: const ToolbarOptions(
- copy: true,
- cut: false,
- paste: false,
- selectAll: false,
- ),
- onChanged: (newValue) {
- // ref.read(addressEntryDataProvider(widget.id)).address = newValue;
- },
- ),
- );
- }),
- const SizedBox(
- height: 8,
- ),
+ );
+ }),
+ if (!Platform.isAndroid)
+ const SizedBox(
+ height: 8,
+ ),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
@@ -315,8 +316,12 @@ class _RestoreFromFileViewState extends State {
.extension()!
.accentColorRed
: passwordStrength < 1
- ? Theme.of(context).extension()!.accentColorYellow
- : Theme.of(context).extension()!.accentColorGreen,
+ ? Theme.of(context)
+ .extension()!
+ .accentColorYellow
+ : Theme.of(context)
+ .extension()!
+ .accentColorGreen,
backgroundColor: Theme.of(context)
.extension()!
.buttonBackSecondary,
@@ -389,8 +394,12 @@ class _RestoreFromFileViewState extends State {
const Spacer(),
TextButton(
style: shouldEnableCreate
- ? Theme.of(context) .extension()!.getPrimaryEnabledButtonColor(context)
- : Theme.of(context) .extension()!.getPrimaryDisabledButtonColor(context),
+ ? Theme.of(context)
+ .extension()!
+ .getPrimaryEnabledButtonColor(context)
+ : Theme.of(context)
+ .extension()!
+ .getPrimaryDisabledButtonColor(context),
onPressed: !shouldEnableCreate
? null
: () async {
@@ -468,8 +477,14 @@ class _RestoreFromFileViewState extends State {
await showDialog(
context: context,
barrierDismissible: false,
- builder: (_) => const StackOkDialog(
- title: "Backup creation succeeded"),
+ builder: (_) => Platform.isAndroid
+ ? StackOkDialog(
+ title: "Backup saved to:",
+ message: fileToSave,
+ )
+ : const StackOkDialog(
+ title:
+ "Backup creation succeeded"),
);
passwordController.text = "";
passwordRepeatController.text = "";
diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/edit_auto_backup_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/edit_auto_backup_view.dart
index fa14358ea..9368d3b77 100644
--- a/lib/pages/settings_views/global_settings_view/stack_backup_views/edit_auto_backup_view.dart
+++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/edit_auto_backup_view.dart
@@ -84,6 +84,17 @@ class _EditAutoBackupViewState extends ConsumerState {
passwordFocusNode = FocusNode();
passwordRepeatFocusNode = FocusNode();
+ if (Platform.isAndroid) {
+ WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
+ final dir = await stackFileSystem.prepareStorage();
+ if (mounted) {
+ setState(() {
+ fileLocationController.text = dir.path;
+ });
+ }
+ });
+ }
+
super.initState();
}
@@ -135,64 +146,70 @@ class _EditAutoBackupViewState extends ConsumerState {
const SizedBox(
height: 10,
),
- TextField(
- onTap: () async {
- try {
- await stackFileSystem.prepareStorage();
+ if (!Platform.isAndroid)
+ TextField(
+ onTap: Platform.isAndroid
+ ? null
+ : () async {
+ try {
+ await stackFileSystem.prepareStorage();
- if (mounted) {
- await stackFileSystem.pickDir(context);
- }
+ if (mounted) {
+ await stackFileSystem.pickDir(context);
+ }
- if (mounted) {
- setState(() {
- fileLocationController.text =
- stackFileSystem.dirPath ?? "";
- });
- }
- } catch (e, s) {
- Logging.instance.log("$e\n$s", level: LogLevel.Error);
- }
- },
- controller: fileLocationController,
- style: STextStyles.field(context),
- decoration: InputDecoration(
- hintText: "Save to...",
- suffixIcon: UnconstrainedBox(
- child: Row(
- children: [
- const SizedBox(
- width: 16,
- ),
- SvgPicture.asset(
- Assets.svg.folder,
- color: Theme.of(context)
- .extension()!
- .textDark3,
- width: 16,
- height: 16,
- ),
- const SizedBox(
- width: 12,
- ),
- ],
+ if (mounted) {
+ setState(() {
+ fileLocationController.text =
+ stackFileSystem.dirPath ?? "";
+ });
+ }
+ } catch (e, s) {
+ Logging.instance
+ .log("$e\n$s", level: LogLevel.Error);
+ }
+ },
+ controller: fileLocationController,
+ style: STextStyles.field(context),
+ decoration: InputDecoration(
+ hintText: "Save to...",
+ hintStyle: STextStyles.fieldLabel(context),
+ suffixIcon: UnconstrainedBox(
+ child: Row(
+ children: [
+ const SizedBox(
+ width: 16,
+ ),
+ SvgPicture.asset(
+ Assets.svg.folder,
+ color: Theme.of(context)
+ .extension()!
+ .textDark3,
+ width: 16,
+ height: 16,
+ ),
+ const SizedBox(
+ width: 12,
+ ),
+ ],
+ ),
),
),
+ key: const Key(
+ "createBackupSaveToFileLocationTextFieldKey"),
+ readOnly: true,
+ toolbarOptions: const ToolbarOptions(
+ copy: true,
+ cut: false,
+ paste: false,
+ selectAll: false,
+ ),
+ onChanged: (newValue) {},
),
- key: const Key(
- "createBackupSaveToFileLocationTextFieldKey"),
- readOnly: true,
- toolbarOptions: const ToolbarOptions(
- copy: true,
- cut: false,
- paste: false,
- selectAll: false,
+ if (!Platform.isAndroid)
+ const SizedBox(
+ height: 10,
),
- onChanged: (newValue) {},
- ),
- const SizedBox(
- height: 10,
- ),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
@@ -594,8 +611,14 @@ class _EditAutoBackupViewState extends ConsumerState {
await showDialog(
context: context,
barrierDismissible: false,
- builder: (_) => const StackOkDialog(
- title: "Stack Auto Backup saved"),
+ builder: (_) => Platform.isAndroid
+ ? StackOkDialog(
+ title:
+ "Stack Auto Backup saved to:",
+ message: fileToSave,
+ )
+ : const StackOkDialog(
+ title: "Stack Auto Backup saved"),
);
if (mounted) {
passwordController.text = "";
diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart
index 9665fdbaf..721141cb8 100644
--- a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart
+++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart
@@ -209,6 +209,10 @@ abstract class SWB {
Logging.instance.log(
"...createStackWalletJSON DB.instance.mutex acquired",
level: LogLevel.Info);
+ Logging.instance.log(
+ "SWB backing up nodes",
+ level: LogLevel.Warning,
+ );
try {
var primaryNodes = nodeService.primaryNodes.map((e) async {
final map = e.toMap();
@@ -231,6 +235,11 @@ abstract class SWB {
Logging.instance.log("$e $s", level: LogLevel.Error);
}
+ Logging.instance.log(
+ "SWB backing up prefs",
+ level: LogLevel.Warning,
+ );
+
Map prefs = {};
final _prefs = Prefs.instance;
await _prefs.init();
@@ -251,11 +260,21 @@ abstract class SWB {
backupJson['prefs'] = prefs;
+ Logging.instance.log(
+ "SWB backing up addressbook",
+ level: LogLevel.Warning,
+ );
+
AddressBookService addressBookService = AddressBookService();
var addresses = await addressBookService.addressBookEntries;
backupJson['addressBookEntries'] =
addresses.map((e) => e.toMap()).toList();
+ Logging.instance.log(
+ "SWB backing up wallets",
+ level: LogLevel.Warning,
+ );
+
List backupWallets = [];
for (var manager in _wallets.managers) {
Map backupWallet = {};
@@ -283,6 +302,11 @@ abstract class SWB {
}
backupJson['wallets'] = backupWallets;
+ Logging.instance.log(
+ "SWB backing up trades",
+ level: LogLevel.Warning,
+ );
+
// back up trade history
final tradesService = TradesService();
final trades =
@@ -295,6 +319,11 @@ abstract class SWB {
tradeTxidLookupDataService.all.map((e) => e.toMap()).toList();
backupJson["tradeTxidLookupData"] = lookupData;
+ Logging.instance.log(
+ "SWB backing up trade notes",
+ level: LogLevel.Warning,
+ );
+
// back up trade notes
final tradeNotesService = TradeNotesService();
final tradeNotes = tradeNotesService.all;
@@ -357,7 +386,7 @@ abstract class SWB {
final notes = walletbackup["notes"] as Map?;
if (notes != null) {
for (final note in notes.entries) {
- notesService.editOrAddNote(
+ await notesService.editOrAddNote(
txid: note.key as String, note: note.value as String);
}
}
@@ -432,11 +461,19 @@ abstract class SWB {
uiState?.preferences = StackRestoringStatus.restoring;
+ Logging.instance.log(
+ "SWB restoring prefs",
+ level: LogLevel.Warning,
+ );
await _restorePrefs(prefs);
uiState?.preferences = StackRestoringStatus.success;
uiState?.addressBook = StackRestoringStatus.restoring;
+ Logging.instance.log(
+ "SWB restoring addressbook",
+ level: LogLevel.Warning,
+ );
if (addressBookEntries != null) {
await _restoreAddressBook(addressBookEntries);
}
@@ -444,6 +481,10 @@ abstract class SWB {
uiState?.addressBook = StackRestoringStatus.success;
uiState?.nodes = StackRestoringStatus.restoring;
+ Logging.instance.log(
+ "SWB restoring nodes",
+ level: LogLevel.Warning,
+ );
await _restoreNodes(nodes, primaryNodes);
uiState?.nodes = StackRestoringStatus.success;
@@ -451,17 +492,29 @@ abstract class SWB {
// restore trade history
if (trades != null) {
+ Logging.instance.log(
+ "SWB restoring trades",
+ level: LogLevel.Warning,
+ );
await _restoreTrades(trades);
}
// restore trade history lookup data for trades send from stack wallet
if (tradeTxidLookupData != null) {
+ Logging.instance.log(
+ "SWB restoring trade look up data",
+ level: LogLevel.Warning,
+ );
await _restoreTradesLookUpData(tradeTxidLookupData, oldToNewWalletIdMap);
}
// restore trade notes
if (tradeNotes != null) {
+ Logging.instance.log(
+ "SWB restoring trade notes",
+ level: LogLevel.Warning,
+ );
await _restoreTradesNotes(tradeNotes);
}
@@ -490,9 +543,17 @@ abstract class SWB {
String jsonBackup,
StackRestoringUIState? uiState,
) async {
- if (!Platform.isLinux) Wakelock.enable();
+ if (!Platform.isLinux) await Wakelock.enable();
+ Logging.instance.log(
+ "SWB creating temp backup",
+ level: LogLevel.Warning,
+ );
final preRestoreJSON = await createStackWalletJSON();
+ Logging.instance.log(
+ "SWB temp backup created",
+ level: LogLevel.Warning,
+ );
List _currentWalletIds = Map.from(DB.instance
.get(
@@ -814,13 +875,13 @@ abstract class SWB {
}
await asyncRestore(epicCashWallets[i], uiState, walletsService);
}
- if (!Platform.isLinux) Wakelock.disable();
+ if (!Platform.isLinux) await Wakelock.disable();
// check if cancel was requested and restore previous state
if (_checkShouldCancel(preRestoreState)) {
return false;
}
- Logging.instance.log("done with SWB restore", level: LogLevel.Info);
+ Logging.instance.log("done with SWB restore", level: LogLevel.Warning);
return true;
}
@@ -849,7 +910,7 @@ abstract class SWB {
// if no contacts were present before attempted restore then delete any that
// could have been added before the restore was cancelled
for (final String idToDelete in allContactIds) {
- addressBookService.removeContact(idToDelete);
+ await addressBookService.removeContact(idToDelete);
}
} else {
final Map preContactMap = {};
@@ -886,7 +947,7 @@ abstract class SWB {
);
} else {
// otherwise remove it as it was not there before attempting SWB restore
- addressBookService.removeContact(id);
+ await addressBookService.removeContact(id);
}
}
}
@@ -898,7 +959,7 @@ abstract class SWB {
// no pre nodes found so we delete all but defaults
for (final node in currentNodes) {
if (!node.isDefault) {
- nodeService.delete(node.id, true);
+ await nodeService.delete(node.id, true);
}
}
} else {
@@ -912,7 +973,7 @@ abstract class SWB {
if (nodeData != null) {
// node existed before restore attempt
// revert to pre restore node
- nodeService.edit(
+ await nodeService.edit(
node.copyWith(
host: nodeData['host'] as String,
port: nodeData['port'] as int,
@@ -927,7 +988,7 @@ abstract class SWB {
nodeData['password'] as String?,
true);
} else {
- nodeService.delete(node.id, true);
+ await nodeService.delete(node.id, true);
}
}
}
@@ -951,7 +1012,7 @@ abstract class SWB {
// no trade history found pre restore attempt so we delete anything that
// was added during the restore attempt
for (final tradeTx in currentTrades) {
- tradesService.delete(trade: tradeTx, shouldNotifyListeners: true);
+ await tradesService.delete(trade: tradeTx, shouldNotifyListeners: true);
}
} else {
final Map preTradeMap = {};
@@ -964,13 +1025,14 @@ abstract class SWB {
if (tradeData != null) {
// trade existed before attempted restore so we don't delete it, only
// revert data to pre restore state
- tradesService.edit(
+ await tradesService.edit(
trade: ExchangeTransaction.fromJson(
tradeData as Map),
shouldNotifyListeners: true);
} else {
// trade did not exist before so we delete it
- tradesService.delete(trade: tradeTx, shouldNotifyListeners: true);
+ await tradesService.delete(
+ trade: tradeTx, shouldNotifyListeners: true);
}
}
}
@@ -982,7 +1044,7 @@ abstract class SWB {
if (tradeNotes == null) {
for (final noteEntry in currentNotes.entries) {
- tradeNotesService.delete(tradeId: noteEntry.key);
+ await tradeNotesService.delete(tradeId: noteEntry.key);
}
} else {
// grab all trade IDs of (reverted to pre state) trades
@@ -991,7 +1053,7 @@ abstract class SWB {
// delete all notes that don't correspond to an id that we have
for (final noteEntry in currentNotes.entries) {
if (!idsToKeep.contains(noteEntry.key)) {
- tradeNotesService.delete(tradeId: noteEntry.key);
+ await tradeNotesService.delete(tradeId: noteEntry.key);
}
}
}
@@ -1009,7 +1071,7 @@ abstract class SWB {
for (int i = 0; i < tradeTxidLookupData.length; i++) {
final json = Map.from(tradeTxidLookupData[i] as Map);
TradeWalletLookup lookup = TradeWalletLookup.fromJson(json);
- tradeTxidLookupDataService.save(tradeWalletLookup: lookup);
+ await tradeTxidLookupDataService.save(tradeWalletLookup: lookup);
}
}
@@ -1127,14 +1189,14 @@ abstract class SWB {
) async {
final tradesService = TradesService();
for (int i = 0; i < trades.length - 1; i++) {
- tradesService.add(
+ await tradesService.add(
trade: ExchangeTransaction.fromJson(trades[i] as Map),
shouldNotifyListeners: false,
);
}
// only call notifyListeners on last one added
if (trades.isNotEmpty) {
- tradesService.add(
+ await tradesService.add(
trade:
ExchangeTransaction.fromJson(trades.last as Map),
shouldNotifyListeners: true,
@@ -1177,7 +1239,7 @@ abstract class SWB {
}
}
- tradeTxidLookupDataService.save(tradeWalletLookup: lookup);
+ await tradeTxidLookupDataService.save(tradeWalletLookup: lookup);
}
}
@@ -1186,7 +1248,8 @@ abstract class SWB {
) async {
final tradeNotesService = TradeNotesService();
for (final note in tradeNotes.entries) {
- tradeNotesService.set(tradeId: note.key, note: note.value as String);
+ await tradeNotesService.set(
+ tradeId: note.key, note: note.value as String);
}
}
}
diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/stack_file_system.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/stack_file_system.dart
index 75f2b272f..3196938da 100644
--- a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/stack_file_system.dart
+++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/stack_file_system.dart
@@ -13,7 +13,7 @@ class StackFileSystem {
final bool isDesktop = !(Platform.isAndroid || Platform.isIOS);
- Future prepareStorage() async {
+ Future prepareStorage() async {
rootPath = (await getApplicationDocumentsDirectory());
debugPrint(rootPath!.absolute.toString());
if (Platform.isAndroid) {
@@ -47,6 +47,7 @@ class StackFileSystem {
debugPrint("$e $s");
}
startPath = sampleFolder;
+ return sampleFolder;
}
Future pickDir(BuildContext context) async {
diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/restore_from_file_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/restore_from_file_view.dart
index feda62d0a..cec114023 100644
--- a/lib/pages/settings_views/global_settings_view/stack_backup_views/restore_from_file_view.dart
+++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/restore_from_file_view.dart
@@ -99,29 +99,17 @@ class _RestoreFromFileViewState extends ConsumerState {
onTap: () async {
try {
await stackFileSystem.prepareStorage();
- // ref
- // .read(shouldShowLockscreenOnResumeStateProvider
- // .state)
- // .state = false;
- await stackFileSystem.openFile(context);
+ if (mounted) {
+ await stackFileSystem.openFile(context);
+ }
- // Future.delayed(
- // const Duration(seconds: 2),
- // () => ref
- // .read(
- // shouldShowLockscreenOnResumeStateProvider
- // .state)
- // .state = true,
- // );
-
- fileLocationController.text =
- stackFileSystem.filePath ?? "";
- setState(() {});
+ if (mounted) {
+ setState(() {
+ fileLocationController.text =
+ stackFileSystem.filePath ?? "";
+ });
+ }
} catch (e, s) {
- // ref
- // .read(shouldShowLockscreenOnResumeStateProvider
- // .state)
- // .state = true;
Logging.instance
.log("$e\n$s", level: LogLevel.Error);
}
@@ -130,6 +118,7 @@ class _RestoreFromFileViewState extends ConsumerState {
style: STextStyles.field(context),
decoration: InputDecoration(
hintText: "Choose file...",
+ hintStyle: STextStyles.fieldLabel(context),
suffixIcon: UnconstrainedBox(
child: Row(
children: [
diff --git a/lib/pages/wallet_view/sub_widgets/transactions_list.dart b/lib/pages/wallet_view/sub_widgets/transactions_list.dart
index bda060071..f95e75997 100644
--- a/lib/pages/wallet_view/sub_widgets/transactions_list.dart
+++ b/lib/pages/wallet_view/sub_widgets/transactions_list.dart
@@ -9,7 +9,12 @@ import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
+import 'package:stackwallet/widgets/trade_card.dart';
import 'package:stackwallet/widgets/transaction_card.dart';
+import 'package:tuple/tuple.dart';
+
+import '../../../providers/global/trades_service_provider.dart';
+import '../../exchange_view/trade_details_view.dart';
class TransactionsList extends ConsumerStatefulWidget {
const TransactionsList({
@@ -125,18 +130,67 @@ class _TransactionsListState extends ConsumerState {
radius = _borderRadiusFirst;
}
final tx = list[index];
- return Container(
- decoration: BoxDecoration(
- color: Theme.of(context).extension()!.popupBG,
- borderRadius: radius,
- ),
- child: TransactionCard(
- // this may mess with combined firo transactions
- key: Key(tx.toString()), //
- transaction: tx,
- walletId: widget.walletId,
- ),
- );
+
+ final matchingTrades = ref
+ .read(tradesServiceProvider)
+ .trades
+ .where((e) =>
+ e.statusObject != null &&
+ (e.statusObject!.payinHash == tx.txid ||
+ e.statusObject!.payoutHash == tx.txid));
+ if (tx.txType == "Sent" && matchingTrades.isNotEmpty) {
+ final trade = matchingTrades.first;
+ return Container(
+ decoration: BoxDecoration(
+ color:
+ Theme.of(context).extension()!.popupBG,
+ borderRadius: radius,
+ ),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ TransactionCard(
+ // this may mess with combined firo transactions
+ key: Key(tx.toString()), //
+ transaction: tx,
+ walletId: widget.walletId,
+ ),
+ TradeCard(
+ // this may mess with combined firo transactions
+ key: Key(tx.toString() + trade.uuid), //
+ trade: trade,
+ onTap: () {
+ unawaited(
+ Navigator.of(context).pushNamed(
+ TradeDetailsView.routeName,
+ arguments: Tuple4(
+ trade.id,
+ tx,
+ widget.walletId,
+ ref.read(managerProvider).walletName,
+ ),
+ ),
+ );
+ },
+ )
+ ],
+ ),
+ );
+ } else {
+ return Container(
+ decoration: BoxDecoration(
+ color:
+ Theme.of(context).extension()!.popupBG,
+ borderRadius: radius,
+ ),
+ child: TransactionCard(
+ // this may mess with combined firo transactions
+ key: Key(tx.toString()), //
+ transaction: tx,
+ walletId: widget.walletId,
+ ),
+ );
+ }
},
),
);
diff --git a/lib/pages/wallets_view/sub_widgets/empty_wallets.dart b/lib/pages/wallets_view/sub_widgets/empty_wallets.dart
index 4309186ac..8691b7d84 100644
--- a/lib/pages/wallets_view/sub_widgets/empty_wallets.dart
+++ b/lib/pages/wallets_view/sub_widgets/empty_wallets.dart
@@ -96,7 +96,14 @@ class AddWalletButton extends StatelessWidget {
.extension()!
.getPrimaryEnabledButtonColor(context),
onPressed: () {
- Navigator.of(context).pushNamed(AddWalletView.routeName);
+ if (isDesktop) {
+ Navigator.of(
+ context,
+ rootNavigator: true,
+ ).pushNamed(AddWalletView.routeName);
+ } else {
+ Navigator.of(context).pushNamed(AddWalletView.routeName);
+ }
},
child: Center(
child: Container(
diff --git a/lib/pages_desktop_specific/home/settings_view/settings_view.dart b/lib/pages_desktop_specific/home/settings_view/settings_view.dart
new file mode 100644
index 000000000..c308f2962
--- /dev/null
+++ b/lib/pages_desktop_specific/home/settings_view/settings_view.dart
@@ -0,0 +1,20 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+class SettingsView extends ConsumerStatefulWidget {
+ const SettingsView({Key? key}) : super(key: key);
+
+ static const String routeName = "/settingsView";
+
+ @override
+ ConsumerState createState() => _SettingsView();
+}
+
+class _SettingsView extends ConsumerState {
+ @override
+ Widget build(BuildContext context) {
+ debugPrint("BUILD: $runtimeType");
+ // TODO: implement build
+ throw UnimplementedError();
+ }
+}
diff --git a/lib/route_generator.dart b/lib/route_generator.dart
index 797b62969..368188171 100644
--- a/lib/route_generator.dart
+++ b/lib/route_generator.dart
@@ -1,7 +1,9 @@
+import 'package:decimal/decimal.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/contact_address_entry.dart';
+import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart';
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
import 'package:stackwallet/models/paymint/transactions_model.dart';
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
@@ -20,12 +22,14 @@ import 'package:stackwallet/pages/address_book_views/subviews/address_book_filte
import 'package:stackwallet/pages/address_book_views/subviews/contact_details_view.dart';
import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_address_view.dart';
import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_name_emoji_view.dart';
+import 'package:stackwallet/pages/exchange_view/choose_from_stack_view.dart';
import 'package:stackwallet/pages/exchange_view/edit_trade_note_view.dart';
import 'package:stackwallet/pages/exchange_view/exchange_loading_overlay.dart';
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_1_view.dart';
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_2_view.dart';
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_3_view.dart';
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_4_view.dart';
+import 'package:stackwallet/pages/exchange_view/send_from_view.dart';
import 'package:stackwallet/pages/exchange_view/trade_details_view.dart';
import 'package:stackwallet/pages/exchange_view/wallet_initiated_exchange_view.dart';
import 'package:stackwallet/pages/home_view/home_view.dart';
@@ -879,6 +883,37 @@ class RouteGenerator {
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
+ case ChooseFromStackView.routeName:
+ if (args is Coin) {
+ return getRoute(
+ shouldUseMaterialRoute: useMaterialPageRoute,
+ builder: (_) => ChooseFromStackView(
+ coin: args,
+ ),
+ settings: RouteSettings(
+ name: settings.name,
+ ),
+ );
+ }
+ return _routeError("${settings.name} invalid args: ${args.toString()}");
+
+ case SendFromView.routeName:
+ if (args is Tuple4) {
+ return getRoute(
+ shouldUseMaterialRoute: useMaterialPageRoute,
+ builder: (_) => SendFromView(
+ coin: args.item1,
+ amount: args.item2,
+ trade: args.item4,
+ address: args.item3,
+ ),
+ settings: RouteSettings(
+ name: settings.name,
+ ),
+ );
+ }
+ return _routeError("${settings.name} invalid args: ${args.toString()}");
+
// == Desktop specific routes ============================================
case CreatePasswordView.routeName:
return getRoute(
diff --git a/lib/services/coins/epiccash/epiccash_wallet.dart b/lib/services/coins/epiccash/epiccash_wallet.dart
index e33428319..112e6c176 100644
--- a/lib/services/coins/epiccash/epiccash_wallet.dart
+++ b/lib/services/coins/epiccash/epiccash_wallet.dart
@@ -259,6 +259,9 @@ Future deleteEpicWallet({
if (Platform.isIOS) {
appDir = (await getLibraryDirectory());
}
+ if (Platform.isLinux) {
+ appDir = Directory("${appDir.path}/.stackwallet");
+ }
final path = "${appDir.path}/epiccash";
final String name = walletId;
@@ -1232,6 +1235,9 @@ class EpicCashWallet extends CoinServiceAPI {
if (Platform.isIOS) {
appDir = (await getLibraryDirectory());
}
+ if (Platform.isLinux) {
+ appDir = Directory("${appDir.path}/.stackwallet");
+ }
final path = "${appDir.path}/epiccash";
final String name = _walletId.trim();
return '$path/$name';
diff --git a/lib/services/coins/monero/monero_wallet.dart b/lib/services/coins/monero/monero_wallet.dart
index b63e711a4..2488acc50 100644
--- a/lib/services/coins/monero/monero_wallet.dart
+++ b/lib/services/coins/monero/monero_wallet.dart
@@ -910,6 +910,10 @@ class MoneroWallet extends CoinServiceAPI {
if (Platform.isIOS) {
root = (await getLibraryDirectory());
}
+ //
+ if (Platform.isLinux) {
+ root = Directory("${root.path}/.stackwallet");
+ }
final prefix = walletTypeToString(type).toLowerCase();
final walletsDir = Directory('${root.path}/wallets');
final walletDire = Directory('${walletsDir.path}/$prefix/$name');
diff --git a/lib/services/coins/namecoin/namecoin_wallet.dart b/lib/services/coins/namecoin/namecoin_wallet.dart
index bedd50327..af4217488 100644
--- a/lib/services/coins/namecoin/namecoin_wallet.dart
+++ b/lib/services/coins/namecoin/namecoin_wallet.dart
@@ -2501,7 +2501,8 @@ class NamecoinWallet extends CoinServiceAPI {
int totalOutput = 0;
for (final output in txObject["vout"] as List) {
- final address = output["scriptPubKey"]["addresses"][0];
+ Logging.instance.log(output, level: LogLevel.Info);
+ final address = output["scriptPubKey"]["address"];
final value = output["value"];
final _value = (Decimal.parse(value.toString()) *
Decimal.fromInt(Constants.satsPerCoin))
diff --git a/lib/services/coins/wownero/wownero_wallet.dart b/lib/services/coins/wownero/wownero_wallet.dart
index 7114269ec..b284c8733 100644
--- a/lib/services/coins/wownero/wownero_wallet.dart
+++ b/lib/services/coins/wownero/wownero_wallet.dart
@@ -913,6 +913,9 @@ class WowneroWallet extends CoinServiceAPI {
if (Platform.isIOS) {
root = (await getLibraryDirectory());
}
+ if (Platform.isLinux) {
+ root = Directory("${root.path}/.stackwallet");
+ }
final prefix = walletTypeToString(type).toLowerCase();
final walletsDir = Directory('${root.path}/wallets');
final walletDire = Directory('${walletsDir.path}/$prefix/$name');
diff --git a/lib/services/debug_service.dart b/lib/services/debug_service.dart
index e7347c6a3..c63e22cb2 100644
--- a/lib/services/debug_service.dart
+++ b/lib/services/debug_service.dart
@@ -75,7 +75,8 @@ class DebugService extends ChangeNotifier {
level: LogLevel.Info);
}
- Future exportToFile(String directory, EventBus eventBus) async {
+ /// returns the filename of the saved logs file
+ Future exportToFile(String directory, EventBus eventBus) async {
final now = DateTime.now();
final filename =
"Stack_Wallet_logs_${now.year}_${now.month}_${now.day}_${now.hour}_${now.minute}_${now.second}.txt";
@@ -99,5 +100,6 @@ class DebugService extends ChangeNotifier {
await sink.close();
eventBus.fire(1.0);
+ return filename;
}
}
diff --git a/lib/utilities/assets.dart b/lib/utilities/assets.dart
index 94558e623..1b38f42bc 100644
--- a/lib/utilities/assets.dart
+++ b/lib/utilities/assets.dart
@@ -51,7 +51,9 @@ class _SVG {
String txExchangeFailed(BuildContext context) =>
"assets/svg/${Theme.of(context).extension()!.themeType.name}/tx-exchange-icon-failed.svg";
+ String get polygon => "assets/svg/Polygon.svg";
String get drd => "assets/svg/drd-icon.svg";
+ String get boxAuto => "assets/svg/box-auto.svg";
String get plus => "assets/svg/plus.svg";
String get gear => "assets/svg/gear.svg";
String get bell => "assets/svg/bell.svg";
diff --git a/lib/utilities/constants.dart b/lib/utilities/constants.dart
index f5139477c..69c1444c3 100644
--- a/lib/utilities/constants.dart
+++ b/lib/utilities/constants.dart
@@ -21,6 +21,8 @@ abstract class Constants {
static const int satsPerCoinWownero = 100000000000;
static const int satsPerCoin = 100000000;
static const int decimalPlaces = 8;
+ static const int decimalPlacesWownero = 11;
+ static const int decimalPlacesMonero = 12;
static const int notificationsMax = 0xFFFFFFFF;
static const Duration networkAliveTimerDuration = Duration(seconds: 10);
diff --git a/lib/utilities/text_styles.dart b/lib/utilities/text_styles.dart
index 7c6aa2aaf..21a1ee488 100644
--- a/lib/utilities/text_styles.dart
+++ b/lib/utilities/text_styles.dart
@@ -698,6 +698,25 @@ class STextStyles {
}
}
+ static TextStyle desktopTextExtraExtraSmall(BuildContext context) {
+ switch (_theme(context).themeType) {
+ case ThemeType.light:
+ return GoogleFonts.inter(
+ color: _theme(context).textSubtitle1,
+ fontWeight: FontWeight.w500,
+ fontSize: 14,
+ height: 21 / 14,
+ );
+ case ThemeType.dark:
+ return GoogleFonts.inter(
+ color: _theme(context).textSubtitle1,
+ fontWeight: FontWeight.w500,
+ fontSize: 14,
+ height: 21 / 14,
+ );
+ }
+ }
+
static TextStyle desktopButtonSmallSecondaryEnabled(BuildContext context) {
switch (_theme(context).themeType) {
case ThemeType.light:
diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt
index 12d9f1f71..53391e7ad 100644
--- a/linux/CMakeLists.txt
+++ b/linux/CMakeLists.txt
@@ -137,6 +137,9 @@ install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/linux/build/libcw_monero.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
+install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/linux/build/libcw_wownero.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libepiccash/scripts/linux/build/rust/target/x86_64-unknown-linux-gnu/release/libepic_cash_wallet.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig
index c2efd0b60..4b81f9b2d 100644
--- a/macos/Flutter/Flutter-Debug.xcconfig
+++ b/macos/Flutter/Flutter-Debug.xcconfig
@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig
index c2efd0b60..5caa9d157 100644
--- a/macos/Flutter/Flutter-Release.xcconfig
+++ b/macos/Flutter/Flutter-Release.xcconfig
@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/macos/Podfile b/macos/Podfile
new file mode 100644
index 000000000..dade8dfad
--- /dev/null
+++ b/macos/Podfile
@@ -0,0 +1,40 @@
+platform :osx, '10.11'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_macos_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+ use_modular_headers!
+
+ flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_macos_build_settings(target)
+ end
+end
diff --git a/pubspec.yaml b/pubspec.yaml
index dbe44c852..60f65f3e3 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -11,7 +11,7 @@ description: Stack Wallet
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
-version: 1.4.52+68
+version: 1.5.0+70
environment:
sdk: ">=2.17.0 <3.0.0"
@@ -289,6 +289,7 @@ flutter:
- assets/svg/tx-icon-anonymize.svg
- assets/svg/tx-icon-anonymize-pending.svg
- assets/svg/tx-icon-anonymize-failed.svg
+ - assets/svg/Polygon.svg
# coin icons
- assets/svg/coin_icons/Bitcoin.svg
- assets/svg/coin_icons/Bitcoincash.svg
@@ -312,6 +313,7 @@ flutter:
- assets/svg/exchange-3.svg
- assets/svg/message-question-1.svg
- assets/svg/drd-icon.svg
+ - assets/svg/box-auto.svg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
diff --git a/scripts/prebuild.sh b/scripts/prebuild.sh
new file mode 100644
index 000000000..31acfa82c
--- /dev/null
+++ b/scripts/prebuild.sh
@@ -0,0 +1,6 @@
+# Create template lib/external_api_keys.dart file if it doesn't already exist
+KEYS=../lib/external_api_keys.dart
+if ! test -f "$KEYS"; then
+ echo 'prebuild.sh: creating template lib/external_api_keys.dart file'
+ echo 'const kChangeNowApiKey = "";' > $KEYS
+fi
diff --git a/test/services/coins/bitcoincash/bitcoincash_wallet_test.dart b/test/services/coins/bitcoincash/bitcoincash_wallet_test.dart
index c373b739a..a9a7fdaf7 100644
--- a/test/services/coins/bitcoincash/bitcoincash_wallet_test.dart
+++ b/test/services/coins/bitcoincash/bitcoincash_wallet_test.dart
@@ -1,3092 +1,3090 @@
-// import 'dart:convert';
-//
-// import 'package:bitcoindart/bitcoindart.dart';
-// import 'package:decimal/decimal.dart';
-// import 'package:flutter_test/flutter_test.dart';
-// import 'package:hive/hive.dart';
-// import 'package:hive_test/hive_test.dart';
-// import 'package:mockito/annotations.dart';
-// import 'package:mockito/mockito.dart';
-// import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart';
-// import 'package:stackwallet/electrumx_rpc/electrumx.dart';
-// import 'package:stackwallet/hive/db.dart';
-// import 'package:stackwallet/models/paymint/fee_object_model.dart';
-// import 'package:stackwallet/models/paymint/transactions_model.dart';
-// import 'package:stackwallet/models/paymint/utxo_model.dart';
-// import 'package:stackwallet/services/coins/bitcoincash/bitcoincash_wallet.dart';
-// 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 'bitcoincash_history_sample_data.dart';
-// import 'bitcoincash_wallet_test.mocks.dart';
-// import 'bitcoincash_wallet_test_parameters.dart';
-//
-// @GenerateMocks(
-// [ElectrumX, CachedElectrumX, PriceAPI, TransactionNotificationTracker])
-void main() {}
-// group("bitcoincash constants", () {
-// test("bitcoincash minimum confirmations", () async {
-// expect(MINIMUM_CONFIRMATIONS, 3);
-// });
-// test("bitcoincash dust limit", () async {
-// expect(DUST_LIMIT, 546);
-// });
-// test("bitcoincash mainnet genesis block hash", () async {
-// expect(GENESIS_HASH_MAINNET,
-// "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
-// });
-//
-// test("bitcoincash testnet genesis block hash", () async {
-// expect(GENESIS_HASH_TESTNET,
-// "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943");
-// });
-// });
-//
-// test("bitcoincash DerivePathType enum", () {
-// expect(DerivePathType.values.length, 2);
-// expect(DerivePathType.values.toString(),
-// "[DerivePathType.bip44, DerivePathType.bip49]");
-// });
-//
-// group("bip32 node/root", () {
-// test("getBip32Root", () {
-// final root = getBip32Root(TEST_MNEMONIC, bitcoincash);
-// expect(root.toWIF(), ROOT_WIF);
-// });
-//
-// test("basic getBip32Node", () {
-// final node =
-// getBip32Node(0, 0, TEST_MNEMONIC, bitcoincash, DerivePathType.bip44);
-// expect(node.toWIF(), NODE_WIF_44);
-// });
-// });
-//
-// group("validate mainnet bitcoincash addresses", () {
-// MockElectrumX? client;
-// MockCachedElectrumX? cachedClient;
-// MockPriceAPI? priceAPI;
-// FakeSecureStorage? secureStore;
-// MockTransactionNotificationTracker? tracker;
-//
-// BitcoinCashWallet? mainnetWallet;
-//
-// setUp(() {
-// client = MockElectrumX();
-// cachedClient = MockCachedElectrumX();
-// priceAPI = MockPriceAPI();
-// secureStore = FakeSecureStorage();
-// tracker = MockTransactionNotificationTracker();
-//
-// mainnetWallet = BitcoinCashWallet(
-// walletId: "validateAddressMainNet",
-// walletName: "validateAddressMainNet",
-// coin: Coin.bitcoincash,
-// client: client!,
-// cachedClient: cachedClient!,
-// tracker: tracker!,
-// priceAPI: priceAPI,
-// secureStore: secureStore,
-// );
-// });
-//
-// test("valid mainnet legacy/p2pkh address type", () {
-// expect(
-// mainnetWallet?.addressType(
-// address: "1DP3PUePwMa5CoZwzjznVKhzdLsZftjcAT"),
-// DerivePathType.bip44);
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("invalid base58 address type", () {
-// expect(
-// () => mainnetWallet?.addressType(
-// address: "mhqpGtwhcR6gFuuRjLTpHo41919QfuGy8Y"),
-// throwsArgumentError);
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("invalid bech32 address type", () {
-// expect(
-// () => mainnetWallet?.addressType(
-// address: "tb1qzzlm6mnc8k54mx6akehl8p9ray8r439va5ndyq"),
-// throwsArgumentError);
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("address has no matching script", () {
-// expect(
-// () => mainnetWallet?.addressType(
-// address: "mpMk94ETazqonHutyC1v6ajshgtP8oiFKU"),
-// throwsArgumentError);
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("invalid mainnet bitcoincash legacy/p2pkh address", () {
-// expect(
-// mainnetWallet?.validateAddress("mhqpGtwhcR6gFuuRjLTpHo41919QfuGy8Y"),
-// true);
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-// });
-//
-// group("testNetworkConnection", () {
-// MockElectrumX? client;
-// MockCachedElectrumX? cachedClient;
-// MockPriceAPI? priceAPI;
-// FakeSecureStorage? secureStore;
-// MockTransactionNotificationTracker? tracker;
-//
-// BitcoinCashWallet? bch;
-//
-// setUp(() {
-// client = MockElectrumX();
-// cachedClient = MockCachedElectrumX();
-// priceAPI = MockPriceAPI();
-// secureStore = FakeSecureStorage();
-// tracker = MockTransactionNotificationTracker();
-//
-// bch = BitcoinCashWallet(
-// walletId: "testNetworkConnection",
-// walletName: "testNetworkConnection",
-// coin: Coin.bitcoincash,
-// client: client!,
-// cachedClient: cachedClient!,
-// tracker: tracker!,
-// priceAPI: priceAPI,
-// secureStore: secureStore,
-// );
-// });
-//
-// test("attempted connection fails due to server error", () async {
-// when(client?.ping()).thenAnswer((_) async => false);
-// final bool? result = await bch?.testNetworkConnection();
-// expect(result, false);
-// expect(secureStore?.interactions, 0);
-// verify(client?.ping()).called(1);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("attempted connection fails due to exception", () async {
-// when(client?.ping()).thenThrow(Exception);
-// final bool? result = await bch?.testNetworkConnection();
-// expect(result, false);
-// expect(secureStore?.interactions, 0);
-// verify(client?.ping()).called(1);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("attempted connection test success", () async {
-// when(client?.ping()).thenAnswer((_) async => true);
-// final bool? result = await bch?.testNetworkConnection();
-// expect(result, true);
-// expect(secureStore?.interactions, 0);
-// verify(client?.ping()).called(1);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-// });
-//
-// group("basic getters, setters, and functions", () {
-// final bchcoin = Coin.bitcoincash;
-// final testWalletId = "BCHtestWalletID";
-// final testWalletName = "BCHWallet";
-//
-// MockElectrumX? client;
-// MockCachedElectrumX? cachedClient;
-// MockPriceAPI? priceAPI;
-// FakeSecureStorage? secureStore;
-// MockTransactionNotificationTracker? tracker;
-//
-// BitcoinCashWallet? bch;
-//
-// setUp(() async {
-// client = MockElectrumX();
-// cachedClient = MockCachedElectrumX();
-// priceAPI = MockPriceAPI();
-// secureStore = FakeSecureStorage();
-// tracker = MockTransactionNotificationTracker();
-//
-// bch = BitcoinCashWallet(
-// walletId: testWalletId,
-// walletName: testWalletName,
-// coin: bchcoin,
-// client: client!,
-// cachedClient: cachedClient!,
-// tracker: tracker!,
-// priceAPI: priceAPI,
-// secureStore: secureStore,
-// );
-// });
-//
-// test("get networkType main", () async {
-// expect(bch?.coin, bchcoin);
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("get networkType test", () async {
-// bch = BitcoinCashWallet(
-// walletId: testWalletId,
-// walletName: testWalletName,
-// coin: bchcoin,
-// client: client!,
-// cachedClient: cachedClient!,
-// tracker: tracker!,
-// priceAPI: priceAPI,
-// secureStore: secureStore,
-// );
-// expect(bch?.coin, bchcoin);
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("get cryptoCurrency", () async {
-// expect(Coin.bitcoincash, Coin.bitcoincash);
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("get coinName", () async {
-// expect(Coin.bitcoincash, Coin.bitcoincash);
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("get coinTicker", () async {
-// expect(Coin.bitcoincash, Coin.bitcoincash);
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("get and set walletName", () async {
-// expect(Coin.bitcoincash, Coin.bitcoincash);
-// bch?.walletName = "new name";
-// expect(bch?.walletName, "new name");
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("estimateTxFee", () async {
-// expect(bch?.estimateTxFee(vSize: 356, feeRatePerKB: 1), 356);
-// expect(bch?.estimateTxFee(vSize: 356, feeRatePerKB: 900), 356);
-// expect(bch?.estimateTxFee(vSize: 356, feeRatePerKB: 999), 356);
-// expect(bch?.estimateTxFee(vSize: 356, feeRatePerKB: 1000), 356);
-// expect(bch?.estimateTxFee(vSize: 356, feeRatePerKB: 1001), 712);
-// expect(bch?.estimateTxFee(vSize: 356, feeRatePerKB: 1699), 712);
-// expect(bch?.estimateTxFee(vSize: 356, feeRatePerKB: 2000), 712);
-// expect(bch?.estimateTxFee(vSize: 356, feeRatePerKB: 12345), 4628);
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("get fees succeeds", () async {
-// when(client?.ping()).thenAnswer((_) async => true);
-// 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_TESTNET,
-// "hash_function": "sha256",
-// "services": []
-// });
-// when(client?.estimateFee(blocks: 1))
-// .thenAnswer((realInvocation) async => Decimal.zero);
-// when(client?.estimateFee(blocks: 5))
-// .thenAnswer((realInvocation) async => Decimal.one);
-// when(client?.estimateFee(blocks: 20))
-// .thenAnswer((realInvocation) async => Decimal.ten);
-//
-// final fees = await bch?.fees;
-// expect(fees, isA());
-// expect(fees?.slow, 1000000000);
-// expect(fees?.medium, 100000000);
-// expect(fees?.fast, 0);
-//
-// verify(client?.estimateFee(blocks: 1)).called(1);
-// verify(client?.estimateFee(blocks: 5)).called(1);
-// verify(client?.estimateFee(blocks: 20)).called(1);
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("get fees fails", () async {
-// when(client?.ping()).thenAnswer((_) async => true);
-// 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_TESTNET,
-// "hash_function": "sha256",
-// "services": []
-// });
-// when(client?.estimateFee(blocks: 1))
-// .thenAnswer((realInvocation) async => Decimal.zero);
-// when(client?.estimateFee(blocks: 5))
-// .thenAnswer((realInvocation) async => Decimal.one);
-// when(client?.estimateFee(blocks: 20))
-// .thenThrow(Exception("some exception"));
-//
-// bool didThrow = false;
-// try {
-// await bch?.fees;
-// } catch (_) {
-// didThrow = true;
-// }
-//
-// expect(didThrow, true);
-//
-// verify(client?.estimateFee(blocks: 1)).called(1);
-// verify(client?.estimateFee(blocks: 5)).called(1);
-// verify(client?.estimateFee(blocks: 20)).called(1);
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("get maxFee", () async {
-// when(client?.ping()).thenAnswer((_) async => true);
-// 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_TESTNET,
-// "hash_function": "sha256",
-// "services": []
-// });
-// when(client?.estimateFee(blocks: 20))
-// .thenAnswer((realInvocation) async => Decimal.zero);
-// when(client?.estimateFee(blocks: 5))
-// .thenAnswer((realInvocation) async => Decimal.one);
-// when(client?.estimateFee(blocks: 1))
-// .thenAnswer((realInvocation) async => Decimal.ten);
-//
-// final maxFee = await bch?.maxFee;
-// expect(maxFee, 1000000000);
-//
-// verify(client?.estimateFee(blocks: 1)).called(1);
-// verify(client?.estimateFee(blocks: 5)).called(1);
-// verify(client?.estimateFee(blocks: 20)).called(1);
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-// });
-//
-// group("BCHWallet service class functions that depend on shared storage", () {
-// final bchcoin = Coin.bitcoincash;
-// final bchtestcoin = Coin.bitcoincashTestnet;
-// final testWalletId = "BCHtestWalletID";
-// final testWalletName = "BCHWallet";
-//
-// bool hiveAdaptersRegistered = false;
-//
-// MockElectrumX? client;
-// MockCachedElectrumX? cachedClient;
-// MockPriceAPI? priceAPI;
-// FakeSecureStorage? secureStore;
-// MockTransactionNotificationTracker? tracker;
-//
-// BitcoinCashWallet? bch;
-//
-// setUp(() async {
-// await setUpTestHive();
-// if (!hiveAdaptersRegistered) {
-// hiveAdaptersRegistered = true;
-//
-// // Registering Transaction Model Adapters
-// Hive.registerAdapter(TransactionDataAdapter());
-// Hive.registerAdapter(TransactionChunkAdapter());
-// Hive.registerAdapter(TransactionAdapter());
-// Hive.registerAdapter(InputAdapter());
-// Hive.registerAdapter(OutputAdapter());
-//
-// // Registering Utxo Model Adapters
-// Hive.registerAdapter(UtxoDataAdapter());
-// Hive.registerAdapter(UtxoObjectAdapter());
-// Hive.registerAdapter(StatusAdapter());
-//
-// final wallets = await Hive.openBox('wallets');
-// await wallets.put('currentWalletName', testWalletName);
-// }
-//
-// client = MockElectrumX();
-// cachedClient = MockCachedElectrumX();
-// priceAPI = MockPriceAPI();
-// secureStore = FakeSecureStorage();
-// tracker = MockTransactionNotificationTracker();
-//
-// bch = BitcoinCashWallet(
-// walletId: testWalletId,
-// walletName: testWalletName,
-// coin: bchcoin,
-// client: client!,
-// cachedClient: cachedClient!,
-// tracker: tracker!,
-// priceAPI: priceAPI,
-// secureStore: secureStore,
-// );
-// });
-//
-// // test("initializeWallet no network", () async {
-// // when(client?.ping()).thenAnswer((_) async => false);
-// // await Hive.openBox(testWalletId);
-// // await Hive.openBox(DB.boxNamePrefs);
-// // expect(bch?.initializeNew(), false);
-// // expect(secureStore?.interactions, 0);
-// // verify(client?.ping()).called(0);
-// // verifyNoMoreInteractions(client);
-// // verifyNoMoreInteractions(cachedClient);
-// // verifyNoMoreInteractions(priceAPI);
-// // });
-//
-// // test("initializeExisting no network exception", () async {
-// // when(client?.ping()).thenThrow(Exception("Network connection failed"));
-// // // bch?.initializeNew();
-// // expect(bch?.initializeExisting(), false);
-// // expect(secureStore?.interactions, 0);
-// // verify(client?.ping()).called(1);
-// // verifyNoMoreInteractions(client);
-// // verifyNoMoreInteractions(cachedClient);
-// // verifyNoMoreInteractions(priceAPI);
-// // });
-//
-// test("initializeNew mainnet throws bad network", () 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_TESTNET,
-// "hash_function": "sha256",
-// "services": []
-// });
-//
-// await Hive.openBox(testWalletId);
-// await Hive.openBox(DB.boxNamePrefs);
-//
-// expectLater(() => bch?.initializeNew(), throwsA(isA()))
-// .then((_) {
-// expect(secureStore?.interactions, 0);
-// verifyNever(client?.ping()).called(0);
-// verify(client?.getServerFeatures()).called(1);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(priceAPI);
-// });
-// });
-//
-// test("initializeNew throws mnemonic overwrite exception", () 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": []
-// });
-// await secureStore?.write(
-// key: "${testWalletId}_mnemonic", value: "some mnemonic");
-//
-// await Hive.openBox(testWalletId);
-// await Hive.openBox(DB.boxNamePrefs);
-//
-// expectLater(() => bch?.initializeNew(), throwsA(isA()))
-// .then((_) {
-// expect(secureStore?.interactions, 2);
-// verifyNever(client?.ping()).called(0);
-// verify(client?.getServerFeatures()).called(1);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(priceAPI);
-// });
-// });
-//
-// test("initializeExisting testnet throws bad network", () async {
-// when(client?.ping()).thenAnswer((_) async => true);
-// 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_TESTNET,
-// "hash_function": "sha256",
-// "services": []
-// });
-//
-// bch = BitcoinCashWallet(
-// walletId: testWalletId,
-// walletName: testWalletName,
-// coin: bchcoin,
-// client: client!,
-// cachedClient: cachedClient!,
-// tracker: tracker!,
-// priceAPI: priceAPI,
-// secureStore: secureStore,
-// );
-//
-// await Hive.openBox(testWalletId);
-// await Hive.openBox(DB.boxNamePrefs);
-//
-// expectLater(() => bch?.initializeNew(), throwsA(isA()))
-// .then((_) {
-// expect(secureStore?.interactions, 0);
-// verifyNever(client?.ping()).called(0);
-// verify(client?.getServerFeatures()).called(1);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(priceAPI);
-// });
-// });
-//
-// // test("getCurrentNode", () async {
-// // // when(priceAPI?.getbitcoincashPrice(baseCurrency: "USD"))
-// // // .thenAnswer((realInvocation) async => Decimal.fromInt(10));
-// // when(client?.ping()).thenAnswer((_) async => true);
-// // 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": []
-// // });
-// // // await DebugService.instance.init();
-// // expect(bch?.initializeExisting(), true);
-// //
-// // bool didThrow = false;
-// // try {
-// // await bch?.getCurrentNode();
-// // } catch (_) {
-// // didThrow = true;
-// // }
-// // // expect no nodes on a fresh wallet unless set in db externally
-// // expect(didThrow, true);
-// //
-// // // set node
-// // final wallet = await Hive.openBox(testWalletId);
-// // await wallet.put("nodes", {
-// // "default": {
-// // "id": "some nodeID",
-// // "ipAddress": "some address",
-// // "port": "9000",
-// // "useSSL": true,
-// // }
-// // });
-// // await wallet.put("activeNodeName", "default");
-// //
-// // // try fetching again
-// // final node = await bch?.getCurrentNode();
-// // expect(node.toString(),
-// // "ElectrumXNode: {address: some address, port: 9000, name: default, useSSL: true}");
-// //
-// // verify(client?.ping()).called(1);
-// // verify(client?.getServerFeatures()).called(1);
-// // verifyNoMoreInteractions(client);
-// // verifyNoMoreInteractions(cachedClient);
-// // verifyNoMoreInteractions(priceAPI);
-// // });
-//
-// // test("initializeWallet new main net wallet", () async {
-// // when(priceAPI?.getbitcoincashPrice(baseCurrency: "USD"))
-// // .thenAnswer((realInvocation) async => Decimal.fromInt(10));
-// // when(client?.ping()).thenAnswer((_) async => true);
-// // 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": []
-// // });
-// // expect(await bch?.initializeWallet(), true);
-// //
-// // final wallet = await Hive.openBox(testWalletId);
-// //
-// // expect(await wallet.get("addressBookEntries"), {});
-// // expect(await wallet.get('notes'), null);
-// // expect(await wallet.get("id"), testWalletId);
-// // expect(await wallet.get("preferredFiatCurrency"), null);
-// // expect(await wallet.get("blocked_tx_hashes"), ["0xdefault"]);
-// //
-// // final changeAddressesP2PKH = await wallet.get("changeAddressesP2PKH");
-// // expect(changeAddressesP2PKH, isA>());
-// // expect(changeAddressesP2PKH.length, 1);
-// // expect(await wallet.get("changeIndexP2PKH"), 0);
-// //
-// // final receivingAddressesP2PKH =
-// // await wallet.get("receivingAddressesP2PKH");
-// // expect(receivingAddressesP2PKH, isA>());
-// // expect(receivingAddressesP2PKH.length, 1);
-// // expect(await wallet.get("receivingIndexP2PKH"), 0);
-// //
-// // final p2pkhReceiveDerivations = jsonDecode(await secureStore?.read(
-// // key: "${testWalletId}_receiveDerivationsP2PKH"));
-// // expect(p2pkhReceiveDerivations.length, 1);
-// //
-// // final p2pkhChangeDerivations = jsonDecode(await secureStore.read(
-// // key: "${testWalletId}_changeDerivationsP2PKH"));
-// // expect(p2pkhChangeDerivations.length, 1);
-// //
-// // expect(secureStore?.interactions, 10);
-// // expect(secureStore?.reads, 7);
-// // expect(secureStore?.writes, 3);
-// // expect(secureStore?.deletes, 0);
-// // verify(client?.ping()).called(1);
-// // verify(client?.getServerFeatures()).called(1);
-// // verifyNoMoreInteractions(client);
-// // verifyNoMoreInteractions(cachedClient);
-// // verifyNoMoreInteractions(priceAPI);
-// // });
-//
-// // // test("initializeWallet existing main net wallet", () async {
-// // // when(priceAPI?.getbitcoincashPrice(baseCurrency: "USD"))
-// // // .thenAnswer((realInvocation) async => Decimal.fromInt(10));
-// // // when(client?.ping()).thenAnswer((_) async => true);
-// // // when(client?.getBatchHistory(args: anyNamed("args")))
-// // // .thenAnswer((_) 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": []
-// // // });
-// // // // init new wallet
-// // // expect(bch?.initializeNew(), true);
-// // //
-// // // // fetch data to compare later
-// // // final newWallet = await Hive.openBox(testWalletId);
-// // //
-// // // final addressBookEntries = await newWallet.get("addressBookEntries");
-// // // final notes = await newWallet.get('notes');
-// // // final wID = await newWallet.get("id");
-// // // final currency = await newWallet.get("preferredFiatCurrency");
-// // // final blockedHashes = await newWallet.get("blocked_tx_hashes");
-// // //
-// // // final changeAddressesP2PKH = await newWallet.get("changeAddressesP2PKH");
-// // // final changeIndexP2PKH = await newWallet.get("changeIndexP2PKH");
-// // //
-// // // final receivingAddressesP2PKH =
-// // // await newWallet.get("receivingAddressesP2PKH");
-// // // final receivingIndexP2PKH = await newWallet.get("receivingIndexP2PKH");
-// // //
-// // // final p2pkhReceiveDerivations = jsonDecode(await secureStore?.read(
-// // // key: "${testWalletId}_receiveDerivationsP2PKH"));
-// // //
-// // // final p2pkhChangeDerivations = jsonDecode(await secureStore?.read(
-// // // key: "${testWalletId}_changeDerivationsP2PKH"));
-// // //
-// // // // exit new wallet
-// // // await bch?.exit();
-// // //
-// // // // open existing/created wallet
-// // // bch = BitcoinCashWallet(
-// // // walletId: testWalletId,
-// // // walletName: testWalletName,
-// // // coin: dtestcoin,
-// // // client: client!,
-// // // cachedClient: cachedClient!,
-// // // priceAPI: priceAPI,
-// // // secureStore: secureStore,
-// // // );
-// // //
-// // // // init existing
-// // // expect(bch?.initializeExisting(), true);
-// // //
-// // // // compare data to ensure state matches state of previously closed wallet
-// // // final wallet = await Hive.openBox(testWalletId);
-// // //
-// // // expect(await wallet.get("addressBookEntries"), addressBookEntries);
-// // // expect(await wallet.get('notes'), notes);
-// // // expect(await wallet.get("id"), wID);
-// // // expect(await wallet.get("preferredFiatCurrency"), currency);
-// // // expect(await wallet.get("blocked_tx_hashes"), blockedHashes);
-// // //
-// // // expect(await wallet.get("changeAddressesP2PKH"), changeAddressesP2PKH);
-// // // expect(await wallet.get("changeIndexP2PKH"), changeIndexP2PKH);
-// // //
-// // // expect(
-// // // await wallet.get("receivingAddressesP2PKH"), receivingAddressesP2PKH);
-// // // expect(await wallet.get("receivingIndexP2PKH"), receivingIndexP2PKH);
-// // //
-// // // expect(
-// // // jsonDecode(await secureStore?.read(
-// // // key: "${testWalletId}_receiveDerivationsP2PKH")),
-// // // p2pkhReceiveDerivations);
-// // //
-// // // expect(
-// // // jsonDecode(await secureStore?.read(
-// // // key: "${testWalletId}_changeDerivationsP2PKH")),
-// // // p2pkhChangeDerivations);
-// // //
-// // // expect(secureStore?.interactions, 12);
-// // // expect(secureStore?.reads, 9);
-// // // expect(secureStore?.writes, 3);
-// // // expect(secureStore?.deletes, 0);
-// // // verify(client?.ping()).called(2);
-// // // verify(client?.getServerFeatures()).called(1);
-// // // verifyNoMoreInteractions(client);
-// // // verifyNoMoreInteractions(cachedClient);
-// // // verifyNoMoreInteractions(priceAPI);
-// // // });
-//
-// test("get current receiving addresses", () async {
-// bch = BitcoinCashWallet(
-// walletId: testWalletId,
-// walletName: testWalletName,
-// coin: bchtestcoin,
-// client: client!,
-// cachedClient: cachedClient!,
-// tracker: tracker!,
-// priceAPI: priceAPI,
-// secureStore: secureStore,
-// );
-// when(client?.ping()).thenAnswer((_) async => true);
-// 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_TESTNET,
-// "hash_function": "sha256",
-// "services": []
-// });
-//
-// await Hive.openBox(testWalletId);
-// await Hive.openBox(DB.boxNamePrefs);
-//
-// await bch?.initializeNew();
-// await bch?.initializeExisting();
-// expect(
-// Address.validateAddress(
-// await bch!.currentReceivingAddress, bitcoincashtestnet),
-// true);
-// expect(
-// Address.validateAddress(
-// await bch!.currentReceivingAddress, bitcoincashtestnet),
-// true);
-// expect(
-// Address.validateAddress(
-// await bch!.currentReceivingAddress, bitcoincashtestnet),
-// true);
-//
-// verifyNever(client?.ping()).called(0);
-// verify(client?.getServerFeatures()).called(1);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("get allOwnAddresses", () async {
-// bch = BitcoinCashWallet(
-// walletId: testWalletId,
-// walletName: testWalletName,
-// coin: bchtestcoin,
-// client: client!,
-// cachedClient: cachedClient!,
-// tracker: tracker!,
-// priceAPI: priceAPI,
-// secureStore: secureStore,
-// );
-// when(client?.ping()).thenAnswer((_) async => true);
-// 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_TESTNET,
-// "hash_function": "sha256",
-// "services": []
-// });
-//
-// await Hive.openBox(testWalletId);
-// await Hive.openBox(DB.boxNamePrefs);
-//
-// await bch?.initializeNew();
-// await bch?.initializeExisting();
-// final addresses = await bch?.allOwnAddresses;
-// expect(addresses, isA>());
-// expect(addresses?.length, 2);
-//
-// for (int i = 0; i < 2; i++) {
-// expect(
-// Address.validateAddress(addresses![i], bitcoincashtestnet), true);
-// }
-//
-// verifyNever(client?.ping()).called(0);
-// verify(client?.getServerFeatures()).called(1);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// // test("get utxos and balances", () async {
-// // bch = BitcoinCashWallet(
-// // walletId: testWalletId,
-// // walletName: testWalletName,
-// // coin: dtestcoin,
-// // client: client!,
-// // cachedClient: cachedClient!,
-// // tracker: tracker!,
-// // priceAPI: priceAPI,
-// // secureStore: secureStore,
-// // );
-// // when(client?.ping()).thenAnswer((_) async => true);
-// // 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_TESTNET,
-// // "hash_function": "sha256",
-// // "services": []
-// // });
-// //
-// // await Hive.openBox(testWalletId);
-// // await Hive.openBox(DB.boxNamePrefs);
-// //
-// // when(client?.getBatchUTXOs(args: anyNamed("args")))
-// // .thenAnswer((_) async => batchGetUTXOResponse0);
-// //
-// // when(client?.estimateFee(blocks: 20))
-// // .thenAnswer((realInvocation) async => Decimal.zero);
-// // when(client?.estimateFee(blocks: 5))
-// // .thenAnswer((realInvocation) async => Decimal.one);
-// // when(client?.estimateFee(blocks: 1))
-// // .thenAnswer((realInvocation) async => Decimal.ten);
-// //
-// // when(cachedClient?.getTransaction(
-// // txHash: tx1.txid,
-// // coin: Coin.bitcoincashTestNet,
-// // )).thenAnswer((_) async => tx1Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash: tx2.txid,
-// // coin: Coin.bitcoincashTestNet,
-// // )).thenAnswer((_) async => tx2Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash: tx3.txid,
-// // coin: Coin.bitcoincashTestNet,
-// // )).thenAnswer((_) async => tx3Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash: tx4.txid,
-// // coin: Coin.bitcoincashTestNet,
-// // )).thenAnswer((_) async => tx4Raw);
-// //
-// // await bch?.initializeNew();
-// // await bch?.initializeExisting();
-// //
-// // final utxoData = await bch?.utxoData;
-// // expect(utxoData, isA());
-// // expect(utxoData.toString(),
-// // r"{totalUserCurrency: $103.2173, satoshiBalance: 1032173000, bitcoinBalance: null, unspentOutputArray: [{txid: 86198a91805b6c53839a6a97736c434a5a2f85d68595905da53df7df59b9f01a, vout: 0, value: 800000000, fiat: $80, blocked: false, status: {confirmed: true, blockHash: e52cabb4445eb9ceb3f4f8d68cc64b1ede8884ce560296c27826a48ecc477370, blockHeight: 4274457, blockTime: 1655755742, confirmations: 100}}, {txid: a4b6bd97a4b01b4305d0cf02e9bac6b7c37cda2f8e9dfe291ce4170b810ed469, vout: 0, value: 72173000, fiat: $7.2173, blocked: false, status: {confirmed: false, blockHash: bd239f922b3ecec299a90e4d1ce389334e8df4b95470fb5919966b0b650bb95b, blockHeight: 4270459, blockTime: 1655500912, confirmations: 0}}, {txid: 68c159dcc2f962cbc61f7dd3c8d0dcc14da8adb443811107115531c853fc0c60, vout: 1, value: 100000000, fiat: $10, blocked: false, status: {confirmed: false, blockHash: 9fee9b9446cfe81abb1a17bec56e6c160d9a6527e5b68b1141a827573bc2649f, blockHeight: 4255659, blockTime: 1654553247, confirmations: 0}}, {txid: 628a78606058ce4036aee3907e042742156c1894d34419578de5671b53ea5800, vout: 0, value: 60000000, fiat: $6, blocked: false, status: {confirmed: true, blockHash: bc461ab43e3a80d9a4d856ee9ff70f41d86b239d5f0581ffd6a5c572889a6b86, blockHeight: 4270352, blockTime: 1652888705, confirmations: 100}}]}");
-// //
-// // final outputs = await bch?.unspentOutputs;
-// // expect(outputs, isA>());
-// // expect(outputs?.length, 4);
-// //
-// // final availableBalance = await bch?.availableBalance;
-// // expect(availableBalance, Decimal.parse("8.6"));
-// //
-// // final totalBalance = await bch?.totalBalance;
-// // expect(totalBalance, Decimal.parse("10.32173"));
-// //
-// // final pendingBalance = await bch?.pendingBalance;
-// // expect(pendingBalance, Decimal.parse("1.72173"));
-// //
-// // final balanceMinusMaxFee = await bch?.balanceMinusMaxFee;
-// // expect(balanceMinusMaxFee, Decimal.parse("7.6"));
-// //
-// // verify(client?.ping()).called(1);
-// // verify(client?.getServerFeatures()).called(1);
-// // verify(client?.estimateFee(blocks: 1)).called(1);
-// // verify(client?.estimateFee(blocks: 5)).called(1);
-// // verify(client?.estimateFee(blocks: 20)).called(1);
-// // verify(client?.getBatchUTXOs(args: anyNamed("args"))).called(1);
-// // verify(cachedClient?.getTransaction(
-// // txHash: tx1.txid,
-// // coin: Coin.bitcoincashTestNet,
-// // )).called(1);
-// // verify(cachedClient?.getTransaction(
-// // txHash: tx2.txid,
-// // coin: Coin.bitcoincashTestNet,
-// // )).called(1);
-// // verify(cachedClient?.getTransaction(
-// // txHash: tx3.txid,
-// // coin: Coin.bitcoincashTestNet,
-// // )).called(1);
-// // verify(cachedClient?.getTransaction(
-// // txHash: tx4.txid,
-// // coin: Coin.bitcoincashTestNet,
-// // )).called(1);
-// //
-// // verifyNoMoreInteractions(client);
-// // verifyNoMoreInteractions(cachedClient);
-// // verifyNoMoreInteractions(priceAPI);
-// // });
-// //
-// // // test("get utxos - multiple batches", () async {
-// // // bch = BitcoinCashWallet(
-// // // walletId: testWalletId,
-// // // walletName: testWalletName,
-// // // coin: dtestcoin,
-// // // client: client!,
-// // // cachedClient: cachedClient!,
-// // // priceAPI: priceAPI,
-// // // secureStore: secureStore,
-// // // );
-// // // when(client?.ping()).thenAnswer((_) async => true);
-// // // 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_TESTNET,
-// // // "hash_function": "sha256",
-// // // "services": []
-// // // });
-// // //
-// // // when(client?.getBatchUTXOs(args: anyNamed("args")))
-// // // .thenAnswer((_) async => {});
-// // //
-// // // when(priceAPI?.getbitcoincashPrice(baseCurrency: "USD"))
-// // // .thenAnswer((realInvocation) async => Decimal.fromInt(10));
-// // //
-// // // await bch?.initializeWallet();
-// // //
-// // // // add some extra addresses to make sure we have more than the single batch size of 10
-// // // final wallet = await Hive.openBox(testWalletId);
-// // // final addresses = await wallet.get("receivingAddressesP2PKH");
-// // // addresses.add("DQaAi9R58GXMpDyhePys6hHCuif4fhc1sN");
-// // // addresses.add("DBVhuF8QgeuxU2pssxzMgJqPhGCx5qyVkD");
-// // // addresses.add("DCAokB2CXXPWC2JPj6jrK6hxANwTF2m21x");
-// // // addresses.add("D6Y9brE3jUGPrqLmSEWh6yQdgY5b7ZkTib");
-// // // addresses.add("DKdtobt3M5b3kQWZf1zRUZn3Ys6JTQwbPL");
-// // // addresses.add("DBYiFr1BRc2zB19p8jxdSu6DvFGTdWvkVF");
-// // // addresses.add("DE5ffowvbHPzzY6aRVGpzxR2QqikXxUKPG");
-// // // addresses.add("DA97TLg1741J2aLK6z9bVZoWysgQbMR45K");
-// // // addresses.add("DGGmf9q4PKcJXauPRstsFetu9DjW1VSBYk");
-// // // addresses.add("D9bXqnTtufcb6oJyuZniCXbst8MMLzHxUd");
-// // // addresses.add("DA6nv8M4kYL4RxxKrcsPaPUA1KrFA7CTfN");
-// // // await wallet.put("receivingAddressesP2PKH", addresses);
-// // //
-// // // final utxoData = await bch?.utxoData;
-// // // expect(utxoData, isA());
-// // //
-// // // final outputs = await bch?.unspentOutputs;
-// // // expect(outputs, isA>());
-// // // expect(outputs?.length, 0);
-// // //
-// // // verify(client?.ping()).called(1);
-// // // verify(client?.getServerFeatures()).called(1);
-// // // verify(client?.getBatchUTXOs(args: anyNamed("args"))).called(2);
-// // // verify(priceAPI?.getbitcoincashPrice(baseCurrency: "USD")).called(1);
-// // //
-// // // verifyNoMoreInteractions(client);
-// // // verifyNoMoreInteractions(cachedClient);
-// // // verifyNoMoreInteractions(priceAPI);
-// // // });
-// //
-// test("get utxos fails", () async {
-// bch = BitcoinCashWallet(
-// walletId: testWalletId,
-// walletName: testWalletName,
-// coin: bchtestcoin,
-// client: client!,
-// cachedClient: cachedClient!,
-// tracker: tracker!,
-// priceAPI: priceAPI,
-// secureStore: secureStore,
-// );
-// when(client?.ping()).thenAnswer((_) async => true);
-// 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_TESTNET,
-// "hash_function": "sha256",
-// "services": []
-// });
-//
-// await Hive.openBox(testWalletId);
-// await Hive.openBox(DB.boxNamePrefs);
-//
-// when(client?.getBatchUTXOs(args: anyNamed("args")))
-// .thenThrow(Exception("some exception"));
-//
-// await bch?.initializeNew();
-// await bch?.initializeExisting();
-//
-// final utxoData = await bch?.utxoData;
-// expect(utxoData, isA());
-// expect(utxoData.toString(),
-// r"{totalUserCurrency: 0.00, satoshiBalance: 0, bitcoinBalance: 0, unspentOutputArray: []}");
-//
-// final outputs = await bch?.unspentOutputs;
-// expect(outputs, isA>());
-// expect(outputs?.length, 0);
-//
-// verifyNever(client?.ping()).called(0);
-// verify(client?.getServerFeatures()).called(1);
-// verify(client?.getBatchUTXOs(args: anyNamed("args"))).called(1);
-//
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("chain height fetch, update, and get", () async {
-// bch = BitcoinCashWallet(
-// walletId: testWalletId,
-// walletName: testWalletName,
-// coin: bchtestcoin,
-// client: client!,
-// cachedClient: cachedClient!,
-// tracker: tracker!,
-// priceAPI: priceAPI,
-// secureStore: secureStore,
-// );
-// when(client?.ping()).thenAnswer((_) async => true);
-// 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_TESTNET,
-// "hash_function": "sha256",
-// "services": []
-// });
-//
-// await Hive.openBox(testWalletId);
-// await Hive.openBox(DB.boxNamePrefs);
-//
-// await bch?.initializeNew();
-// await bch?.initializeExisting();
-//
-// // get stored
-// expect(await bch?.storedChainHeight, 0);
-//
-// // fetch fails
-// when(client?.getBlockHeadTip()).thenThrow(Exception("Some exception"));
-// expect(await bch?.chainHeight, -1);
-//
-// // fetch succeeds
-// when(client?.getBlockHeadTip()).thenAnswer((realInvocation) async => {
-// "height": 100,
-// "hex": "some block hex",
-// });
-// expect(await bch?.chainHeight, 100);
-//
-// // update
-// await bch?.updateStoredChainHeight(newHeight: 1000);
-//
-// // fetch updated
-// expect(await bch?.storedChainHeight, 1000);
-//
-// verifyNever(client?.ping()).called(0);
-// verify(client?.getServerFeatures()).called(1);
-// verify(client?.getBlockHeadTip()).called(2);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("getTxCount succeeds", () async {
-// when(client?.getHistory(
-// scripthash:
-// "1df1cab6d109d506aa424b00b6a013c5e1947dc13b78d62b4d0e9f518b3035d1"))
-// .thenAnswer((realInvocation) async => [
-// {
-// "height": 757727,
-// "tx_hash":
-// "aaac451c49c2e3bcbccb8a9fded22257eeb94c1702b456171aa79250bc1b20e0"
-// },
-// {
-// "height": 0,
-// "tx_hash":
-// "9ac29f35b72ca596bc45362d1f9556b0555e1fb633ca5ac9147a7fd467700afe"
-// }
-// ]);
-//
-// final count =
-// await bch?.getTxCount(address: "1MMi672ueYFXLLdtZqPe4FsrS46gNDyRq1");
-//
-// expect(count, 2);
-//
-// verify(client?.getHistory(
-// scripthash:
-// "1df1cab6d109d506aa424b00b6a013c5e1947dc13b78d62b4d0e9f518b3035d1"))
-// .called(1);
-//
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-// //TODO - Needs refactoring
-// test("getTxCount fails", () async {
-// when(client?.getHistory(
-// scripthash:
-// "64953f7db441a21172de206bf70b920c8c718ed4f03df9a85073c0400be0053c"))
-// .thenThrow(Exception("some exception"));
-//
-// bool didThrow = false;
-// try {
-// await bch?.getTxCount(address: "D6biRASajCy7GcJ8R6ZP4RE94fNRerJLCC");
-// } catch (_) {
-// didThrow = true;
-// }
-// expect(didThrow, true);
-//
-// verifyNever(client?.getHistory(
-// scripthash:
-// "64953f7db441a21172de206bf70b920c8c718ed4f03df9a85073c0400be0053c"))
-// .called(0);
-//
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("_checkCurrentReceivingAddressesForTransactions succeeds", () async {
-// when(client?.ping()).thenAnswer((_) async => true);
-// 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?.getHistory(scripthash: anyNamed("scripthash")))
-// .thenAnswer((realInvocation) async => [
-// {
-// "height": 4270385,
-// "tx_hash":
-// "c07f740ad72c0dd759741f4c9ab4b1586a22bc16545584364ac9b3d845766271"
-// },
-// {
-// "height": 4270459,
-// "tx_hash":
-// "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a"
-// }
-// ]);
-//
-// await Hive.openBox(testWalletId);
-// await Hive.openBox(DB.boxNamePrefs);
-//
-// await bch?.initializeNew();
-// await bch?.initializeExisting();
-//
-// bool didThrow = false;
-// try {
-// await bch?.checkCurrentReceivingAddressesForTransactions();
-// } catch (_) {
-// didThrow = true;
-// }
-// expect(didThrow, false);
-//
-// verify(client?.getHistory(scripthash: anyNamed("scripthash"))).called(2);
-// verify(client?.getServerFeatures()).called(1);
-// verifyNever(client?.ping()).called(0);
-//
-// expect(secureStore?.interactions, 20);
-// expect(secureStore?.reads, 13);
-// expect(secureStore?.writes, 7);
-// expect(secureStore?.deletes, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("_checkCurrentReceivingAddressesForTransactions fails", () async {
-// when(client?.ping()).thenAnswer((_) async => true);
-// 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?.getHistory(scripthash: anyNamed("scripthash")))
-// .thenThrow(Exception("some exception"));
-//
-// await Hive.openBox(testWalletId);
-// await Hive.openBox(DB.boxNamePrefs);
-//
-// await bch?.initializeNew();
-// await bch?.initializeExisting();
-//
-// bool didThrow = false;
-// try {
-// await bch?.checkCurrentReceivingAddressesForTransactions();
-// } catch (_) {
-// didThrow = true;
-// }
-// expect(didThrow, true);
-//
-// verify(client?.getHistory(scripthash: anyNamed("scripthash"))).called(1);
-// verify(client?.getServerFeatures()).called(1);
-// verifyNever(client?.ping()).called(0);
-//
-// expect(secureStore?.interactions, 14);
-// expect(secureStore?.reads, 9);
-// expect(secureStore?.writes, 5);
-// expect(secureStore?.deletes, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("_checkCurrentChangeAddressesForTransactions succeeds", () async {
-// when(client?.ping()).thenAnswer((_) async => true);
-// 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?.getHistory(scripthash: anyNamed("scripthash")))
-// .thenAnswer((realInvocation) async => [
-// {
-// "height": 4286283,
-// "tx_hash":
-// "4c119685401e28982283e644c57d84fde6aab83324012e35c9b49e6efd99b49b"
-// },
-// {
-// "height": 4286295,
-// "tx_hash":
-// "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a"
-// }
-// ]);
-//
-// await Hive.openBox(testWalletId);
-// await Hive.openBox(DB.boxNamePrefs);
-//
-// await bch?.initializeNew();
-// await bch?.initializeExisting();
-//
-// bool didThrow = false;
-// try {
-// await bch?.checkCurrentChangeAddressesForTransactions();
-// } catch (_) {
-// didThrow = true;
-// }
-// expect(didThrow, false);
-//
-// verify(client?.getHistory(scripthash: anyNamed("scripthash"))).called(2);
-// verify(client?.getServerFeatures()).called(1);
-// verifyNever(client?.ping()).called(0);
-//
-// expect(secureStore?.interactions, 20);
-// expect(secureStore?.reads, 13);
-// expect(secureStore?.writes, 7);
-// expect(secureStore?.deletes, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("_checkCurrentChangeAddressesForTransactions fails", () async {
-// when(client?.ping()).thenAnswer((_) async => true);
-// 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?.getHistory(scripthash: anyNamed("scripthash")))
-// .thenThrow(Exception("some exception"));
-//
-// await Hive.openBox(testWalletId);
-// await Hive.openBox(DB.boxNamePrefs);
-//
-// await bch?.initializeNew();
-// await bch?.initializeExisting();
-//
-// bool didThrow = false;
-// try {
-// await bch?.checkCurrentChangeAddressesForTransactions();
-// } catch (_) {
-// didThrow = true;
-// }
-// expect(didThrow, true);
-//
-// verify(client?.getHistory(scripthash: anyNamed("scripthash"))).called(1);
-// verify(client?.getServerFeatures()).called(1);
-// verifyNever(client?.ping()).called(0);
-//
-// expect(secureStore?.interactions, 14);
-// expect(secureStore?.reads, 9);
-// expect(secureStore?.writes, 5);
-// expect(secureStore?.deletes, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// // test("getAllTxsToWatch", () async {
-// // TestWidgetsFlutterBinding.ensureInitialized();
-// // var notifications = {"show": 0};
-// // const MethodChannel('dexterous.com/flutter/local_notifications')
-// // .setMockMethodCallHandler((call) async {
-// // notifications[call.method]++;
-// // });
-// //
-// // bch?.pastUnconfirmedTxs = {
-// // "88b7b5077d940dde1bc63eba37a09dec8e7b9dad14c183a2e879a21b6ec0ac1c",
-// // "b39bac02b65af46a49e2985278fe24ca00dd5d627395d88f53e35568a04e10fa",
-// // };
-// //
-// // await bch?.getAllTxsToWatch(transactionData);
-// // expect(notifications.length, 1);
-// // expect(notifications["show"], 3);
-// //
-// // expect(bch?.unconfirmedTxs, {
-// // "b2f75a017a7435f1b8c2e080a865275d8f80699bba68d8dce99a94606e7b3528",
-// // 'dcca229760b44834478f0b266c9b3f5801e0139fdecacdc0820e447289a006d3',
-// // });
-// //
-// // expect(secureStore?.interactions, 0);
-// // verifyNoMoreInteractions(client);
-// // verifyNoMoreInteractions(cachedClient);
-// // verifyNoMoreInteractions(priceAPI);
-// // });
-// //
-// // test("refreshIfThereIsNewData true A", () async {
-// // when(client?.getTransaction(
-// // txHash:
-// // "a4b6bd97a4b01b4305d0cf02e9bac6b7c37cda2f8e9dfe291ce4170b810ed469",
-// // )).thenAnswer((_) async => tx2Raw);
-// // when(client?.getTransaction(
-// // txHash:
-// // "86198a91805b6c53839a6a97736c434a5a2f85d68595905da53df7df59b9f01a",
-// // )).thenAnswer((_) async => tx1Raw);
-// //
-// // bch = BitcoinCashWallet(
-// // walletId: testWalletId,
-// // walletName: testWalletName,
-// // coin: dtestcoin,
-// // client: client!,
-// // cachedClient: cachedClient!,
-// // priceAPI: priceAPI,
-// // secureStore: secureStore,
-// // );
-// // final wallet = await Hive.openBox(testWalletId);
-// // await wallet.put('receivingAddressesP2PKH', []);
-// //
-// // await wallet.put('changeAddressesP2PKH', []);
-// //
-// // bch?.unconfirmedTxs = {
-// // "a4b6bd97a4b01b4305d0cf02e9bac6b7c37cda2f8e9dfe291ce4170b810ed469",
-// // "86198a91805b6c53839a6a97736c434a5a2f85d68595905da53df7df59b9f01a"
-// // };
-// //
-// // final result = await bch?.refreshIfThereIsNewData();
-// //
-// // expect(result, true);
-// //
-// // verify(client?.getTransaction(
-// // txHash:
-// // "a4b6bd97a4b01b4305d0cf02e9bac6b7c37cda2f8e9dfe291ce4170b810ed469",
-// // )).called(1);
-// // verify(client?.getTransaction(
-// // txHash:
-// // "86198a91805b6c53839a6a97736c434a5a2f85d68595905da53df7df59b9f01a",
-// // )).called(1);
-// //
-// // expect(secureStore?.interactions, 0);
-// // verifyNoMoreInteractions(client);
-// // verifyNoMoreInteractions(cachedClient);
-// // verifyNoMoreInteractions(priceAPI);
-// // });
-// //
-// // test("refreshIfThereIsNewData true B", () async {
-// // // when(priceAPI.getbitcoincashPrice(baseCurrency: "USD"))
-// // // .thenAnswer((_) async => Decimal.fromInt(10));
-// //
-// // when(client?.getBatchHistory(args: anyNamed("args")))
-// // .thenAnswer((realInvocation) async {
-// // final uuids = Map>.from(realInvocation
-// // .namedArguments.values.first as Map)
-// // .keys
-// // .toList(growable: false);
-// // return {
-// // uuids[0]: [
-// // {
-// // "tx_hash":
-// // "351a94874379a5444c8891162472acf66de538a1abc647d4753f3e1eb5ec66f9",
-// // "height": 4286305
-// // },
-// // {
-// // "tx_hash":
-// // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
-// // "height": 4286295
-// // }
-// // ],
-// // uuids[1]: [
-// // {
-// // "tx_hash":
-// // "4c119685401e28982283e644c57d84fde6aab83324012e35c9b49e6efd99b49b",
-// // "height": 4286283
-// // }
-// // ],
-// // };
-// // });
-// //
-// // when(client?.getTransaction(
-// // txHash:
-// // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
-// // )).thenAnswer((_) async => tx2Raw);
-// // when(client?.getTransaction(
-// // txHash:
-// // "4c119685401e28982283e644c57d84fde6aab83324012e35c9b49e6efd99b49b",
-// // )).thenAnswer((_) async => tx1Raw);
-// //
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "351a94874379a5444c8891162472acf66de538a1abc647d4753f3e1eb5ec66f9",
-// // coin: Coin.bitcoincashTestNet,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx3Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "351a94874379a5444c8891162472acf66de538a1abc647d4753f3e1eb5ec66f9",
-// // coin: Coin.bitcoincashTestNet,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx3Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "4c119685401e28982283e644c57d84fde6aab83324012e35c9b49e6efd99b49b",
-// // coin: Coin.bitcoincashTestNet,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx1Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "4493caff0e1b4f248e3c6219e7f288cfdb46c32b72a77aec469098c5f7f5154e",
-// // coin: Coin.bitcoincashTestNet,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx5Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "e095cbe5531d174c3fc5c9c39a0e6ba2769489cdabdc17b35b2e3a33a3c2fc61",
-// // coin: Coin.bitcoincashTestNet,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx6Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "d3054c63fe8cfafcbf67064ec66b9fbe1ac293860b5d6ffaddd39546658b72de",
-// // coin: Coin.bitcoincashTestNet,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx7Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "7b34e60cc37306f866667deb67b14096f4ea2add941fd6e2238a639000642b82",
-// // coin: Coin.bitcoincashTestNet,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx4Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "a70c6f0690fa84712dc6b3d20ee13862fe015a08cf2dc8949c4300d49c3bdeb5",
-// // coin: Coin.bitcoincashTestNet,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx8Raw);
-// //
-// // bch = BitcoinCashWallet(
-// // walletId: testWalletId,
-// // walletName: testWalletName,
-// // coin: dtestcoin,
-// // client: client!,
-// // cachedClient: cachedClient!,
-// // priceAPI: priceAPI,
-// // secureStore: secureStore,
-// // );
-// // final wallet = await Hive.openBox(testWalletId);
-// // await wallet.put('receivingAddressesP2PKH', []);
-// //
-// // await wallet.put('changeAddressesP2PKH', []);
-// //
-// // bch?.unconfirmedTxs = {
-// // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
-// // };
-// //
-// // final result = await bch?.refreshIfThereIsNewData();
-// //
-// // expect(result, true);
-// //
-// // verify(client?.getBatchHistory(args: anyNamed("args"))).called(2);
-// // verify(client?.getTransaction(
-// // txHash:
-// // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
-// // )).called(1);
-// // verify(cachedClient?.getTransaction(
-// // txHash: anyNamed("tx_hash"),
-// // verbose: true,
-// // coin: Coin.bitcoincashTestNet,
-// // callOutSideMainIsolate: false))
-// // .called(9);
-// // // verify(priceAPI?.getbitcoincashPrice(baseCurrency: "USD")).called(1);
-// //
-// // expect(secureStore?.interactions, 0);
-// // verifyNoMoreInteractions(client);
-// // verifyNoMoreInteractions(cachedClient);
-// // verifyNoMoreInteractions(priceAPI);
-// // });
-//
-// // test("refreshIfThereIsNewData false A", () async {
-// // // when(priceAPI.getbitcoincashPrice(baseCurrency: "USD"))
-// // // .thenAnswer((_) async => Decimal.fromInt(10));
-// //
-// // when(client?.getBatchHistory(args: anyNamed("args")))
-// // .thenAnswer((realInvocation) async {
-// // final uuids = Map>.from(realInvocation
-// // .namedArguments.values.first as Map)
-// // .keys
-// // .toList(growable: false);
-// // return {
-// // uuids[0]: [
-// // {
-// // "tx_hash":
-// // "351a94874379a5444c8891162472acf66de538a1abc647d4753f3e1eb5ec66f9",
-// // "height": 4286305
-// // },
-// // {
-// // "tx_hash":
-// // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
-// // "height": 4286295
-// // }
-// // ],
-// // uuids[1]: [
-// // {
-// // "tx_hash":
-// // "4c119685401e28982283e644c57d84fde6aab83324012e35c9b49e6efd99b49b",
-// // "height": 4286283
-// // }
-// // ],
-// // };
-// // });
-// //
-// // when(client?.getTransaction(
-// // txHash:
-// // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
-// // )).thenAnswer((_) async => tx2Raw);
-// // when(client?.getTransaction(
-// // txHash:
-// // "4c119685401e28982283e644c57d84fde6aab83324012e35c9b49e6efd99b49b",
-// // )).thenAnswer((_) async => tx1Raw);
-// //
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "4c119685401e28982283e644c57d84fde6aab83324012e35c9b49e6efd99b49b",
-// // coin: Coin.bitcoincashTestNet,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx1Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
-// // coin: Coin.bitcoincashTestNet,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx2Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "351a94874379a5444c8891162472acf66de538a1abc647d4753f3e1eb5ec66f9",
-// // coin: Coin.bitcoincashTestNet,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx3Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "4493caff0e1b4f248e3c6219e7f288cfdb46c32b72a77aec469098c5f7f5154e",
-// // coin: Coin.bitcoincashTestNet,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx5Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "7b34e60cc37306f866667deb67b14096f4ea2add941fd6e2238a639000642b82",
-// // coin: Coin.bitcoincashTestNet,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx4Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "e095cbe5531d174c3fc5c9c39a0e6ba2769489cdabdc17b35b2e3a33a3c2fc61",
-// // coin: Coin.bitcoincashTestNet,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx6Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "d3054c63fe8cfafcbf67064ec66b9fbe1ac293860b5d6ffaddd39546658b72de",
-// // coin: Coin.bitcoincashTestNet,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx7Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "a70c6f0690fa84712dc6b3d20ee13862fe015a08cf2dc8949c4300d49c3bdeb5",
-// // coin: Coin.bitcoincashTestNet,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx8Raw);
-// //
-// // bch = BitcoinCashWallet(
-// // walletId: testWalletId,
-// // walletName: testWalletName,
-// // coin: dtestcoin,
-// // client: client!,
-// // cachedClient: cachedClient!,
-// // tracker: tracker!,
-// // priceAPI: priceAPI,
-// // secureStore: secureStore,
-// // );
-// // final wallet = await Hive.openBox(testWalletId);
-// // await wallet.put('receivingAddressesP2PKH', []);
-// //
-// // await wallet.put('changeAddressesP2PKH', []);
-// //
-// // bch?.unconfirmedTxs = {
-// // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
-// // "351a94874379a5444c8891162472acf66de538a1abc647d4753f3e1eb5ec66f9"
-// // };
-// //
-// // final result = await bch?.refreshIfThereIsNewData();
-// //
-// // expect(result, false);
-// //
-// // verify(client?.getBatchHistory(args: anyNamed("args"))).called(2);
-// // verify(client?.getTransaction(
-// // txHash:
-// // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
-// // )).called(1);
-// // verify(cachedClient?.getTransaction(
-// // txHash: anyNamed("tx_hash"),
-// // verbose: true,
-// // coin: Coin.bitcoincashTestNet,
-// // callOutSideMainIsolate: false))
-// // .called(15);
-// // // verify(priceAPI.getbitcoincashPrice(baseCurrency: "USD")).called(1);
-// //
-// // expect(secureStore?.interactions, 0);
-// // verifyNoMoreInteractions(client);
-// // verifyNoMoreInteractions(cachedClient);
-// // verifyNoMoreInteractions(priceAPI);
-// // });
-//
-// // // test("refreshIfThereIsNewData false B", () async {
-// // // when(client?.getBatchHistory(args: anyNamed("args")))
-// // // .thenThrow(Exception("some exception"));
-// // //
-// // // when(client?.getTransaction(
-// // // txHash:
-// // // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
-// // // )).thenAnswer((_) async => tx2Raw);
-// // //
-// // // bch = BitcoinCashWallet(
-// // // walletId: testWalletId,
-// // // walletName: testWalletName,
-// // // coin: dtestcoin,
-// // // client: client!,
-// // // cachedClient: cachedClient!,
-// // // tracker: tracker!,
-// // // priceAPI: priceAPI,
-// // // secureStore: secureStore,
-// // // );
-// // // final wallet = await Hive.openBox(testWalletId);
-// // // await wallet.put('receivingAddressesP2PKH', []);
-// // //
-// // // await wallet.put('changeAddressesP2PKH', []);
-// // //
-// // // bch?.unconfirmedTxs = {
-// // // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
-// // // };
-// // //
-// // // final result = await bch?.refreshIfThereIsNewData();
-// // //
-// // // expect(result, false);
-// // //
-// // // verify(client?.getBatchHistory(args: anyNamed("args"))).called(1);
-// // // verify(client?.getTransaction(
-// // // txHash:
-// // // "a4b6bd97a4b01b4305d0cf02e9bac6b7c37cda2f8e9dfe291ce4170b810ed469",
-// // // )).called(1);
-// // //
-// // // expect(secureStore?.interactions, 0);
-// // // verifyNoMoreInteractions(client);
-// // // verifyNoMoreInteractions(cachedClient);
-// // // verifyNoMoreInteractions(priceAPI);
-// // // });
-//
-// test("get mnemonic list", () 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: anyNamed("args")))
-// // .thenAnswer((thing) async {
-// // print(jsonEncode(thing.namedArguments.entries.first.value));
-// // return {};
-// // });
-//
-// when(client?.getBatchHistory(args: historyBatchArgs0))
-// .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);
-//
-// final wallet = await Hive.openBox(testWalletId);
-//
-// // add maxNumberOfIndexesToCheck and height
-// await bch?.recoverFromMnemonic(
-// mnemonic: TEST_MNEMONIC,
-// maxUnusedAddressGap: 2,
-// maxNumberOfIndexesToCheck: 1000,
-// height: 4000);
-//
-// expect(await bch?.mnemonic, TEST_MNEMONIC.split(" "));
-// //
-// 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);
-//
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test(
-// "recoverFromMnemonic using empty seed on mainnet fails due to bad genesis hash match",
-// () 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_TESTNET,
-// "hash_function": "sha256",
-// "services": []
-// });
-//
-// bool hasThrown = false;
-// try {
-// await bch?.recoverFromMnemonic(
-// mnemonic: TEST_MNEMONIC,
-// maxUnusedAddressGap: 2,
-// maxNumberOfIndexesToCheck: 1000,
-// height: 4000);
-// } catch (_) {
-// hasThrown = true;
-// }
-// expect(hasThrown, true);
-//
-// verify(client?.getServerFeatures()).called(1);
-//
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test(
-// "recoverFromMnemonic using empty seed on testnet fails due to bad genesis hash match",
-// () async {
-// bch = BitcoinCashWallet(
-// walletId: testWalletId,
-// walletName: testWalletName,
-// coin: Coin.bitcoincashTestnet,
-// client: client!,
-// cachedClient: cachedClient!,
-// tracker: tracker!,
-// priceAPI: priceAPI,
-// secureStore: secureStore,
-// );
-// 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": []
-// });
-//
-// bool hasThrown = false;
-// try {
-// await bch?.recoverFromMnemonic(
-// mnemonic: TEST_MNEMONIC,
-// maxUnusedAddressGap: 2,
-// maxNumberOfIndexesToCheck: 1000,
-// height: 4000);
-// } catch (_) {
-// hasThrown = true;
-// }
-// expect(hasThrown, true);
-//
-// verify(client?.getServerFeatures()).called(1);
-//
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test(
-// "recoverFromMnemonic using empty seed on mainnet fails due to attempted overwrite of mnemonic",
-// () 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": []
-// });
-//
-// await secureStore?.write(
-// key: "${testWalletId}_mnemonic", value: "some mnemonic words");
-//
-// bool hasThrown = false;
-// try {
-// await bch?.recoverFromMnemonic(
-// mnemonic: TEST_MNEMONIC,
-// maxUnusedAddressGap: 2,
-// maxNumberOfIndexesToCheck: 1000,
-// height: 4000);
-// } catch (_) {
-// hasThrown = true;
-// }
-// expect(hasThrown, true);
-//
-// verify(client?.getServerFeatures()).called(1);
-//
-// expect(secureStore?.interactions, 2);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("recoverFromMnemonic using non empty seed on mainnet 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(client?.getBatchHistory(args: historyBatchArgs2))
-// .thenAnswer((_) async => historyBatchResponse);
-// when(client?.getBatchHistory(args: historyBatchArgs3))
-// .thenAnswer((_) async => historyBatchResponse);
-//
-// List 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(testWalletId);
-// await Hive.openBox(testWalletId);
-//
-// bool hasThrown = false;
-// try {
-// await bch?.recoverFromMnemonic(
-// mnemonic: TEST_MNEMONIC,
-// maxUnusedAddressGap: 2,
-// maxNumberOfIndexesToCheck: 1000,
-// height: 4000);
-// } catch (_) {
-// hasThrown = true;
-// }
-// expect(hasThrown, false);
-//
-// 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);
-//
-// for (final arg in dynamicArgValues) {
-// final map = Map>.from(arg as Map);
-//
-// verify(client?.getBatchHistory(args: map)).called(1);
-// expect(activeScriptHashes.contains(map.values.first.first as String),
-// true);
-// }
-//
-// expect(secureStore?.interactions, 10);
-// expect(secureStore?.writes, 5);
-// expect(secureStore?.reads, 5);
-// expect(secureStore?.deletes, 0);
-//
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// 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(client?.getBatchHistory(args: historyBatchArgs2))
-// .thenAnswer((_) async => historyBatchResponse);
-// when(client?.getBatchHistory(args: historyBatchArgs3))
-// .thenAnswer((_) async => historyBatchResponse);
-// when(cachedClient?.clearSharedTransactionCache(coin: Coin.bitcoincash))
-// .thenAnswer((realInvocation) async {});
-//
-// when(client?.getBatchHistory(args: {
-// "0": [
-// "04818da846fe5e03ac993d2e0c1ccc3848ff6073c3aba6a572df4efc5432ae8b"
-// ]
-// })).thenAnswer((_) async => {"0": []});
-// when(client?.getBatchHistory(args: {
-// "0": [
-// "f0c86f888f2aca0efaf1705247dbd1ebc02347c183e197310c9062ea2c9d2e34"
-// ]
-// })).thenAnswer((_) async => {"0": []});
-// when(client?.getBatchHistory(args: {
-// "0": [
-// "ff7f0d2a4b8e2805706ece77f4e672550fe4c505a150c781639814338eda1734"
-// ]
-// })).thenAnswer((_) async => {"0": []});
-// when(client?.getBatchHistory(args: {
-// "0": [
-// "1c2336c32dc62f00862ee6a75643e01017c86edece10b5a9d7defbd5f66b0a80"
-// ]
-// })).thenAnswer((_) async => {"0": []});
-//
-// final wallet = await Hive.openBox(testWalletId);
-//
-// // restore so we have something to rescan
-// await bch?.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 preReceivingAddressesP2SH =
-// await wallet.get('receivingAddressesP2SH');
-// final preChangeAddressesP2SH = await wallet.get('changeAddressesP2SH');
-// final preReceivingIndexP2SH = await wallet.get('receivingIndexP2PKH');
-// final preChangeIndexP2SH = await wallet.get('changeIndexP2SH');
-//
-// final preUtxoData = await wallet.get('latest_utxo_model');
-// final preReceiveDerivationsStringP2PKH = await secureStore?.read(
-// key: "${testWalletId}_receiveDerivationsP2PKH");
-// final preChangeDerivationsStringP2PKH = await secureStore?.read(
-// key: "${testWalletId}_changeDerivationsP2PKH");
-//
-// final preReceiveDerivationsStringP2SH = await secureStore?.read(
-// key: "${testWalletId}_receiveDerivationsP2SH");
-// final preChangeDerivationsStringP2SH =
-// await secureStore?.read(key: "${testWalletId}_changeDerivationsP2SH");
-//
-// // 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(
-// 'receivingAddressesP2SH', ["some address", "some other address"]);
-// await wallet
-// .put('changeAddressesP2SH', ["some address", "some other address"]);
-//
-// await wallet.put('receivingIndexP2PKH', 123);
-// await wallet.put('changeIndexP2PKH', 123);
-//
-// await wallet.put('receivingIndexP2SH', 123);
-// await wallet.put('changeIndexP2SH', 123);
-//
-// await secureStore?.write(
-// key: "${testWalletId}_receiveDerivationsP2PKH", value: "{}");
-// await secureStore?.write(
-// key: "${testWalletId}_changeDerivationsP2PKH", value: "{}");
-//
-// await secureStore?.write(
-// key: "${testWalletId}_receiveDerivationsP2SH", value: "{}");
-// await secureStore?.write(
-// key: "${testWalletId}_changeDerivationsP2SH", value: "{}");
-//
-// bool hasThrown = false;
-// try {
-// await bch?.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 receivingAddressesP2SH = await wallet.get('receivingAddressesP2SH');
-// final changeAddressesP2SH = await wallet.get('changeAddressesP2SH');
-// final receivingIndexP2SH = await wallet.get('receivingIndexP2SH');
-// final changeIndexP2SH = await wallet.get('changeIndexP2SH');
-//
-// final utxoData = await wallet.get('latest_utxo_model');
-// final receiveDerivationsStringP2PKH = await secureStore?.read(
-// key: "${testWalletId}_receiveDerivationsP2PKH");
-// final changeDerivationsStringP2PKH = await secureStore?.read(
-// key: "${testWalletId}_changeDerivationsP2PKH");
-//
-// final receiveDerivationsStringP2SH = await secureStore?.read(
-// key: "${testWalletId}_receiveDerivationsP2SH");
-// final changeDerivationsStringP2SH =
-// await secureStore?.read(key: "${testWalletId}_changeDerivationsP2SH");
-//
-// expect(preReceivingAddressesP2PKH, receivingAddressesP2PKH);
-// expect(preChangeAddressesP2PKH, changeAddressesP2PKH);
-// expect(preReceivingIndexP2PKH, receivingIndexP2PKH);
-// expect(preChangeIndexP2PKH, changeIndexP2PKH);
-//
-// expect(preReceivingAddressesP2SH, receivingAddressesP2SH);
-// expect(preChangeAddressesP2SH, changeAddressesP2SH);
-// expect(preReceivingIndexP2SH, receivingIndexP2SH);
-// expect(preChangeIndexP2SH, changeIndexP2SH);
-//
-// expect(preUtxoData, utxoData);
-//
-// expect(preReceiveDerivationsStringP2PKH, receiveDerivationsStringP2PKH);
-// expect(preChangeDerivationsStringP2PKH, changeDerivationsStringP2PKH);
-//
-// expect(preReceiveDerivationsStringP2SH, receiveDerivationsStringP2SH);
-// expect(preChangeDerivationsStringP2SH, changeDerivationsStringP2SH);
-//
-// 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(cachedClient?.clearSharedTransactionCache(coin: Coin.bitcoincash))
-// .called(1);
-//
-// verify(client?.getBatchHistory(args: {
-// "0": [
-// "04818da846fe5e03ac993d2e0c1ccc3848ff6073c3aba6a572df4efc5432ae8b"
-// ]
-// })).called(2);
-// verify(client?.getBatchHistory(args: {
-// "0": [
-// "f0c86f888f2aca0efaf1705247dbd1ebc02347c183e197310c9062ea2c9d2e34"
-// ]
-// })).called(2);
-// verify(client?.getBatchHistory(args: {
-// "0": [
-// "ff7f0d2a4b8e2805706ece77f4e672550fe4c505a150c781639814338eda1734"
-// ]
-// })).called(2);
-// verify(client?.getBatchHistory(args: {
-// "0": [
-// "1c2336c32dc62f00862ee6a75643e01017c86edece10b5a9d7defbd5f66b0a80"
-// ]
-// })).called(2);
-//
-// expect(secureStore?.writes, 17);
-// expect(secureStore?.reads, 22);
-// expect(secureStore?.deletes, 4);
-//
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("fullRescan fails", () 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(client?.getBatchHistory(args: historyBatchArgs2))
-// .thenAnswer((_) async => historyBatchResponse);
-// when(client?.getBatchHistory(args: historyBatchArgs3))
-// .thenAnswer((_) async => historyBatchResponse);
-// when(cachedClient?.clearSharedTransactionCache(coin: Coin.bitcoincash))
-// .thenAnswer((realInvocation) async {});
-//
-// when(client?.getBatchHistory(args: {
-// "0": [
-// "04818da846fe5e03ac993d2e0c1ccc3848ff6073c3aba6a572df4efc5432ae8b"
-// ]
-// })).thenAnswer((realInvocation) async => {"0": []});
-// when(client?.getBatchHistory(args: {
-// "0": [
-// "f0c86f888f2aca0efaf1705247dbd1ebc02347c183e197310c9062ea2c9d2e34"
-// ]
-// })).thenAnswer((_) async => {"0": []});
-// when(client?.getBatchHistory(args: {
-// "0": [
-// "ff7f0d2a4b8e2805706ece77f4e672550fe4c505a150c781639814338eda1734"
-// ]
-// })).thenAnswer((_) async => {"0": []});
-// when(client?.getBatchHistory(args: {
-// "0": [
-// "1c2336c32dc62f00862ee6a75643e01017c86edece10b5a9d7defbd5f66b0a80"
-// ]
-// })).thenAnswer((_) async => {"0": []});
-//
-// final wallet = await Hive.openBox(testWalletId);
-//
-// // restore so we have something to rescan
-// await bch?.recoverFromMnemonic(
-// mnemonic: TEST_MNEMONIC,
-// maxUnusedAddressGap: 2,
-// maxNumberOfIndexesToCheck: 1000,
-// height: 4000);
-//
-// // fetch 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");
-//
-// when(client?.getBatchHistory(args: historyBatchArgs0))
-// .thenThrow(Exception("fake exception"));
-//
-// bool hasThrown = false;
-// try {
-// await bch?.fullRescan(2, 1000);
-// } catch (_) {
-// hasThrown = true;
-// }
-// expect(hasThrown, true);
-//
-// // 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(client?.getBatchHistory(args: historyBatchArgs2)).called(2);
-// verify(client?.getBatchHistory(args: historyBatchArgs3)).called(2);
-// verify(cachedClient?.clearSharedTransactionCache(coin: Coin.bitcoincash))
-// .called(1);
-//
-// verify(client?.getBatchHistory(args: {
-// "0": [
-// "04818da846fe5e03ac993d2e0c1ccc3848ff6073c3aba6a572df4efc5432ae8b"
-// ]
-// })).called(1);
-// verify(client?.getBatchHistory(args: {
-// "0": [
-// "f0c86f888f2aca0efaf1705247dbd1ebc02347c183e197310c9062ea2c9d2e34"
-// ]
-// })).called(2);
-// verify(client?.getBatchHistory(args: {
-// "0": [
-// "ff7f0d2a4b8e2805706ece77f4e672550fe4c505a150c781639814338eda1734"
-// ]
-// })).called(2);
-// verify(client?.getBatchHistory(args: {
-// "0": [
-// "1c2336c32dc62f00862ee6a75643e01017c86edece10b5a9d7defbd5f66b0a80"
-// ]
-// })).called(2);
-//
-// expect(secureStore?.writes, 13);
-// expect(secureStore?.reads, 18);
-// expect(secureStore?.deletes, 8);
-// });
-//
-// // // test("fetchBuildTxData 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.getTransaction(
-// // // tx_hash:
-// // // "339dac760e4c9c81ed30a7fde7062785cb20712b18e108accdc39800f884fda9",
-// // // coinName: "bitcoincash",
-// // // callOutSideMainIsolate: false))
-// // // .thenAnswer((_) async => tx9Raw);
-// // // when(cachedClient.getTransaction(
-// // // tx_hash:
-// // // "c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e",
-// // // coinName: "bitcoincash",
-// // // callOutSideMainIsolate: false))
-// // // .thenAnswer((_) async => tx10Raw);
-// // // when(cachedClient.getTransaction(
-// // // tx_hash:
-// // // "d0c451513bee7d96cb88824d9d720e6b5b90073721b4985b439687f894c3989c",
-// // // coinName: "bitcoincash",
-// // // callOutSideMainIsolate: false))
-// // // .thenAnswer((_) async => tx11Raw);
-// // //
-// // // // recover to fill data
-// // // await bch.recoverFromMnemonic(
-// // // mnemonic: TEST_MNEMONIC,
-// // // maxUnusedAddressGap: 2,
-// // // maxNumberOfIndexesToCheck: 1000,
-// // // height: 4000);
-// // //
-// // // // modify addresses to trigger all change code branches
-// // // final chg44 =
-// // // await secureStore.read(key: testWalletId + "_changeDerivationsP2PKH");
-// // // await secureStore.write(
-// // // key: testWalletId + "_changeDerivationsP2PKH",
-// // // value: chg44.replaceFirst("1vFHF5q21GccoBwrB4zEUAs9i3Bfx797U",
-// // // "D5cQWPnhM3RRJVDz8wWC5jWt3PRCfg1zA6"));
-// // //
-// // // final data = await bch.fetchBuildTxData(utxoList);
-// // //
-// // // expect(data.length, 3);
-// // // expect(
-// // // data["339dac760e4c9c81ed30a7fde7062785cb20712b18e108accdc39800f884fda9"]
-// // // .length,
-// // // 2);
-// // // expect(
-// // // data["c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e"]
-// // // .length,
-// // // 3);
-// // // expect(
-// // // data["d0c451513bee7d96cb88824d9d720e6b5b90073721b4985b439687f894c3989c"]
-// // // .length,
-// // // 2);
-// // // expect(
-// // // data["339dac760e4c9c81ed30a7fde7062785cb20712b18e108accdc39800f884fda9"]
-// // // ["output"],
-// // // isA());
-// // // expect(
-// // // data["c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e"]
-// // // ["output"],
-// // // isA());
-// // // expect(
-// // // data["d0c451513bee7d96cb88824d9d720e6b5b90073721b4985b439687f894c3989c"]
-// // // ["output"],
-// // // isA());
-// // // expect(
-// // // data["339dac760e4c9c81ed30a7fde7062785cb20712b18e108accdc39800f884fda9"]
-// // // ["keyPair"],
-// // // isA());
-// // // expect(
-// // // data["c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e"]
-// // // ["keyPair"],
-// // // isA());
-// // // expect(
-// // // data["d0c451513bee7d96cb88824d9d720e6b5b90073721b4985b439687f894c3989c"]
-// // // ["keyPair"],
-// // // isA());
-// // // expect(
-// // // data["c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e"]
-// // // ["redeemScript"],
-// // // isA());
-// // //
-// // // // modify addresses to trigger all receiving code branches
-// // // final rcv44 = await secureStore.read(
-// // // key: testWalletId + "_receiveDerivationsP2PKH");
-// // // await secureStore.write(
-// // // key: testWalletId + "_receiveDerivationsP2PKH",
-// // // value: rcv44.replaceFirst("1RMSPixoLPuaXuhR2v4HsUMcRjLncKDaw",
-// // // "D5cQWPnhM3RRJVDz8wWC5jWt3PRCfg1zA6"));
-// // //
-// // // final data2 = await bch.fetchBuildTxData(utxoList);
-// // //
-// // // expect(data2.length, 3);
-// // // expect(
-// // // data2["339dac760e4c9c81ed30a7fde7062785cb20712b18e108accdc39800f884fda9"]
-// // // .length,
-// // // 2);
-// // // expect(
-// // // data2["c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e"]
-// // // .length,
-// // // 3);
-// // // expect(
-// // // data2["d0c451513bee7d96cb88824d9d720e6b5b90073721b4985b439687f894c3989c"]
-// // // .length,
-// // // 2);
-// // // expect(
-// // // data2["339dac760e4c9c81ed30a7fde7062785cb20712b18e108accdc39800f884fda9"]
-// // // ["output"],
-// // // isA());
-// // // expect(
-// // // data2["c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e"]
-// // // ["output"],
-// // // isA());
-// // // expect(
-// // // data2["d0c451513bee7d96cb88824d9d720e6b5b90073721b4985b439687f894c3989c"]
-// // // ["output"],
-// // // isA());
-// // // expect(
-// // // data2["339dac760e4c9c81ed30a7fde7062785cb20712b18e108accdc39800f884fda9"]
-// // // ["keyPair"],
-// // // isA());
-// // // expect(
-// // // data2["c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e"]
-// // // ["keyPair"],
-// // // isA());
-// // // expect(
-// // // data2["d0c451513bee7d96cb88824d9d720e6b5b90073721b4985b439687f894c3989c"]
-// // // ["keyPair"],
-// // // isA());
-// // // expect(
-// // // data2["c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e"]
-// // // ["redeemScript"],
-// // // isA());
-// // //
-// // // verify(client.getServerFeatures()).called(1);
-// // // verify(cachedClient.getTransaction(
-// // // tx_hash:
-// // // "339dac760e4c9c81ed30a7fde7062785cb20712b18e108accdc39800f884fda9",
-// // // coinName: "bitcoincash",
-// // // callOutSideMainIsolate: false))
-// // // .called(2);
-// // // verify(cachedClient.getTransaction(
-// // // tx_hash:
-// // // "c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e",
-// // // coinName: "bitcoincash",
-// // // callOutSideMainIsolate: false))
-// // // .called(2);
-// // // verify(cachedClient.getTransaction(
-// // // tx_hash:
-// // // "d0c451513bee7d96cb88824d9d720e6b5b90073721b4985b439687f894c3989c",
-// // // coinName: "bitcoincash",
-// // // callOutSideMainIsolate: false))
-// // // .called(2);
-// // // verify(client.getBatchHistory(args: historyBatchArgs0)).called(1);
-// // // verify(client.getBatchHistory(args: historyBatchArgs1)).called(1);
-// // //
-// // // expect(secureStore.interactions, 38);
-// // // expect(secureStore.writes, 13);
-// // // expect(secureStore.reads, 25);
-// // // expect(secureStore.deletes, 0);
-// // //
-// // // verifyNoMoreInteractions(client);
-// // // verifyNoMoreInteractions(cachedClient);
-// // // verifyNoMoreInteractions(priceAPI);
-// // // });
-//
-// // test("fetchBuildTxData throws", () 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?.getTransaction(
-// // txHash:
-// // "2087ce09bc316877c9f10971526a2bffa3078d52ea31752639305cdcd8230703",
-// // coin: Coin.bitcoincash,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx9Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "ed32c967a0e86d51669ac21c2bb9bc9c50f0f55fbacdd8db21d0a986fba93bd7",
-// // coin: Coin.bitcoincash,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx10Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "3f0032f89ac44b281b50314cff3874c969c922839dddab77ced54e86a21c3fd4",
-// // coin: Coin.bitcoincash,
-// // callOutSideMainIsolate: false))
-// // .thenThrow(Exception("some exception"));
-// //
-// // // recover to fill data
-// // await bch?.recoverFromMnemonic(
-// // mnemonic: TEST_MNEMONIC,
-// // maxUnusedAddressGap: 2,
-// // maxNumberOfIndexesToCheck: 1000,
-// // height: 4000);
-// //
-// // bool didThrow = false;
-// // try {
-// // await bch?.fetchBuildTxData(utxoList);
-// // } catch (_) {
-// // didThrow = true;
-// // }
-// // expect(didThrow, true);
-// //
-// // verify(client?.getServerFeatures()).called(1);
-// // verify(cachedClient?.getTransaction(
-// // txHash:
-// // "2087ce09bc316877c9f10971526a2bffa3078d52ea31752639305cdcd8230703",
-// // coin: Coin.bitcoincash,
-// // callOutSideMainIsolate: false))
-// // .called(1);
-// // verify(cachedClient?.getTransaction(
-// // txHash:
-// // "ed32c967a0e86d51669ac21c2bb9bc9c50f0f55fbacdd8db21d0a986fba93bd7",
-// // coin: Coin.bitcoincash,
-// // callOutSideMainIsolate: false))
-// // .called(1);
-// // verify(cachedClient?.getTransaction(
-// // txHash:
-// // "3f0032f89ac44b281b50314cff3874c969c922839dddab77ced54e86a21c3fd4",
-// // coin: Coin.bitcoincash,
-// // callOutSideMainIsolate: false))
-// // .called(1);
-// // verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1);
-// // verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1);
-// //
-// // expect(secureStore?.interactions, 14);
-// // expect(secureStore?.writes, 7);
-// // expect(secureStore?.reads, 7);
-// // expect(secureStore?.deletes, 0);
-// //
-// // verifyNoMoreInteractions(client);
-// // verifyNoMoreInteractions(cachedClient);
-// // verifyNoMoreInteractions(priceAPI);
-// // });
-//
-// // test("build transaction 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?.getTransaction(
-// // txHash:
-// // "e9673acb3bfa928f92a7d5a545151a672e9613fdf972f3849e16094c1ed28268",
-// // coin: Coin.bitcoincash,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx9Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "fa5bfa4eb581bedb28ca96a65ee77d8e81159914b70d5b7e215994221cc02a63",
-// // coin: Coin.bitcoincash,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx10Raw);
-// // when(cachedClient?.getTransaction(
-// // txHash:
-// // "694617f0000499be2f6af5f8d1ddbcf1a70ad4710c0cee6f33a13a64bba454ed",
-// // coin: Coin.bitcoincash,
-// // callOutSideMainIsolate: false))
-// // .thenAnswer((_) async => tx11Raw);
-// //
-// // // recover to fill data
-// // await bch?.recoverFromMnemonic(
-// // mnemonic: TEST_MNEMONIC,
-// // maxUnusedAddressGap: 2,
-// // maxNumberOfIndexesToCheck: 1000,
-// // height: 4000);
-// //
-// // // modify addresses to properly mock data to build a tx
-// // final rcv44 = await secureStore?.read(
-// // key: testWalletId + "_receiveDerivationsP2PKH");
-// // await secureStore?.write(
-// // key: testWalletId + "_receiveDerivationsP2PKH",
-// // value: rcv44?.replaceFirst("1RMSPixoLPuaXuhR2v4HsUMcRjLncKDaw",
-// // "D5cQWPnhM3RRJVDz8wWC5jWt3PRCfg1zA6"));
-// //
-// // final data = await bch?.fetchBuildTxData(utxoList);
-// //
-// // final txData = await bch?.buildTransaction(
-// // utxosToUse: utxoList,
-// // utxoSigningData: data!,
-// // recipients: ["DS7cKFKdfbarMrYjFBQqEcHR5km6D51c74"],
-// // satoshiAmounts: [13000]);
-// //
-// // expect(txData?.length, 2);
-// // expect(txData?["hex"], isA());
-// // expect(txData?["vSize"], isA());
-// //
-// // verify(client?.getServerFeatures()).called(1);
-// // verify(cachedClient?.getTransaction(
-// // txHash:
-// // "d3054c63fe8cfafcbf67064ec66b9fbe1ac293860b5d6ffaddd39546658b72de",
-// // coin: Coin.bitcoincash,
-// // callOutSideMainIsolate: false))
-// // .called(1);
-// // verify(cachedClient?.getTransaction(
-// // txHash:
-// // "fa5bfa4eb581bedb28ca96a65ee77d8e81159914b70d5b7e215994221cc02a63",
-// // coin: Coin.bitcoincash,
-// // callOutSideMainIsolate: false))
-// // .called(1);
-// // verify(cachedClient?.getTransaction(
-// // txHash:
-// // "694617f0000499be2f6af5f8d1ddbcf1a70ad4710c0cee6f33a13a64bba454ed",
-// // coin: Coin.bitcoincash,
-// // callOutSideMainIsolate: false))
-// // .called(1);
-// // verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1);
-// // verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1);
-// //
-// // expect(secureStore?.interactions, 26);
-// // expect(secureStore?.writes, 10);
-// // expect(secureStore?.reads, 16);
-// // expect(secureStore?.deletes, 0);
-// //
-// // verifyNoMoreInteractions(client);
-// // verifyNoMoreInteractions(cachedClient);
-// // verifyNoMoreInteractions(priceAPI);
-// // });
-//
-// test("confirmSend error 1", () async {
-// bool didThrow = false;
-// try {
-// await bch?.confirmSend(txData: 1);
-// } catch (_) {
-// didThrow = true;
-// }
-//
-// expect(didThrow, true);
-//
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("confirmSend error 2", () async {
-// bool didThrow = false;
-// try {
-// await bch?.confirmSend(txData: 2);
-// } catch (_) {
-// didThrow = true;
-// }
-//
-// expect(didThrow, true);
-//
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("confirmSend some other error code", () async {
-// bool didThrow = false;
-// try {
-// await bch?.confirmSend(txData: 42);
-// } catch (_) {
-// didThrow = true;
-// }
-//
-// expect(didThrow, true);
-//
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("confirmSend no hex", () async {
-// bool didThrow = false;
-// try {
-// await bch?.confirmSend(txData: {"some": "strange map"});
-// } catch (_) {
-// didThrow = true;
-// }
-//
-// expect(didThrow, true);
-//
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("confirmSend fails due to vSize being greater than fee", () async {
-// bool didThrow = false;
-// try {
-// await bch
-// ?.confirmSend(txData: {"hex": "a string", "fee": 1, "vSize": 10});
-// } catch (_) {
-// didThrow = true;
-// }
-//
-// expect(didThrow, true);
-//
-// verify(client?.broadcastTransaction(
-// rawTx: "a string", requestID: anyNamed("requestID")))
-// .called(1);
-//
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("confirmSend fails when broadcast transactions throws", () async {
-// when(client?.broadcastTransaction(
-// rawTx: "a string", requestID: anyNamed("requestID")))
-// .thenThrow(Exception("some exception"));
-//
-// bool didThrow = false;
-// try {
-// await bch
-// ?.confirmSend(txData: {"hex": "a string", "fee": 10, "vSize": 10});
-// } catch (_) {
-// didThrow = true;
-// }
-//
-// expect(didThrow, true);
-//
-// verify(client?.broadcastTransaction(
-// rawTx: "a string", requestID: anyNamed("requestID")))
-// .called(1);
-//
-// expect(secureStore?.interactions, 0);
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("refresh wallet mutex locked", () 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(client?.getBatchHistory(args: historyBatchArgs2))
-// .thenAnswer((_) async => historyBatchResponse);
-// when(client?.getBatchHistory(args: historyBatchArgs3))
-// .thenAnswer((_) async => historyBatchResponse);
-// when(client?.getBatchHistory(args: {
-// "0": [
-// "f0c86f888f2aca0efaf1705247dbd1ebc02347c183e197310c9062ea2c9d2e34"
-// ]
-// })).thenAnswer((realInvocation) async => {"0": []});
-// when(client?.getBatchHistory(args: {
-// "0": [
-// "04818da846fe5e03ac993d2e0c1ccc3848ff6073c3aba6a572df4efc5432ae8b"
-// ]
-// })).thenAnswer((realInvocation) async => {"0": []});
-// when(client?.getBatchHistory(args: {
-// "0": [
-// "ff7f0d2a4b8e2805706ece77f4e672550fe4c505a150c781639814338eda1734"
-// ]
-// })).thenAnswer((realInvocation) async => {"0": []});
-// when(client?.getBatchHistory(args: {
-// "0": [
-// "1c2336c32dc62f00862ee6a75643e01017c86edece10b5a9d7defbd5f66b0a80"
-// ]
-// })).thenAnswer((realInvocation) async => {"0": []});
-//
-// final wallet = await Hive.openBox(testWalletId);
-// // recover to fill data
-// await bch?.recoverFromMnemonic(
-// mnemonic: TEST_MNEMONIC,
-// maxUnusedAddressGap: 2,
-// maxNumberOfIndexesToCheck: 1000,
-// height: 4000);
-//
-// bch?.refreshMutex = true;
-//
-// await bch?.refresh();
-//
-// 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: {
-// "0": [
-// "f0c86f888f2aca0efaf1705247dbd1ebc02347c183e197310c9062ea2c9d2e34"
-// ]
-// })).called(1);
-// verify(client?.getBatchHistory(args: {
-// "0": [
-// "04818da846fe5e03ac993d2e0c1ccc3848ff6073c3aba6a572df4efc5432ae8b"
-// ]
-// })).called(1);
-// verify(client?.getBatchHistory(args: {
-// "0": [
-// "ff7f0d2a4b8e2805706ece77f4e672550fe4c505a150c781639814338eda1734"
-// ]
-// })).called(1);
-// verify(client?.getBatchHistory(args: {
-// "0": [
-// "1c2336c32dc62f00862ee6a75643e01017c86edece10b5a9d7defbd5f66b0a80"
-// ]
-// })).called(1);
-//
-// expect(secureStore?.interactions, 10);
-// expect(secureStore?.writes, 5);
-// expect(secureStore?.reads, 5);
-// expect(secureStore?.deletes, 0);
-//
-// verifyNoMoreInteractions(client);
-// verifyNoMoreInteractions(cachedClient);
-// verifyNoMoreInteractions(tracker);
-// verifyNoMoreInteractions(priceAPI);
-// });
-//
-// test("refresh wallet throws", () async {
-// when(client?.getBlockHeadTip()).thenThrow(Exception("some exception"));
-// 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?.getBatchHistory(args: historyBatchArgs2))
-// .thenAnswer((_) async => historyBatchResponse);
-// when(client?.getBatchHistory(args: historyBatchArgs3))
-// .thenAnswer((_) async => historyBatchResponse);
-//
-// when(client?.getBatchHistory(args: {
-// "0": [
-// "04818da846fe5e03ac993d2e0c1ccc3848ff6073c3aba6a572df4efc5432ae8b"
-// ]
-// })).thenAnswer((realInvocation) async => {"0": []});
-// when(client?.getBatchHistory(args: {
-// "0": [
-// "f0c86f888f2aca0efaf1705247dbd1ebc02347c183e197310c9062ea2c9d2e34"
-// ]
-// })).thenAnswer((realInvocation) async => {"0": []});
-// when(client?.getBatchHistory(args: {
-// "0": [
-// "ff7f0d2a4b8e2805706ece77f4e672550fe4c505a150c781639814338eda1734"
-// ]
-// })).thenAnswer((realInvocation) async => {"0": []});
-// when(client?.getBatchHistory(args: {
-// "0": [
-// "1c2336c32dc62f00862ee6a75643e01017c86edece10b5a9d7defbd5f66b0a80"
-// ]
-// })).thenAnswer((realInvocation) async => {"0": []});
-//
-// when(client?.getHistory(scripthash: anyNamed("scripthash")))
-// .thenThrow(Exception("some exception"));
-//
-// final wallet = await Hive.openBox(testWalletId);
-//
-// // recover to fill data
-// await bch?.recoverFromMnemonic(
-// mnemonic: TEST_MNEMONIC,
-// maxUnusedAddressGap: 2,
-// maxNumberOfIndexesToCheck: 1000,
-// height: 4000);
-//
-// await bch?.refresh();
-//
-// 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: {
-// "0": [
-// "04818da846fe5e03ac993d2e0c1ccc3848ff6073c3aba6a572df4efc5432ae8b"
-// ]
-// })).called(1);
-// verify(client?.getBatchHistory(args: {
-// "0": [
-// "f0c86f888f2aca0efaf1705247dbd1ebc02347c183e197310c9062ea2c9d2e34"
-// ]
-// })).called(1);
-// verify(client?.getBatchHistory(args: {
-// "0": [
-// "ff7f0d2a4b8e2805706ece77f4e672550fe4c505a150c781639814338eda1734"
-// ]
-// })).called(1);
-// verify(client?.getBatchHistory(args: {
-// "0": [
-// "1c2336c32dc62f00862ee6a75643e01017c86edece10b5a9d7defbd5f66b0a80"
-// ]
-// })).called(1);
-//
-// verify(client?.getBlockHeadTip()).called(1);
-// verify(client?.getHistory(scripthash: anyNamed("scripthash"))).called(1);
-//
-// expect(secureStore?.interactions, 10);
-// expect(secureStore?.writes, 5);
-// expect(secureStore?.reads, 5);
-// expect(secureStore?.deletes, 0);
-//
-// 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?.getPricesAnd24hChange(baseCurrency: "USD"))
-// // // .thenAnswer((_) async => Decimal.one);
-// //
-// // await Hive.openBox(testWalletId);
-// // await Hive.openBox(DB.boxNamePrefs);
-// //
-// // // recover to fill data
-// // await bch?.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 bch?.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(2);
-// // verify(client?.estimateFee(blocks: anyNamed("blocks"))).called(3);
-// // verify(client?.getBlockHeadTip()).called(1);
-// // // verify(priceAPI?.getPricesAnd24hChange(baseCurrency: "USD")).called(2);
-// //
-// // expect(secureStore?.interactions, 6);
-// // expect(secureStore?.writes, 2);
-// // expect(secureStore?.reads, 2);
-// // expect(secureStore?.deletes, 0);
-// //
-// // verifyNoMoreInteractions(client);
-// // verifyNoMoreInteractions(cachedClient);
-// // verifyNoMoreInteractions(priceAPI);
-// // });
-// });
-//
-// tearDown(() async {
-// await tearDownTestHive();
-// });
-// }
+import 'package:bitcoindart/bitcoindart.dart';
+import 'package:decimal/decimal.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:hive/hive.dart';
+import 'package:hive_test/hive_test.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
+import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart';
+import 'package:stackwallet/electrumx_rpc/electrumx.dart';
+import 'package:stackwallet/hive/db.dart';
+import 'package:stackwallet/models/paymint/fee_object_model.dart';
+import 'package:stackwallet/models/paymint/transactions_model.dart';
+import 'package:stackwallet/models/paymint/utxo_model.dart';
+import 'package:stackwallet/services/coins/bitcoincash/bitcoincash_wallet.dart';
+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 'bitcoincash_history_sample_data.dart';
+import 'bitcoincash_wallet_test.mocks.dart';
+import 'bitcoincash_wallet_test_parameters.dart';
+
+@GenerateMocks(
+ [ElectrumX, CachedElectrumX, PriceAPI, TransactionNotificationTracker])
+void main() {
+ group("bitcoincash constants", () {
+ test("bitcoincash minimum confirmations", () async {
+ expect(MINIMUM_CONFIRMATIONS, 3);
+ });
+ test("bitcoincash dust limit", () async {
+ expect(DUST_LIMIT, 546);
+ });
+ test("bitcoincash mainnet genesis block hash", () async {
+ expect(GENESIS_HASH_MAINNET,
+ "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
+ });
+
+ test("bitcoincash testnet genesis block hash", () async {
+ expect(GENESIS_HASH_TESTNET,
+ "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943");
+ });
+ });
+
+ test("bitcoincash DerivePathType enum", () {
+ expect(DerivePathType.values.length, 2);
+ expect(DerivePathType.values.toString(),
+ "[DerivePathType.bip44, DerivePathType.bip49]");
+ });
+
+ group("bip32 node/root", () {
+ test("getBip32Root", () {
+ final root = getBip32Root(TEST_MNEMONIC, bitcoincash);
+ expect(root.toWIF(), ROOT_WIF);
+ });
+
+ test("basic getBip32Node", () {
+ final node =
+ getBip32Node(0, 0, TEST_MNEMONIC, bitcoincash, DerivePathType.bip44);
+ expect(node.toWIF(), NODE_WIF_44);
+ });
+ });
+
+ group("validate mainnet bitcoincash addresses", () {
+ MockElectrumX? client;
+ MockCachedElectrumX? cachedClient;
+ MockPriceAPI? priceAPI;
+ FakeSecureStorage? secureStore;
+ MockTransactionNotificationTracker? tracker;
+
+ BitcoinCashWallet? mainnetWallet;
+
+ setUp(() {
+ client = MockElectrumX();
+ cachedClient = MockCachedElectrumX();
+ priceAPI = MockPriceAPI();
+ secureStore = FakeSecureStorage();
+ tracker = MockTransactionNotificationTracker();
+
+ mainnetWallet = BitcoinCashWallet(
+ walletId: "validateAddressMainNet",
+ walletName: "validateAddressMainNet",
+ coin: Coin.bitcoincash,
+ client: client!,
+ cachedClient: cachedClient!,
+ tracker: tracker!,
+ priceAPI: priceAPI,
+ secureStore: secureStore,
+ );
+ });
+
+ test("valid mainnet legacy/p2pkh address type", () {
+ expect(
+ mainnetWallet?.addressType(
+ address: "1DP3PUePwMa5CoZwzjznVKhzdLsZftjcAT"),
+ DerivePathType.bip44);
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("invalid base58 address type", () {
+ expect(
+ () => mainnetWallet?.addressType(
+ address: "mhqpGtwhcR6gFuuRjLTpHo41919QfuGy8Y"),
+ throwsArgumentError);
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("invalid bech32 address type", () {
+ expect(
+ () => mainnetWallet?.addressType(
+ address: "tb1qzzlm6mnc8k54mx6akehl8p9ray8r439va5ndyq"),
+ throwsArgumentError);
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("address has no matching script", () {
+ expect(
+ () => mainnetWallet?.addressType(
+ address: "mpMk94ETazqonHutyC1v6ajshgtP8oiFKU"),
+ throwsArgumentError);
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("invalid mainnet bitcoincash legacy/p2pkh address", () {
+ expect(
+ mainnetWallet?.validateAddress("mhqpGtwhcR6gFuuRjLTpHo41919QfuGy8Y"),
+ true);
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+ });
+
+ group("testNetworkConnection", () {
+ MockElectrumX? client;
+ MockCachedElectrumX? cachedClient;
+ MockPriceAPI? priceAPI;
+ FakeSecureStorage? secureStore;
+ MockTransactionNotificationTracker? tracker;
+
+ BitcoinCashWallet? bch;
+
+ setUp(() {
+ client = MockElectrumX();
+ cachedClient = MockCachedElectrumX();
+ priceAPI = MockPriceAPI();
+ secureStore = FakeSecureStorage();
+ tracker = MockTransactionNotificationTracker();
+
+ bch = BitcoinCashWallet(
+ walletId: "testNetworkConnection",
+ walletName: "testNetworkConnection",
+ coin: Coin.bitcoincash,
+ client: client!,
+ cachedClient: cachedClient!,
+ tracker: tracker!,
+ priceAPI: priceAPI,
+ secureStore: secureStore,
+ );
+ });
+
+ test("attempted connection fails due to server error", () async {
+ when(client?.ping()).thenAnswer((_) async => false);
+ final bool? result = await bch?.testNetworkConnection();
+ expect(result, false);
+ expect(secureStore?.interactions, 0);
+ verify(client?.ping()).called(1);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("attempted connection fails due to exception", () async {
+ when(client?.ping()).thenThrow(Exception);
+ final bool? result = await bch?.testNetworkConnection();
+ expect(result, false);
+ expect(secureStore?.interactions, 0);
+ verify(client?.ping()).called(1);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("attempted connection test success", () async {
+ when(client?.ping()).thenAnswer((_) async => true);
+ final bool? result = await bch?.testNetworkConnection();
+ expect(result, true);
+ expect(secureStore?.interactions, 0);
+ verify(client?.ping()).called(1);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+ });
+
+ group("basic getters, setters, and functions", () {
+ final bchcoin = Coin.bitcoincash;
+ final testWalletId = "BCHtestWalletID";
+ final testWalletName = "BCHWallet";
+
+ MockElectrumX? client;
+ MockCachedElectrumX? cachedClient;
+ MockPriceAPI? priceAPI;
+ FakeSecureStorage? secureStore;
+ MockTransactionNotificationTracker? tracker;
+
+ BitcoinCashWallet? bch;
+
+ setUp(() async {
+ client = MockElectrumX();
+ cachedClient = MockCachedElectrumX();
+ priceAPI = MockPriceAPI();
+ secureStore = FakeSecureStorage();
+ tracker = MockTransactionNotificationTracker();
+
+ bch = BitcoinCashWallet(
+ walletId: testWalletId,
+ walletName: testWalletName,
+ coin: bchcoin,
+ client: client!,
+ cachedClient: cachedClient!,
+ tracker: tracker!,
+ priceAPI: priceAPI,
+ secureStore: secureStore,
+ );
+ });
+
+ test("get networkType main", () async {
+ expect(bch?.coin, bchcoin);
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("get networkType test", () async {
+ bch = BitcoinCashWallet(
+ walletId: testWalletId,
+ walletName: testWalletName,
+ coin: bchcoin,
+ client: client!,
+ cachedClient: cachedClient!,
+ tracker: tracker!,
+ priceAPI: priceAPI,
+ secureStore: secureStore,
+ );
+ expect(bch?.coin, bchcoin);
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("get cryptoCurrency", () async {
+ expect(Coin.bitcoincash, Coin.bitcoincash);
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("get coinName", () async {
+ expect(Coin.bitcoincash, Coin.bitcoincash);
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("get coinTicker", () async {
+ expect(Coin.bitcoincash, Coin.bitcoincash);
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("get and set walletName", () async {
+ expect(Coin.bitcoincash, Coin.bitcoincash);
+ bch?.walletName = "new name";
+ expect(bch?.walletName, "new name");
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("estimateTxFee", () async {
+ expect(bch?.estimateTxFee(vSize: 356, feeRatePerKB: 1), 356);
+ expect(bch?.estimateTxFee(vSize: 356, feeRatePerKB: 900), 356);
+ expect(bch?.estimateTxFee(vSize: 356, feeRatePerKB: 999), 356);
+ expect(bch?.estimateTxFee(vSize: 356, feeRatePerKB: 1000), 356);
+ expect(bch?.estimateTxFee(vSize: 356, feeRatePerKB: 1001), 712);
+ expect(bch?.estimateTxFee(vSize: 356, feeRatePerKB: 1699), 712);
+ expect(bch?.estimateTxFee(vSize: 356, feeRatePerKB: 2000), 712);
+ expect(bch?.estimateTxFee(vSize: 356, feeRatePerKB: 12345), 4628);
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("get fees succeeds", () async {
+ when(client?.ping()).thenAnswer((_) async => true);
+ 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_TESTNET,
+ "hash_function": "sha256",
+ "services": []
+ });
+ when(client?.estimateFee(blocks: 1))
+ .thenAnswer((realInvocation) async => Decimal.zero);
+ when(client?.estimateFee(blocks: 5))
+ .thenAnswer((realInvocation) async => Decimal.one);
+ when(client?.estimateFee(blocks: 20))
+ .thenAnswer((realInvocation) async => Decimal.ten);
+
+ final fees = await bch?.fees;
+ expect(fees, isA());
+ expect(fees?.slow, 1000000000);
+ expect(fees?.medium, 100000000);
+ expect(fees?.fast, 0);
+
+ verify(client?.estimateFee(blocks: 1)).called(1);
+ verify(client?.estimateFee(blocks: 5)).called(1);
+ verify(client?.estimateFee(blocks: 20)).called(1);
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("get fees fails", () async {
+ when(client?.ping()).thenAnswer((_) async => true);
+ 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_TESTNET,
+ "hash_function": "sha256",
+ "services": []
+ });
+ when(client?.estimateFee(blocks: 1))
+ .thenAnswer((realInvocation) async => Decimal.zero);
+ when(client?.estimateFee(blocks: 5))
+ .thenAnswer((realInvocation) async => Decimal.one);
+ when(client?.estimateFee(blocks: 20))
+ .thenThrow(Exception("some exception"));
+
+ bool didThrow = false;
+ try {
+ await bch?.fees;
+ } catch (_) {
+ didThrow = true;
+ }
+
+ expect(didThrow, true);
+
+ verify(client?.estimateFee(blocks: 1)).called(1);
+ verify(client?.estimateFee(blocks: 5)).called(1);
+ verify(client?.estimateFee(blocks: 20)).called(1);
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("get maxFee", () async {
+ when(client?.ping()).thenAnswer((_) async => true);
+ 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_TESTNET,
+ "hash_function": "sha256",
+ "services": []
+ });
+ when(client?.estimateFee(blocks: 20))
+ .thenAnswer((realInvocation) async => Decimal.zero);
+ when(client?.estimateFee(blocks: 5))
+ .thenAnswer((realInvocation) async => Decimal.one);
+ when(client?.estimateFee(blocks: 1))
+ .thenAnswer((realInvocation) async => Decimal.ten);
+
+ final maxFee = await bch?.maxFee;
+ expect(maxFee, 1000000000);
+
+ verify(client?.estimateFee(blocks: 1)).called(1);
+ verify(client?.estimateFee(blocks: 5)).called(1);
+ verify(client?.estimateFee(blocks: 20)).called(1);
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+ });
+
+ group("BCHWallet service class functions that depend on shared storage", () {
+ final bchcoin = Coin.bitcoincash;
+ final bchtestcoin = Coin.bitcoincashTestnet;
+ final testWalletId = "BCHtestWalletID";
+ final testWalletName = "BCHWallet";
+
+ bool hiveAdaptersRegistered = false;
+
+ MockElectrumX? client;
+ MockCachedElectrumX? cachedClient;
+ MockPriceAPI? priceAPI;
+ FakeSecureStorage? secureStore;
+ MockTransactionNotificationTracker? tracker;
+
+ BitcoinCashWallet? bch;
+
+ setUp(() async {
+ await setUpTestHive();
+ if (!hiveAdaptersRegistered) {
+ hiveAdaptersRegistered = true;
+
+ // Registering Transaction Model Adapters
+ Hive.registerAdapter(TransactionDataAdapter());
+ Hive.registerAdapter(TransactionChunkAdapter());
+ Hive.registerAdapter(TransactionAdapter());
+ Hive.registerAdapter(InputAdapter());
+ Hive.registerAdapter(OutputAdapter());
+
+ // Registering Utxo Model Adapters
+ Hive.registerAdapter(UtxoDataAdapter());
+ Hive.registerAdapter(UtxoObjectAdapter());
+ Hive.registerAdapter(StatusAdapter());
+
+ final wallets = await Hive.openBox('wallets');
+ await wallets.put('currentWalletName', testWalletName);
+ }
+
+ client = MockElectrumX();
+ cachedClient = MockCachedElectrumX();
+ priceAPI = MockPriceAPI();
+ secureStore = FakeSecureStorage();
+ tracker = MockTransactionNotificationTracker();
+
+ bch = BitcoinCashWallet(
+ walletId: testWalletId,
+ walletName: testWalletName,
+ coin: bchcoin,
+ client: client!,
+ cachedClient: cachedClient!,
+ tracker: tracker!,
+ priceAPI: priceAPI,
+ secureStore: secureStore,
+ );
+ });
+
+ // test("initializeWallet no network", () async {
+ // when(client?.ping()).thenAnswer((_) async => false);
+ // await Hive.openBox(testWalletId);
+ // await Hive.openBox(DB.boxNamePrefs);
+ // expect(bch?.initializeNew(), false);
+ // expect(secureStore?.interactions, 0);
+ // verify(client?.ping()).called(0);
+ // verifyNoMoreInteractions(client);
+ // verifyNoMoreInteractions(cachedClient);
+ // verifyNoMoreInteractions(priceAPI);
+ // });
+
+ // test("initializeExisting no network exception", () async {
+ // when(client?.ping()).thenThrow(Exception("Network connection failed"));
+ // // bch?.initializeNew();
+ // expect(bch?.initializeExisting(), false);
+ // expect(secureStore?.interactions, 0);
+ // verify(client?.ping()).called(1);
+ // verifyNoMoreInteractions(client);
+ // verifyNoMoreInteractions(cachedClient);
+ // verifyNoMoreInteractions(priceAPI);
+ // });
+
+ test("initializeNew mainnet throws bad network", () 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_TESTNET,
+ "hash_function": "sha256",
+ "services": []
+ });
+
+ await Hive.openBox(testWalletId);
+ await Hive.openBox(DB.boxNamePrefs);
+
+ expectLater(() => bch?.initializeNew(), throwsA(isA()))
+ .then((_) {
+ expect(secureStore?.interactions, 0);
+ verifyNever(client?.ping()).called(0);
+ verify(client?.getServerFeatures()).called(1);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(priceAPI);
+ });
+ });
+
+ test("initializeNew throws mnemonic overwrite exception", () 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": []
+ });
+ await secureStore?.write(
+ key: "${testWalletId}_mnemonic", value: "some mnemonic");
+
+ await Hive.openBox(testWalletId);
+ await Hive.openBox(DB.boxNamePrefs);
+
+ expectLater(() => bch?.initializeNew(), throwsA(isA()))
+ .then((_) {
+ expect(secureStore?.interactions, 2);
+ verifyNever(client?.ping()).called(0);
+ verify(client?.getServerFeatures()).called(1);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(priceAPI);
+ });
+ });
+
+ test("initializeExisting testnet throws bad network", () async {
+ when(client?.ping()).thenAnswer((_) async => true);
+ 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_TESTNET,
+ "hash_function": "sha256",
+ "services": []
+ });
+
+ bch = BitcoinCashWallet(
+ walletId: testWalletId,
+ walletName: testWalletName,
+ coin: bchcoin,
+ client: client!,
+ cachedClient: cachedClient!,
+ tracker: tracker!,
+ priceAPI: priceAPI,
+ secureStore: secureStore,
+ );
+
+ await Hive.openBox(testWalletId);
+ await Hive.openBox(DB.boxNamePrefs);
+
+ expectLater(() => bch?.initializeNew(), throwsA(isA()))
+ .then((_) {
+ expect(secureStore?.interactions, 0);
+ verifyNever(client?.ping()).called(0);
+ verify(client?.getServerFeatures()).called(1);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(priceAPI);
+ });
+ });
+
+ // test("getCurrentNode", () async {
+ // // when(priceAPI?.getbitcoincashPrice(baseCurrency: "USD"))
+ // // .thenAnswer((realInvocation) async => Decimal.fromInt(10));
+ // when(client?.ping()).thenAnswer((_) async => true);
+ // 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": []
+ // });
+ // // await DebugService.instance.init();
+ // expect(bch?.initializeExisting(), true);
+ //
+ // bool didThrow = false;
+ // try {
+ // await bch?.getCurrentNode();
+ // } catch (_) {
+ // didThrow = true;
+ // }
+ // // expect no nodes on a fresh wallet unless set in db externally
+ // expect(didThrow, true);
+ //
+ // // set node
+ // final wallet = await Hive.openBox(testWalletId);
+ // await wallet.put("nodes", {
+ // "default": {
+ // "id": "some nodeID",
+ // "ipAddress": "some address",
+ // "port": "9000",
+ // "useSSL": true,
+ // }
+ // });
+ // await wallet.put("activeNodeName", "default");
+ //
+ // // try fetching again
+ // final node = await bch?.getCurrentNode();
+ // expect(node.toString(),
+ // "ElectrumXNode: {address: some address, port: 9000, name: default, useSSL: true}");
+ //
+ // verify(client?.ping()).called(1);
+ // verify(client?.getServerFeatures()).called(1);
+ // verifyNoMoreInteractions(client);
+ // verifyNoMoreInteractions(cachedClient);
+ // verifyNoMoreInteractions(priceAPI);
+ // });
+
+ // test("initializeWallet new main net wallet", () async {
+ // when(priceAPI?.getbitcoincashPrice(baseCurrency: "USD"))
+ // .thenAnswer((realInvocation) async => Decimal.fromInt(10));
+ // when(client?.ping()).thenAnswer((_) async => true);
+ // 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": []
+ // });
+ // expect(await bch?.initializeWallet(), true);
+ //
+ // final wallet = await Hive.openBox(testWalletId);
+ //
+ // expect(await wallet.get("addressBookEntries"), {});
+ // expect(await wallet.get('notes'), null);
+ // expect(await wallet.get("id"), testWalletId);
+ // expect(await wallet.get("preferredFiatCurrency"), null);
+ // expect(await wallet.get("blocked_tx_hashes"), ["0xdefault"]);
+ //
+ // final changeAddressesP2PKH = await wallet.get("changeAddressesP2PKH");
+ // expect(changeAddressesP2PKH, isA>());
+ // expect(changeAddressesP2PKH.length, 1);
+ // expect(await wallet.get("changeIndexP2PKH"), 0);
+ //
+ // final receivingAddressesP2PKH =
+ // await wallet.get("receivingAddressesP2PKH");
+ // expect(receivingAddressesP2PKH, isA>());
+ // expect(receivingAddressesP2PKH.length, 1);
+ // expect(await wallet.get("receivingIndexP2PKH"), 0);
+ //
+ // final p2pkhReceiveDerivations = jsonDecode(await secureStore?.read(
+ // key: "${testWalletId}_receiveDerivationsP2PKH"));
+ // expect(p2pkhReceiveDerivations.length, 1);
+ //
+ // final p2pkhChangeDerivations = jsonDecode(await secureStore.read(
+ // key: "${testWalletId}_changeDerivationsP2PKH"));
+ // expect(p2pkhChangeDerivations.length, 1);
+ //
+ // expect(secureStore?.interactions, 10);
+ // expect(secureStore?.reads, 7);
+ // expect(secureStore?.writes, 3);
+ // expect(secureStore?.deletes, 0);
+ // verify(client?.ping()).called(1);
+ // verify(client?.getServerFeatures()).called(1);
+ // verifyNoMoreInteractions(client);
+ // verifyNoMoreInteractions(cachedClient);
+ // verifyNoMoreInteractions(priceAPI);
+ // });
+
+ // // test("initializeWallet existing main net wallet", () async {
+ // // when(priceAPI?.getbitcoincashPrice(baseCurrency: "USD"))
+ // // .thenAnswer((realInvocation) async => Decimal.fromInt(10));
+ // // when(client?.ping()).thenAnswer((_) async => true);
+ // // when(client?.getBatchHistory(args: anyNamed("args")))
+ // // .thenAnswer((_) 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": []
+ // // });
+ // // // init new wallet
+ // // expect(bch?.initializeNew(), true);
+ // //
+ // // // fetch data to compare later
+ // // final newWallet = await Hive.openBox(testWalletId);
+ // //
+ // // final addressBookEntries = await newWallet.get("addressBookEntries");
+ // // final notes = await newWallet.get('notes');
+ // // final wID = await newWallet.get("id");
+ // // final currency = await newWallet.get("preferredFiatCurrency");
+ // // final blockedHashes = await newWallet.get("blocked_tx_hashes");
+ // //
+ // // final changeAddressesP2PKH = await newWallet.get("changeAddressesP2PKH");
+ // // final changeIndexP2PKH = await newWallet.get("changeIndexP2PKH");
+ // //
+ // // final receivingAddressesP2PKH =
+ // // await newWallet.get("receivingAddressesP2PKH");
+ // // final receivingIndexP2PKH = await newWallet.get("receivingIndexP2PKH");
+ // //
+ // // final p2pkhReceiveDerivations = jsonDecode(await secureStore?.read(
+ // // key: "${testWalletId}_receiveDerivationsP2PKH"));
+ // //
+ // // final p2pkhChangeDerivations = jsonDecode(await secureStore?.read(
+ // // key: "${testWalletId}_changeDerivationsP2PKH"));
+ // //
+ // // // exit new wallet
+ // // await bch?.exit();
+ // //
+ // // // open existing/created wallet
+ // // bch = BitcoinCashWallet(
+ // // walletId: testWalletId,
+ // // walletName: testWalletName,
+ // // coin: dtestcoin,
+ // // client: client!,
+ // // cachedClient: cachedClient!,
+ // // priceAPI: priceAPI,
+ // // secureStore: secureStore,
+ // // );
+ // //
+ // // // init existing
+ // // expect(bch?.initializeExisting(), true);
+ // //
+ // // // compare data to ensure state matches state of previously closed wallet
+ // // final wallet = await Hive.openBox(testWalletId);
+ // //
+ // // expect(await wallet.get("addressBookEntries"), addressBookEntries);
+ // // expect(await wallet.get('notes'), notes);
+ // // expect(await wallet.get("id"), wID);
+ // // expect(await wallet.get("preferredFiatCurrency"), currency);
+ // // expect(await wallet.get("blocked_tx_hashes"), blockedHashes);
+ // //
+ // // expect(await wallet.get("changeAddressesP2PKH"), changeAddressesP2PKH);
+ // // expect(await wallet.get("changeIndexP2PKH"), changeIndexP2PKH);
+ // //
+ // // expect(
+ // // await wallet.get("receivingAddressesP2PKH"), receivingAddressesP2PKH);
+ // // expect(await wallet.get("receivingIndexP2PKH"), receivingIndexP2PKH);
+ // //
+ // // expect(
+ // // jsonDecode(await secureStore?.read(
+ // // key: "${testWalletId}_receiveDerivationsP2PKH")),
+ // // p2pkhReceiveDerivations);
+ // //
+ // // expect(
+ // // jsonDecode(await secureStore?.read(
+ // // key: "${testWalletId}_changeDerivationsP2PKH")),
+ // // p2pkhChangeDerivations);
+ // //
+ // // expect(secureStore?.interactions, 12);
+ // // expect(secureStore?.reads, 9);
+ // // expect(secureStore?.writes, 3);
+ // // expect(secureStore?.deletes, 0);
+ // // verify(client?.ping()).called(2);
+ // // verify(client?.getServerFeatures()).called(1);
+ // // verifyNoMoreInteractions(client);
+ // // verifyNoMoreInteractions(cachedClient);
+ // // verifyNoMoreInteractions(priceAPI);
+ // // });
+
+ test("get current receiving addresses", () async {
+ bch = BitcoinCashWallet(
+ walletId: testWalletId,
+ walletName: testWalletName,
+ coin: bchtestcoin,
+ client: client!,
+ cachedClient: cachedClient!,
+ tracker: tracker!,
+ priceAPI: priceAPI,
+ secureStore: secureStore,
+ );
+ when(client?.ping()).thenAnswer((_) async => true);
+ 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_TESTNET,
+ "hash_function": "sha256",
+ "services": []
+ });
+
+ await Hive.openBox(testWalletId);
+ await Hive.openBox(DB.boxNamePrefs);
+
+ await bch?.initializeNew();
+ await bch?.initializeExisting();
+ expect(
+ Address.validateAddress(
+ await bch!.currentReceivingAddress, bitcoincashtestnet),
+ true);
+ expect(
+ Address.validateAddress(
+ await bch!.currentReceivingAddress, bitcoincashtestnet),
+ true);
+ expect(
+ Address.validateAddress(
+ await bch!.currentReceivingAddress, bitcoincashtestnet),
+ true);
+
+ verifyNever(client?.ping()).called(0);
+ verify(client?.getServerFeatures()).called(1);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("get allOwnAddresses", () async {
+ bch = BitcoinCashWallet(
+ walletId: testWalletId,
+ walletName: testWalletName,
+ coin: bchtestcoin,
+ client: client!,
+ cachedClient: cachedClient!,
+ tracker: tracker!,
+ priceAPI: priceAPI,
+ secureStore: secureStore,
+ );
+ when(client?.ping()).thenAnswer((_) async => true);
+ 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_TESTNET,
+ "hash_function": "sha256",
+ "services": []
+ });
+
+ await Hive.openBox(testWalletId);
+ await Hive.openBox(DB.boxNamePrefs);
+
+ await bch?.initializeNew();
+ await bch?.initializeExisting();
+ final addresses = await bch?.allOwnAddresses;
+ expect(addresses, isA>());
+ expect(addresses?.length, 2);
+
+ for (int i = 0; i < 2; i++) {
+ expect(
+ Address.validateAddress(addresses![i], bitcoincashtestnet), true);
+ }
+
+ verifyNever(client?.ping()).called(0);
+ verify(client?.getServerFeatures()).called(1);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ // test("get utxos and balances", () async {
+ // bch = BitcoinCashWallet(
+ // walletId: testWalletId,
+ // walletName: testWalletName,
+ // coin: dtestcoin,
+ // client: client!,
+ // cachedClient: cachedClient!,
+ // tracker: tracker!,
+ // priceAPI: priceAPI,
+ // secureStore: secureStore,
+ // );
+ // when(client?.ping()).thenAnswer((_) async => true);
+ // 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_TESTNET,
+ // "hash_function": "sha256",
+ // "services": []
+ // });
+ //
+ // await Hive.openBox(testWalletId);
+ // await Hive.openBox(DB.boxNamePrefs);
+ //
+ // when(client?.getBatchUTXOs(args: anyNamed("args")))
+ // .thenAnswer((_) async => batchGetUTXOResponse0);
+ //
+ // when(client?.estimateFee(blocks: 20))
+ // .thenAnswer((realInvocation) async => Decimal.zero);
+ // when(client?.estimateFee(blocks: 5))
+ // .thenAnswer((realInvocation) async => Decimal.one);
+ // when(client?.estimateFee(blocks: 1))
+ // .thenAnswer((realInvocation) async => Decimal.ten);
+ //
+ // when(cachedClient?.getTransaction(
+ // txHash: tx1.txid,
+ // coin: Coin.bitcoincashTestNet,
+ // )).thenAnswer((_) async => tx1Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash: tx2.txid,
+ // coin: Coin.bitcoincashTestNet,
+ // )).thenAnswer((_) async => tx2Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash: tx3.txid,
+ // coin: Coin.bitcoincashTestNet,
+ // )).thenAnswer((_) async => tx3Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash: tx4.txid,
+ // coin: Coin.bitcoincashTestNet,
+ // )).thenAnswer((_) async => tx4Raw);
+ //
+ // await bch?.initializeNew();
+ // await bch?.initializeExisting();
+ //
+ // final utxoData = await bch?.utxoData;
+ // expect(utxoData, isA());
+ // expect(utxoData.toString(),
+ // r"{totalUserCurrency: $103.2173, satoshiBalance: 1032173000, bitcoinBalance: null, unspentOutputArray: [{txid: 86198a91805b6c53839a6a97736c434a5a2f85d68595905da53df7df59b9f01a, vout: 0, value: 800000000, fiat: $80, blocked: false, status: {confirmed: true, blockHash: e52cabb4445eb9ceb3f4f8d68cc64b1ede8884ce560296c27826a48ecc477370, blockHeight: 4274457, blockTime: 1655755742, confirmations: 100}}, {txid: a4b6bd97a4b01b4305d0cf02e9bac6b7c37cda2f8e9dfe291ce4170b810ed469, vout: 0, value: 72173000, fiat: $7.2173, blocked: false, status: {confirmed: false, blockHash: bd239f922b3ecec299a90e4d1ce389334e8df4b95470fb5919966b0b650bb95b, blockHeight: 4270459, blockTime: 1655500912, confirmations: 0}}, {txid: 68c159dcc2f962cbc61f7dd3c8d0dcc14da8adb443811107115531c853fc0c60, vout: 1, value: 100000000, fiat: $10, blocked: false, status: {confirmed: false, blockHash: 9fee9b9446cfe81abb1a17bec56e6c160d9a6527e5b68b1141a827573bc2649f, blockHeight: 4255659, blockTime: 1654553247, confirmations: 0}}, {txid: 628a78606058ce4036aee3907e042742156c1894d34419578de5671b53ea5800, vout: 0, value: 60000000, fiat: $6, blocked: false, status: {confirmed: true, blockHash: bc461ab43e3a80d9a4d856ee9ff70f41d86b239d5f0581ffd6a5c572889a6b86, blockHeight: 4270352, blockTime: 1652888705, confirmations: 100}}]}");
+ //
+ // final outputs = await bch?.unspentOutputs;
+ // expect(outputs, isA>());
+ // expect(outputs?.length, 4);
+ //
+ // final availableBalance = await bch?.availableBalance;
+ // expect(availableBalance, Decimal.parse("8.6"));
+ //
+ // final totalBalance = await bch?.totalBalance;
+ // expect(totalBalance, Decimal.parse("10.32173"));
+ //
+ // final pendingBalance = await bch?.pendingBalance;
+ // expect(pendingBalance, Decimal.parse("1.72173"));
+ //
+ // final balanceMinusMaxFee = await bch?.balanceMinusMaxFee;
+ // expect(balanceMinusMaxFee, Decimal.parse("7.6"));
+ //
+ // verify(client?.ping()).called(1);
+ // verify(client?.getServerFeatures()).called(1);
+ // verify(client?.estimateFee(blocks: 1)).called(1);
+ // verify(client?.estimateFee(blocks: 5)).called(1);
+ // verify(client?.estimateFee(blocks: 20)).called(1);
+ // verify(client?.getBatchUTXOs(args: anyNamed("args"))).called(1);
+ // verify(cachedClient?.getTransaction(
+ // txHash: tx1.txid,
+ // coin: Coin.bitcoincashTestNet,
+ // )).called(1);
+ // verify(cachedClient?.getTransaction(
+ // txHash: tx2.txid,
+ // coin: Coin.bitcoincashTestNet,
+ // )).called(1);
+ // verify(cachedClient?.getTransaction(
+ // txHash: tx3.txid,
+ // coin: Coin.bitcoincashTestNet,
+ // )).called(1);
+ // verify(cachedClient?.getTransaction(
+ // txHash: tx4.txid,
+ // coin: Coin.bitcoincashTestNet,
+ // )).called(1);
+ //
+ // verifyNoMoreInteractions(client);
+ // verifyNoMoreInteractions(cachedClient);
+ // verifyNoMoreInteractions(priceAPI);
+ // });
+ //
+ // // test("get utxos - multiple batches", () async {
+ // // bch = BitcoinCashWallet(
+ // // walletId: testWalletId,
+ // // walletName: testWalletName,
+ // // coin: dtestcoin,
+ // // client: client!,
+ // // cachedClient: cachedClient!,
+ // // priceAPI: priceAPI,
+ // // secureStore: secureStore,
+ // // );
+ // // when(client?.ping()).thenAnswer((_) async => true);
+ // // 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_TESTNET,
+ // // "hash_function": "sha256",
+ // // "services": []
+ // // });
+ // //
+ // // when(client?.getBatchUTXOs(args: anyNamed("args")))
+ // // .thenAnswer((_) async => {});
+ // //
+ // // when(priceAPI?.getbitcoincashPrice(baseCurrency: "USD"))
+ // // .thenAnswer((realInvocation) async => Decimal.fromInt(10));
+ // //
+ // // await bch?.initializeWallet();
+ // //
+ // // // add some extra addresses to make sure we have more than the single batch size of 10
+ // // final wallet = await Hive.openBox(testWalletId);
+ // // final addresses = await wallet.get("receivingAddressesP2PKH");
+ // // addresses.add("DQaAi9R58GXMpDyhePys6hHCuif4fhc1sN");
+ // // addresses.add("DBVhuF8QgeuxU2pssxzMgJqPhGCx5qyVkD");
+ // // addresses.add("DCAokB2CXXPWC2JPj6jrK6hxANwTF2m21x");
+ // // addresses.add("D6Y9brE3jUGPrqLmSEWh6yQdgY5b7ZkTib");
+ // // addresses.add("DKdtobt3M5b3kQWZf1zRUZn3Ys6JTQwbPL");
+ // // addresses.add("DBYiFr1BRc2zB19p8jxdSu6DvFGTdWvkVF");
+ // // addresses.add("DE5ffowvbHPzzY6aRVGpzxR2QqikXxUKPG");
+ // // addresses.add("DA97TLg1741J2aLK6z9bVZoWysgQbMR45K");
+ // // addresses.add("DGGmf9q4PKcJXauPRstsFetu9DjW1VSBYk");
+ // // addresses.add("D9bXqnTtufcb6oJyuZniCXbst8MMLzHxUd");
+ // // addresses.add("DA6nv8M4kYL4RxxKrcsPaPUA1KrFA7CTfN");
+ // // await wallet.put("receivingAddressesP2PKH", addresses);
+ // //
+ // // final utxoData = await bch?.utxoData;
+ // // expect(utxoData, isA());
+ // //
+ // // final outputs = await bch?.unspentOutputs;
+ // // expect(outputs, isA>());
+ // // expect(outputs?.length, 0);
+ // //
+ // // verify(client?.ping()).called(1);
+ // // verify(client?.getServerFeatures()).called(1);
+ // // verify(client?.getBatchUTXOs(args: anyNamed("args"))).called(2);
+ // // verify(priceAPI?.getbitcoincashPrice(baseCurrency: "USD")).called(1);
+ // //
+ // // verifyNoMoreInteractions(client);
+ // // verifyNoMoreInteractions(cachedClient);
+ // // verifyNoMoreInteractions(priceAPI);
+ // // });
+ //
+ test("get utxos fails", () async {
+ bch = BitcoinCashWallet(
+ walletId: testWalletId,
+ walletName: testWalletName,
+ coin: bchtestcoin,
+ client: client!,
+ cachedClient: cachedClient!,
+ tracker: tracker!,
+ priceAPI: priceAPI,
+ secureStore: secureStore,
+ );
+ when(client?.ping()).thenAnswer((_) async => true);
+ 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_TESTNET,
+ "hash_function": "sha256",
+ "services": []
+ });
+
+ await Hive.openBox(testWalletId);
+ await Hive.openBox(DB.boxNamePrefs);
+
+ when(client?.getBatchUTXOs(args: anyNamed("args")))
+ .thenThrow(Exception("some exception"));
+
+ await bch?.initializeNew();
+ await bch?.initializeExisting();
+
+ final utxoData = await bch?.utxoData;
+ expect(utxoData, isA());
+ expect(utxoData.toString(),
+ r"{totalUserCurrency: 0.00, satoshiBalance: 0, bitcoinBalance: 0, unspentOutputArray: []}");
+
+ final outputs = await bch?.unspentOutputs;
+ expect(outputs, isA>());
+ expect(outputs?.length, 0);
+
+ verifyNever(client?.ping()).called(0);
+ verify(client?.getServerFeatures()).called(1);
+ verify(client?.getBatchUTXOs(args: anyNamed("args"))).called(1);
+
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("chain height fetch, update, and get", () async {
+ bch = BitcoinCashWallet(
+ walletId: testWalletId,
+ walletName: testWalletName,
+ coin: bchtestcoin,
+ client: client!,
+ cachedClient: cachedClient!,
+ tracker: tracker!,
+ priceAPI: priceAPI,
+ secureStore: secureStore,
+ );
+ when(client?.ping()).thenAnswer((_) async => true);
+ 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_TESTNET,
+ "hash_function": "sha256",
+ "services": []
+ });
+
+ await Hive.openBox(testWalletId);
+ await Hive.openBox(DB.boxNamePrefs);
+
+ await bch?.initializeNew();
+ await bch?.initializeExisting();
+
+ // get stored
+ expect(await bch?.storedChainHeight, 0);
+
+ // fetch fails
+ when(client?.getBlockHeadTip()).thenThrow(Exception("Some exception"));
+ expect(await bch?.chainHeight, -1);
+
+ // fetch succeeds
+ when(client?.getBlockHeadTip()).thenAnswer((realInvocation) async => {
+ "height": 100,
+ "hex": "some block hex",
+ });
+ expect(await bch?.chainHeight, 100);
+
+ // update
+ await bch?.updateStoredChainHeight(newHeight: 1000);
+
+ // fetch updated
+ expect(await bch?.storedChainHeight, 1000);
+
+ verifyNever(client?.ping()).called(0);
+ verify(client?.getServerFeatures()).called(1);
+ verify(client?.getBlockHeadTip()).called(2);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("getTxCount succeeds", () async {
+ when(client?.getHistory(
+ scripthash:
+ "1df1cab6d109d506aa424b00b6a013c5e1947dc13b78d62b4d0e9f518b3035d1"))
+ .thenAnswer((realInvocation) async => [
+ {
+ "height": 757727,
+ "tx_hash":
+ "aaac451c49c2e3bcbccb8a9fded22257eeb94c1702b456171aa79250bc1b20e0"
+ },
+ {
+ "height": 0,
+ "tx_hash":
+ "9ac29f35b72ca596bc45362d1f9556b0555e1fb633ca5ac9147a7fd467700afe"
+ }
+ ]);
+
+ final count =
+ await bch?.getTxCount(address: "1MMi672ueYFXLLdtZqPe4FsrS46gNDyRq1");
+
+ expect(count, 2);
+
+ verify(client?.getHistory(
+ scripthash:
+ "1df1cab6d109d506aa424b00b6a013c5e1947dc13b78d62b4d0e9f518b3035d1"))
+ .called(1);
+
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+ //TODO - Needs refactoring
+ test("getTxCount fails", () async {
+ when(client?.getHistory(
+ scripthash:
+ "64953f7db441a21172de206bf70b920c8c718ed4f03df9a85073c0400be0053c"))
+ .thenThrow(Exception("some exception"));
+
+ bool didThrow = false;
+ try {
+ await bch?.getTxCount(address: "D6biRASajCy7GcJ8R6ZP4RE94fNRerJLCC");
+ } catch (_) {
+ didThrow = true;
+ }
+ expect(didThrow, true);
+
+ verifyNever(client?.getHistory(
+ scripthash:
+ "64953f7db441a21172de206bf70b920c8c718ed4f03df9a85073c0400be0053c"))
+ .called(0);
+
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("_checkCurrentReceivingAddressesForTransactions succeeds", () async {
+ when(client?.ping()).thenAnswer((_) async => true);
+ 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?.getHistory(scripthash: anyNamed("scripthash")))
+ .thenAnswer((realInvocation) async => [
+ {
+ "height": 4270385,
+ "tx_hash":
+ "c07f740ad72c0dd759741f4c9ab4b1586a22bc16545584364ac9b3d845766271"
+ },
+ {
+ "height": 4270459,
+ "tx_hash":
+ "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a"
+ }
+ ]);
+
+ await Hive.openBox(testWalletId);
+ await Hive.openBox(DB.boxNamePrefs);
+
+ await bch?.initializeNew();
+ await bch?.initializeExisting();
+
+ bool didThrow = false;
+ try {
+ await bch?.checkCurrentReceivingAddressesForTransactions();
+ } catch (_) {
+ didThrow = true;
+ }
+ expect(didThrow, false);
+
+ verify(client?.getHistory(scripthash: anyNamed("scripthash"))).called(2);
+ verify(client?.getServerFeatures()).called(1);
+ verifyNever(client?.ping()).called(0);
+
+ expect(secureStore?.interactions, 20);
+ expect(secureStore?.reads, 13);
+ expect(secureStore?.writes, 7);
+ expect(secureStore?.deletes, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("_checkCurrentReceivingAddressesForTransactions fails", () async {
+ when(client?.ping()).thenAnswer((_) async => true);
+ 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?.getHistory(scripthash: anyNamed("scripthash")))
+ .thenThrow(Exception("some exception"));
+
+ await Hive.openBox(testWalletId);
+ await Hive.openBox(DB.boxNamePrefs);
+
+ await bch?.initializeNew();
+ await bch?.initializeExisting();
+
+ bool didThrow = false;
+ try {
+ await bch?.checkCurrentReceivingAddressesForTransactions();
+ } catch (_) {
+ didThrow = true;
+ }
+ expect(didThrow, true);
+
+ verify(client?.getHistory(scripthash: anyNamed("scripthash"))).called(1);
+ verify(client?.getServerFeatures()).called(1);
+ verifyNever(client?.ping()).called(0);
+
+ expect(secureStore?.interactions, 14);
+ expect(secureStore?.reads, 9);
+ expect(secureStore?.writes, 5);
+ expect(secureStore?.deletes, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("_checkCurrentChangeAddressesForTransactions succeeds", () async {
+ when(client?.ping()).thenAnswer((_) async => true);
+ 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?.getHistory(scripthash: anyNamed("scripthash")))
+ .thenAnswer((realInvocation) async => [
+ {
+ "height": 4286283,
+ "tx_hash":
+ "4c119685401e28982283e644c57d84fde6aab83324012e35c9b49e6efd99b49b"
+ },
+ {
+ "height": 4286295,
+ "tx_hash":
+ "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a"
+ }
+ ]);
+
+ await Hive.openBox(testWalletId);
+ await Hive.openBox(DB.boxNamePrefs);
+
+ await bch?.initializeNew();
+ await bch?.initializeExisting();
+
+ bool didThrow = false;
+ try {
+ await bch?.checkCurrentChangeAddressesForTransactions();
+ } catch (_) {
+ didThrow = true;
+ }
+ expect(didThrow, false);
+
+ verify(client?.getHistory(scripthash: anyNamed("scripthash"))).called(2);
+ verify(client?.getServerFeatures()).called(1);
+ verifyNever(client?.ping()).called(0);
+
+ expect(secureStore?.interactions, 20);
+ expect(secureStore?.reads, 13);
+ expect(secureStore?.writes, 7);
+ expect(secureStore?.deletes, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("_checkCurrentChangeAddressesForTransactions fails", () async {
+ when(client?.ping()).thenAnswer((_) async => true);
+ 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?.getHistory(scripthash: anyNamed("scripthash")))
+ .thenThrow(Exception("some exception"));
+
+ await Hive.openBox(testWalletId);
+ await Hive.openBox(DB.boxNamePrefs);
+
+ await bch?.initializeNew();
+ await bch?.initializeExisting();
+
+ bool didThrow = false;
+ try {
+ await bch?.checkCurrentChangeAddressesForTransactions();
+ } catch (_) {
+ didThrow = true;
+ }
+ expect(didThrow, true);
+
+ verify(client?.getHistory(scripthash: anyNamed("scripthash"))).called(1);
+ verify(client?.getServerFeatures()).called(1);
+ verifyNever(client?.ping()).called(0);
+
+ expect(secureStore?.interactions, 14);
+ expect(secureStore?.reads, 9);
+ expect(secureStore?.writes, 5);
+ expect(secureStore?.deletes, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ // test("getAllTxsToWatch", () async {
+ // TestWidgetsFlutterBinding.ensureInitialized();
+ // var notifications = {"show": 0};
+ // const MethodChannel('dexterous.com/flutter/local_notifications')
+ // .setMockMethodCallHandler((call) async {
+ // notifications[call.method]++;
+ // });
+ //
+ // bch?.pastUnconfirmedTxs = {
+ // "88b7b5077d940dde1bc63eba37a09dec8e7b9dad14c183a2e879a21b6ec0ac1c",
+ // "b39bac02b65af46a49e2985278fe24ca00dd5d627395d88f53e35568a04e10fa",
+ // };
+ //
+ // await bch?.getAllTxsToWatch(transactionData);
+ // expect(notifications.length, 1);
+ // expect(notifications["show"], 3);
+ //
+ // expect(bch?.unconfirmedTxs, {
+ // "b2f75a017a7435f1b8c2e080a865275d8f80699bba68d8dce99a94606e7b3528",
+ // 'dcca229760b44834478f0b266c9b3f5801e0139fdecacdc0820e447289a006d3',
+ // });
+ //
+ // expect(secureStore?.interactions, 0);
+ // verifyNoMoreInteractions(client);
+ // verifyNoMoreInteractions(cachedClient);
+ // verifyNoMoreInteractions(priceAPI);
+ // });
+ //
+ // test("refreshIfThereIsNewData true A", () async {
+ // when(client?.getTransaction(
+ // txHash:
+ // "a4b6bd97a4b01b4305d0cf02e9bac6b7c37cda2f8e9dfe291ce4170b810ed469",
+ // )).thenAnswer((_) async => tx2Raw);
+ // when(client?.getTransaction(
+ // txHash:
+ // "86198a91805b6c53839a6a97736c434a5a2f85d68595905da53df7df59b9f01a",
+ // )).thenAnswer((_) async => tx1Raw);
+ //
+ // bch = BitcoinCashWallet(
+ // walletId: testWalletId,
+ // walletName: testWalletName,
+ // coin: dtestcoin,
+ // client: client!,
+ // cachedClient: cachedClient!,
+ // priceAPI: priceAPI,
+ // secureStore: secureStore,
+ // );
+ // final wallet = await Hive.openBox(testWalletId);
+ // await wallet.put('receivingAddressesP2PKH', []);
+ //
+ // await wallet.put('changeAddressesP2PKH', []);
+ //
+ // bch?.unconfirmedTxs = {
+ // "a4b6bd97a4b01b4305d0cf02e9bac6b7c37cda2f8e9dfe291ce4170b810ed469",
+ // "86198a91805b6c53839a6a97736c434a5a2f85d68595905da53df7df59b9f01a"
+ // };
+ //
+ // final result = await bch?.refreshIfThereIsNewData();
+ //
+ // expect(result, true);
+ //
+ // verify(client?.getTransaction(
+ // txHash:
+ // "a4b6bd97a4b01b4305d0cf02e9bac6b7c37cda2f8e9dfe291ce4170b810ed469",
+ // )).called(1);
+ // verify(client?.getTransaction(
+ // txHash:
+ // "86198a91805b6c53839a6a97736c434a5a2f85d68595905da53df7df59b9f01a",
+ // )).called(1);
+ //
+ // expect(secureStore?.interactions, 0);
+ // verifyNoMoreInteractions(client);
+ // verifyNoMoreInteractions(cachedClient);
+ // verifyNoMoreInteractions(priceAPI);
+ // });
+ //
+ // test("refreshIfThereIsNewData true B", () async {
+ // // when(priceAPI.getbitcoincashPrice(baseCurrency: "USD"))
+ // // .thenAnswer((_) async => Decimal.fromInt(10));
+ //
+ // when(client?.getBatchHistory(args: anyNamed("args")))
+ // .thenAnswer((realInvocation) async {
+ // final uuids = Map>.from(realInvocation
+ // .namedArguments.values.first as Map)
+ // .keys
+ // .toList(growable: false);
+ // return {
+ // uuids[0]: [
+ // {
+ // "tx_hash":
+ // "351a94874379a5444c8891162472acf66de538a1abc647d4753f3e1eb5ec66f9",
+ // "height": 4286305
+ // },
+ // {
+ // "tx_hash":
+ // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
+ // "height": 4286295
+ // }
+ // ],
+ // uuids[1]: [
+ // {
+ // "tx_hash":
+ // "4c119685401e28982283e644c57d84fde6aab83324012e35c9b49e6efd99b49b",
+ // "height": 4286283
+ // }
+ // ],
+ // };
+ // });
+ //
+ // when(client?.getTransaction(
+ // txHash:
+ // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
+ // )).thenAnswer((_) async => tx2Raw);
+ // when(client?.getTransaction(
+ // txHash:
+ // "4c119685401e28982283e644c57d84fde6aab83324012e35c9b49e6efd99b49b",
+ // )).thenAnswer((_) async => tx1Raw);
+ //
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "351a94874379a5444c8891162472acf66de538a1abc647d4753f3e1eb5ec66f9",
+ // coin: Coin.bitcoincashTestNet,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx3Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "351a94874379a5444c8891162472acf66de538a1abc647d4753f3e1eb5ec66f9",
+ // coin: Coin.bitcoincashTestNet,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx3Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "4c119685401e28982283e644c57d84fde6aab83324012e35c9b49e6efd99b49b",
+ // coin: Coin.bitcoincashTestNet,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx1Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "4493caff0e1b4f248e3c6219e7f288cfdb46c32b72a77aec469098c5f7f5154e",
+ // coin: Coin.bitcoincashTestNet,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx5Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "e095cbe5531d174c3fc5c9c39a0e6ba2769489cdabdc17b35b2e3a33a3c2fc61",
+ // coin: Coin.bitcoincashTestNet,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx6Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "d3054c63fe8cfafcbf67064ec66b9fbe1ac293860b5d6ffaddd39546658b72de",
+ // coin: Coin.bitcoincashTestNet,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx7Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "7b34e60cc37306f866667deb67b14096f4ea2add941fd6e2238a639000642b82",
+ // coin: Coin.bitcoincashTestNet,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx4Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "a70c6f0690fa84712dc6b3d20ee13862fe015a08cf2dc8949c4300d49c3bdeb5",
+ // coin: Coin.bitcoincashTestNet,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx8Raw);
+ //
+ // bch = BitcoinCashWallet(
+ // walletId: testWalletId,
+ // walletName: testWalletName,
+ // coin: dtestcoin,
+ // client: client!,
+ // cachedClient: cachedClient!,
+ // priceAPI: priceAPI,
+ // secureStore: secureStore,
+ // );
+ // final wallet = await Hive.openBox(testWalletId);
+ // await wallet.put('receivingAddressesP2PKH', []);
+ //
+ // await wallet.put('changeAddressesP2PKH', []);
+ //
+ // bch?.unconfirmedTxs = {
+ // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
+ // };
+ //
+ // final result = await bch?.refreshIfThereIsNewData();
+ //
+ // expect(result, true);
+ //
+ // verify(client?.getBatchHistory(args: anyNamed("args"))).called(2);
+ // verify(client?.getTransaction(
+ // txHash:
+ // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
+ // )).called(1);
+ // verify(cachedClient?.getTransaction(
+ // txHash: anyNamed("tx_hash"),
+ // verbose: true,
+ // coin: Coin.bitcoincashTestNet,
+ // callOutSideMainIsolate: false))
+ // .called(9);
+ // // verify(priceAPI?.getbitcoincashPrice(baseCurrency: "USD")).called(1);
+ //
+ // expect(secureStore?.interactions, 0);
+ // verifyNoMoreInteractions(client);
+ // verifyNoMoreInteractions(cachedClient);
+ // verifyNoMoreInteractions(priceAPI);
+ // });
+
+ // test("refreshIfThereIsNewData false A", () async {
+ // // when(priceAPI.getbitcoincashPrice(baseCurrency: "USD"))
+ // // .thenAnswer((_) async => Decimal.fromInt(10));
+ //
+ // when(client?.getBatchHistory(args: anyNamed("args")))
+ // .thenAnswer((realInvocation) async {
+ // final uuids = Map>.from(realInvocation
+ // .namedArguments.values.first as Map)
+ // .keys
+ // .toList(growable: false);
+ // return {
+ // uuids[0]: [
+ // {
+ // "tx_hash":
+ // "351a94874379a5444c8891162472acf66de538a1abc647d4753f3e1eb5ec66f9",
+ // "height": 4286305
+ // },
+ // {
+ // "tx_hash":
+ // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
+ // "height": 4286295
+ // }
+ // ],
+ // uuids[1]: [
+ // {
+ // "tx_hash":
+ // "4c119685401e28982283e644c57d84fde6aab83324012e35c9b49e6efd99b49b",
+ // "height": 4286283
+ // }
+ // ],
+ // };
+ // });
+ //
+ // when(client?.getTransaction(
+ // txHash:
+ // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
+ // )).thenAnswer((_) async => tx2Raw);
+ // when(client?.getTransaction(
+ // txHash:
+ // "4c119685401e28982283e644c57d84fde6aab83324012e35c9b49e6efd99b49b",
+ // )).thenAnswer((_) async => tx1Raw);
+ //
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "4c119685401e28982283e644c57d84fde6aab83324012e35c9b49e6efd99b49b",
+ // coin: Coin.bitcoincashTestNet,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx1Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
+ // coin: Coin.bitcoincashTestNet,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx2Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "351a94874379a5444c8891162472acf66de538a1abc647d4753f3e1eb5ec66f9",
+ // coin: Coin.bitcoincashTestNet,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx3Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "4493caff0e1b4f248e3c6219e7f288cfdb46c32b72a77aec469098c5f7f5154e",
+ // coin: Coin.bitcoincashTestNet,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx5Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "7b34e60cc37306f866667deb67b14096f4ea2add941fd6e2238a639000642b82",
+ // coin: Coin.bitcoincashTestNet,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx4Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "e095cbe5531d174c3fc5c9c39a0e6ba2769489cdabdc17b35b2e3a33a3c2fc61",
+ // coin: Coin.bitcoincashTestNet,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx6Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "d3054c63fe8cfafcbf67064ec66b9fbe1ac293860b5d6ffaddd39546658b72de",
+ // coin: Coin.bitcoincashTestNet,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx7Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "a70c6f0690fa84712dc6b3d20ee13862fe015a08cf2dc8949c4300d49c3bdeb5",
+ // coin: Coin.bitcoincashTestNet,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx8Raw);
+ //
+ // bch = BitcoinCashWallet(
+ // walletId: testWalletId,
+ // walletName: testWalletName,
+ // coin: dtestcoin,
+ // client: client!,
+ // cachedClient: cachedClient!,
+ // tracker: tracker!,
+ // priceAPI: priceAPI,
+ // secureStore: secureStore,
+ // );
+ // final wallet = await Hive.openBox(testWalletId);
+ // await wallet.put('receivingAddressesP2PKH', []);
+ //
+ // await wallet.put('changeAddressesP2PKH', []);
+ //
+ // bch?.unconfirmedTxs = {
+ // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
+ // "351a94874379a5444c8891162472acf66de538a1abc647d4753f3e1eb5ec66f9"
+ // };
+ //
+ // final result = await bch?.refreshIfThereIsNewData();
+ //
+ // expect(result, false);
+ //
+ // verify(client?.getBatchHistory(args: anyNamed("args"))).called(2);
+ // verify(client?.getTransaction(
+ // txHash:
+ // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
+ // )).called(1);
+ // verify(cachedClient?.getTransaction(
+ // txHash: anyNamed("tx_hash"),
+ // verbose: true,
+ // coin: Coin.bitcoincashTestNet,
+ // callOutSideMainIsolate: false))
+ // .called(15);
+ // // verify(priceAPI.getbitcoincashPrice(baseCurrency: "USD")).called(1);
+ //
+ // expect(secureStore?.interactions, 0);
+ // verifyNoMoreInteractions(client);
+ // verifyNoMoreInteractions(cachedClient);
+ // verifyNoMoreInteractions(priceAPI);
+ // });
+
+ // // test("refreshIfThereIsNewData false B", () async {
+ // // when(client?.getBatchHistory(args: anyNamed("args")))
+ // // .thenThrow(Exception("some exception"));
+ // //
+ // // when(client?.getTransaction(
+ // // txHash:
+ // // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
+ // // )).thenAnswer((_) async => tx2Raw);
+ // //
+ // // bch = BitcoinCashWallet(
+ // // walletId: testWalletId,
+ // // walletName: testWalletName,
+ // // coin: dtestcoin,
+ // // client: client!,
+ // // cachedClient: cachedClient!,
+ // // tracker: tracker!,
+ // // priceAPI: priceAPI,
+ // // secureStore: secureStore,
+ // // );
+ // // final wallet = await Hive.openBox(testWalletId);
+ // // await wallet.put('receivingAddressesP2PKH', []);
+ // //
+ // // await wallet.put('changeAddressesP2PKH', []);
+ // //
+ // // bch?.unconfirmedTxs = {
+ // // "82da70c660daf4d42abd403795d047918c4021ff1d706b61790cda01a1c5ae5a",
+ // // };
+ // //
+ // // final result = await bch?.refreshIfThereIsNewData();
+ // //
+ // // expect(result, false);
+ // //
+ // // verify(client?.getBatchHistory(args: anyNamed("args"))).called(1);
+ // // verify(client?.getTransaction(
+ // // txHash:
+ // // "a4b6bd97a4b01b4305d0cf02e9bac6b7c37cda2f8e9dfe291ce4170b810ed469",
+ // // )).called(1);
+ // //
+ // // expect(secureStore?.interactions, 0);
+ // // verifyNoMoreInteractions(client);
+ // // verifyNoMoreInteractions(cachedClient);
+ // // verifyNoMoreInteractions(priceAPI);
+ // // });
+
+ test("get mnemonic list", () 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: anyNamed("args")))
+ // .thenAnswer((thing) async {
+ // print(jsonEncode(thing.namedArguments.entries.first.value));
+ // return {};
+ // });
+
+ when(client?.getBatchHistory(args: historyBatchArgs0))
+ .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);
+
+ final wallet = await Hive.openBox(testWalletId);
+
+ // add maxNumberOfIndexesToCheck and height
+ await bch?.recoverFromMnemonic(
+ mnemonic: TEST_MNEMONIC,
+ maxUnusedAddressGap: 2,
+ maxNumberOfIndexesToCheck: 1000,
+ height: 4000);
+
+ expect(await bch?.mnemonic, TEST_MNEMONIC.split(" "));
+ //
+ 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);
+
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test(
+ "recoverFromMnemonic using empty seed on mainnet fails due to bad genesis hash match",
+ () 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_TESTNET,
+ "hash_function": "sha256",
+ "services": []
+ });
+
+ bool hasThrown = false;
+ try {
+ await bch?.recoverFromMnemonic(
+ mnemonic: TEST_MNEMONIC,
+ maxUnusedAddressGap: 2,
+ maxNumberOfIndexesToCheck: 1000,
+ height: 4000);
+ } catch (_) {
+ hasThrown = true;
+ }
+ expect(hasThrown, true);
+
+ verify(client?.getServerFeatures()).called(1);
+
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test(
+ "recoverFromMnemonic using empty seed on testnet fails due to bad genesis hash match",
+ () async {
+ bch = BitcoinCashWallet(
+ walletId: testWalletId,
+ walletName: testWalletName,
+ coin: Coin.bitcoincashTestnet,
+ client: client!,
+ cachedClient: cachedClient!,
+ tracker: tracker!,
+ priceAPI: priceAPI,
+ secureStore: secureStore,
+ );
+ 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": []
+ });
+
+ bool hasThrown = false;
+ try {
+ await bch?.recoverFromMnemonic(
+ mnemonic: TEST_MNEMONIC,
+ maxUnusedAddressGap: 2,
+ maxNumberOfIndexesToCheck: 1000,
+ height: 4000);
+ } catch (_) {
+ hasThrown = true;
+ }
+ expect(hasThrown, true);
+
+ verify(client?.getServerFeatures()).called(1);
+
+ expect(secureStore?.interactions, 0);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test(
+ "recoverFromMnemonic using empty seed on mainnet fails due to attempted overwrite of mnemonic",
+ () 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": []
+ });
+
+ await secureStore?.write(
+ key: "${testWalletId}_mnemonic", value: "some mnemonic words");
+
+ bool hasThrown = false;
+ try {
+ await bch?.recoverFromMnemonic(
+ mnemonic: TEST_MNEMONIC,
+ maxUnusedAddressGap: 2,
+ maxNumberOfIndexesToCheck: 1000,
+ height: 4000);
+ } catch (_) {
+ hasThrown = true;
+ }
+ expect(hasThrown, true);
+
+ verify(client?.getServerFeatures()).called(1);
+
+ expect(secureStore?.interactions, 2);
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("recoverFromMnemonic using non empty seed on mainnet 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(client?.getBatchHistory(args: historyBatchArgs2))
+ .thenAnswer((_) async => historyBatchResponse);
+ when(client?.getBatchHistory(args: historyBatchArgs3))
+ .thenAnswer((_) async => historyBatchResponse);
+
+ List 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(testWalletId);
+ await Hive.openBox(testWalletId);
+
+ bool hasThrown = false;
+ try {
+ await bch?.recoverFromMnemonic(
+ mnemonic: TEST_MNEMONIC,
+ maxUnusedAddressGap: 2,
+ maxNumberOfIndexesToCheck: 1000,
+ height: 4000);
+ } catch (_) {
+ hasThrown = true;
+ }
+ expect(hasThrown, false);
+
+ 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);
+
+ for (final arg in dynamicArgValues) {
+ final map = Map>.from(arg as Map);
+
+ verify(client?.getBatchHistory(args: map)).called(1);
+ expect(activeScriptHashes.contains(map.values.first.first as String),
+ true);
+ }
+
+ expect(secureStore?.interactions, 10);
+ expect(secureStore?.writes, 5);
+ expect(secureStore?.reads, 5);
+ expect(secureStore?.deletes, 0);
+
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ 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(client?.getBatchHistory(args: historyBatchArgs2))
+ .thenAnswer((_) async => historyBatchResponse);
+ when(client?.getBatchHistory(args: historyBatchArgs3))
+ .thenAnswer((_) async => historyBatchResponse);
+ when(cachedClient?.clearSharedTransactionCache(coin: Coin.bitcoincash))
+ .thenAnswer((realInvocation) async {});
+
+ when(client?.getBatchHistory(args: {
+ "0": [
+ "04818da846fe5e03ac993d2e0c1ccc3848ff6073c3aba6a572df4efc5432ae8b"
+ ]
+ })).thenAnswer((_) async => {"0": []});
+ when(client?.getBatchHistory(args: {
+ "0": [
+ "f0c86f888f2aca0efaf1705247dbd1ebc02347c183e197310c9062ea2c9d2e34"
+ ]
+ })).thenAnswer((_) async => {"0": []});
+ when(client?.getBatchHistory(args: {
+ "0": [
+ "ff7f0d2a4b8e2805706ece77f4e672550fe4c505a150c781639814338eda1734"
+ ]
+ })).thenAnswer((_) async => {"0": []});
+ when(client?.getBatchHistory(args: {
+ "0": [
+ "1c2336c32dc62f00862ee6a75643e01017c86edece10b5a9d7defbd5f66b0a80"
+ ]
+ })).thenAnswer((_) async => {"0": []});
+
+ final wallet = await Hive.openBox(testWalletId);
+
+ // restore so we have something to rescan
+ await bch?.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 preReceivingAddressesP2SH =
+ await wallet.get('receivingAddressesP2SH');
+ final preChangeAddressesP2SH = await wallet.get('changeAddressesP2SH');
+ final preReceivingIndexP2SH = await wallet.get('receivingIndexP2PKH');
+ final preChangeIndexP2SH = await wallet.get('changeIndexP2SH');
+
+ final preUtxoData = await wallet.get('latest_utxo_model');
+ final preReceiveDerivationsStringP2PKH = await secureStore?.read(
+ key: "${testWalletId}_receiveDerivationsP2PKH");
+ final preChangeDerivationsStringP2PKH = await secureStore?.read(
+ key: "${testWalletId}_changeDerivationsP2PKH");
+
+ final preReceiveDerivationsStringP2SH = await secureStore?.read(
+ key: "${testWalletId}_receiveDerivationsP2SH");
+ final preChangeDerivationsStringP2SH =
+ await secureStore?.read(key: "${testWalletId}_changeDerivationsP2SH");
+
+ // 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(
+ 'receivingAddressesP2SH', ["some address", "some other address"]);
+ await wallet
+ .put('changeAddressesP2SH', ["some address", "some other address"]);
+
+ await wallet.put('receivingIndexP2PKH', 123);
+ await wallet.put('changeIndexP2PKH', 123);
+
+ await wallet.put('receivingIndexP2SH', 123);
+ await wallet.put('changeIndexP2SH', 123);
+
+ await secureStore?.write(
+ key: "${testWalletId}_receiveDerivationsP2PKH", value: "{}");
+ await secureStore?.write(
+ key: "${testWalletId}_changeDerivationsP2PKH", value: "{}");
+
+ await secureStore?.write(
+ key: "${testWalletId}_receiveDerivationsP2SH", value: "{}");
+ await secureStore?.write(
+ key: "${testWalletId}_changeDerivationsP2SH", value: "{}");
+
+ bool hasThrown = false;
+ try {
+ await bch?.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 receivingAddressesP2SH = await wallet.get('receivingAddressesP2SH');
+ final changeAddressesP2SH = await wallet.get('changeAddressesP2SH');
+ final receivingIndexP2SH = await wallet.get('receivingIndexP2SH');
+ final changeIndexP2SH = await wallet.get('changeIndexP2SH');
+
+ final utxoData = await wallet.get('latest_utxo_model');
+ final receiveDerivationsStringP2PKH = await secureStore?.read(
+ key: "${testWalletId}_receiveDerivationsP2PKH");
+ final changeDerivationsStringP2PKH = await secureStore?.read(
+ key: "${testWalletId}_changeDerivationsP2PKH");
+
+ final receiveDerivationsStringP2SH = await secureStore?.read(
+ key: "${testWalletId}_receiveDerivationsP2SH");
+ final changeDerivationsStringP2SH =
+ await secureStore?.read(key: "${testWalletId}_changeDerivationsP2SH");
+
+ expect(preReceivingAddressesP2PKH, receivingAddressesP2PKH);
+ expect(preChangeAddressesP2PKH, changeAddressesP2PKH);
+ expect(preReceivingIndexP2PKH, receivingIndexP2PKH);
+ expect(preChangeIndexP2PKH, changeIndexP2PKH);
+
+ expect(preReceivingAddressesP2SH, receivingAddressesP2SH);
+ expect(preChangeAddressesP2SH, changeAddressesP2SH);
+ expect(preReceivingIndexP2SH, receivingIndexP2SH);
+ expect(preChangeIndexP2SH, changeIndexP2SH);
+
+ expect(preUtxoData, utxoData);
+
+ expect(preReceiveDerivationsStringP2PKH, receiveDerivationsStringP2PKH);
+ expect(preChangeDerivationsStringP2PKH, changeDerivationsStringP2PKH);
+
+ expect(preReceiveDerivationsStringP2SH, receiveDerivationsStringP2SH);
+ expect(preChangeDerivationsStringP2SH, changeDerivationsStringP2SH);
+
+ 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(cachedClient?.clearSharedTransactionCache(coin: Coin.bitcoincash))
+ .called(1);
+
+ verify(client?.getBatchHistory(args: {
+ "0": [
+ "04818da846fe5e03ac993d2e0c1ccc3848ff6073c3aba6a572df4efc5432ae8b"
+ ]
+ })).called(2);
+ verify(client?.getBatchHistory(args: {
+ "0": [
+ "f0c86f888f2aca0efaf1705247dbd1ebc02347c183e197310c9062ea2c9d2e34"
+ ]
+ })).called(2);
+ verify(client?.getBatchHistory(args: {
+ "0": [
+ "ff7f0d2a4b8e2805706ece77f4e672550fe4c505a150c781639814338eda1734"
+ ]
+ })).called(2);
+ verify(client?.getBatchHistory(args: {
+ "0": [
+ "1c2336c32dc62f00862ee6a75643e01017c86edece10b5a9d7defbd5f66b0a80"
+ ]
+ })).called(2);
+
+ expect(secureStore?.writes, 17);
+ expect(secureStore?.reads, 22);
+ expect(secureStore?.deletes, 4);
+
+ verifyNoMoreInteractions(client);
+ verifyNoMoreInteractions(cachedClient);
+ verifyNoMoreInteractions(tracker);
+ verifyNoMoreInteractions(priceAPI);
+ });
+
+ test("fullRescan fails", () 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(client?.getBatchHistory(args: historyBatchArgs2))
+ .thenAnswer((_) async => historyBatchResponse);
+ when(client?.getBatchHistory(args: historyBatchArgs3))
+ .thenAnswer((_) async => historyBatchResponse);
+ when(cachedClient?.clearSharedTransactionCache(coin: Coin.bitcoincash))
+ .thenAnswer((realInvocation) async {});
+
+ when(client?.getBatchHistory(args: {
+ "0": [
+ "04818da846fe5e03ac993d2e0c1ccc3848ff6073c3aba6a572df4efc5432ae8b"
+ ]
+ })).thenAnswer((realInvocation) async => {"0": []});
+ when(client?.getBatchHistory(args: {
+ "0": [
+ "f0c86f888f2aca0efaf1705247dbd1ebc02347c183e197310c9062ea2c9d2e34"
+ ]
+ })).thenAnswer((_) async => {"0": []});
+ when(client?.getBatchHistory(args: {
+ "0": [
+ "ff7f0d2a4b8e2805706ece77f4e672550fe4c505a150c781639814338eda1734"
+ ]
+ })).thenAnswer((_) async => {"0": []});
+ when(client?.getBatchHistory(args: {
+ "0": [
+ "1c2336c32dc62f00862ee6a75643e01017c86edece10b5a9d7defbd5f66b0a80"
+ ]
+ })).thenAnswer((_) async => {"0": []});
+
+ final wallet = await Hive.openBox(testWalletId);
+
+ // restore so we have something to rescan
+ await bch?.recoverFromMnemonic(
+ mnemonic: TEST_MNEMONIC,
+ maxUnusedAddressGap: 2,
+ maxNumberOfIndexesToCheck: 1000,
+ height: 4000);
+
+ // fetch 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");
+
+ when(client?.getBatchHistory(args: historyBatchArgs0))
+ .thenThrow(Exception("fake exception"));
+
+ bool hasThrown = false;
+ try {
+ await bch?.fullRescan(2, 1000);
+ } catch (_) {
+ hasThrown = true;
+ }
+ expect(hasThrown, true);
+
+ // 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(client?.getBatchHistory(args: historyBatchArgs2)).called(2);
+ verify(client?.getBatchHistory(args: historyBatchArgs3)).called(2);
+ verify(cachedClient?.clearSharedTransactionCache(coin: Coin.bitcoincash))
+ .called(1);
+
+ verify(client?.getBatchHistory(args: {
+ "0": [
+ "04818da846fe5e03ac993d2e0c1ccc3848ff6073c3aba6a572df4efc5432ae8b"
+ ]
+ })).called(1);
+ verify(client?.getBatchHistory(args: {
+ "0": [
+ "f0c86f888f2aca0efaf1705247dbd1ebc02347c183e197310c9062ea2c9d2e34"
+ ]
+ })).called(2);
+ verify(client?.getBatchHistory(args: {
+ "0": [
+ "ff7f0d2a4b8e2805706ece77f4e672550fe4c505a150c781639814338eda1734"
+ ]
+ })).called(2);
+ verify(client?.getBatchHistory(args: {
+ "0": [
+ "1c2336c32dc62f00862ee6a75643e01017c86edece10b5a9d7defbd5f66b0a80"
+ ]
+ })).called(2);
+
+ expect(secureStore?.writes, 13);
+ expect(secureStore?.reads, 18);
+ expect(secureStore?.deletes, 8);
+ });
+
+ // // test("fetchBuildTxData 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.getTransaction(
+ // // tx_hash:
+ // // "339dac760e4c9c81ed30a7fde7062785cb20712b18e108accdc39800f884fda9",
+ // // coinName: "bitcoincash",
+ // // callOutSideMainIsolate: false))
+ // // .thenAnswer((_) async => tx9Raw);
+ // // when(cachedClient.getTransaction(
+ // // tx_hash:
+ // // "c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e",
+ // // coinName: "bitcoincash",
+ // // callOutSideMainIsolate: false))
+ // // .thenAnswer((_) async => tx10Raw);
+ // // when(cachedClient.getTransaction(
+ // // tx_hash:
+ // // "d0c451513bee7d96cb88824d9d720e6b5b90073721b4985b439687f894c3989c",
+ // // coinName: "bitcoincash",
+ // // callOutSideMainIsolate: false))
+ // // .thenAnswer((_) async => tx11Raw);
+ // //
+ // // // recover to fill data
+ // // await bch.recoverFromMnemonic(
+ // // mnemonic: TEST_MNEMONIC,
+ // // maxUnusedAddressGap: 2,
+ // // maxNumberOfIndexesToCheck: 1000,
+ // // height: 4000);
+ // //
+ // // // modify addresses to trigger all change code branches
+ // // final chg44 =
+ // // await secureStore.read(key: testWalletId + "_changeDerivationsP2PKH");
+ // // await secureStore.write(
+ // // key: testWalletId + "_changeDerivationsP2PKH",
+ // // value: chg44.replaceFirst("1vFHF5q21GccoBwrB4zEUAs9i3Bfx797U",
+ // // "D5cQWPnhM3RRJVDz8wWC5jWt3PRCfg1zA6"));
+ // //
+ // // final data = await bch.fetchBuildTxData(utxoList);
+ // //
+ // // expect(data.length, 3);
+ // // expect(
+ // // data["339dac760e4c9c81ed30a7fde7062785cb20712b18e108accdc39800f884fda9"]
+ // // .length,
+ // // 2);
+ // // expect(
+ // // data["c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e"]
+ // // .length,
+ // // 3);
+ // // expect(
+ // // data["d0c451513bee7d96cb88824d9d720e6b5b90073721b4985b439687f894c3989c"]
+ // // .length,
+ // // 2);
+ // // expect(
+ // // data["339dac760e4c9c81ed30a7fde7062785cb20712b18e108accdc39800f884fda9"]
+ // // ["output"],
+ // // isA());
+ // // expect(
+ // // data["c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e"]
+ // // ["output"],
+ // // isA());
+ // // expect(
+ // // data["d0c451513bee7d96cb88824d9d720e6b5b90073721b4985b439687f894c3989c"]
+ // // ["output"],
+ // // isA());
+ // // expect(
+ // // data["339dac760e4c9c81ed30a7fde7062785cb20712b18e108accdc39800f884fda9"]
+ // // ["keyPair"],
+ // // isA());
+ // // expect(
+ // // data["c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e"]
+ // // ["keyPair"],
+ // // isA());
+ // // expect(
+ // // data["d0c451513bee7d96cb88824d9d720e6b5b90073721b4985b439687f894c3989c"]
+ // // ["keyPair"],
+ // // isA());
+ // // expect(
+ // // data["c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e"]
+ // // ["redeemScript"],
+ // // isA());
+ // //
+ // // // modify addresses to trigger all receiving code branches
+ // // final rcv44 = await secureStore.read(
+ // // key: testWalletId + "_receiveDerivationsP2PKH");
+ // // await secureStore.write(
+ // // key: testWalletId + "_receiveDerivationsP2PKH",
+ // // value: rcv44.replaceFirst("1RMSPixoLPuaXuhR2v4HsUMcRjLncKDaw",
+ // // "D5cQWPnhM3RRJVDz8wWC5jWt3PRCfg1zA6"));
+ // //
+ // // final data2 = await bch.fetchBuildTxData(utxoList);
+ // //
+ // // expect(data2.length, 3);
+ // // expect(
+ // // data2["339dac760e4c9c81ed30a7fde7062785cb20712b18e108accdc39800f884fda9"]
+ // // .length,
+ // // 2);
+ // // expect(
+ // // data2["c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e"]
+ // // .length,
+ // // 3);
+ // // expect(
+ // // data2["d0c451513bee7d96cb88824d9d720e6b5b90073721b4985b439687f894c3989c"]
+ // // .length,
+ // // 2);
+ // // expect(
+ // // data2["339dac760e4c9c81ed30a7fde7062785cb20712b18e108accdc39800f884fda9"]
+ // // ["output"],
+ // // isA());
+ // // expect(
+ // // data2["c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e"]
+ // // ["output"],
+ // // isA());
+ // // expect(
+ // // data2["d0c451513bee7d96cb88824d9d720e6b5b90073721b4985b439687f894c3989c"]
+ // // ["output"],
+ // // isA());
+ // // expect(
+ // // data2["339dac760e4c9c81ed30a7fde7062785cb20712b18e108accdc39800f884fda9"]
+ // // ["keyPair"],
+ // // isA());
+ // // expect(
+ // // data2["c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e"]
+ // // ["keyPair"],
+ // // isA());
+ // // expect(
+ // // data2["d0c451513bee7d96cb88824d9d720e6b5b90073721b4985b439687f894c3989c"]
+ // // ["keyPair"],
+ // // isA());
+ // // expect(
+ // // data2["c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e"]
+ // // ["redeemScript"],
+ // // isA());
+ // //
+ // // verify(client.getServerFeatures()).called(1);
+ // // verify(cachedClient.getTransaction(
+ // // tx_hash:
+ // // "339dac760e4c9c81ed30a7fde7062785cb20712b18e108accdc39800f884fda9",
+ // // coinName: "bitcoincash",
+ // // callOutSideMainIsolate: false))
+ // // .called(2);
+ // // verify(cachedClient.getTransaction(
+ // // tx_hash:
+ // // "c2edf283df75cc2724320b866857a82d80266a59d69ab5a7ca12033adbffa44e",
+ // // coinName: "bitcoincash",
+ // // callOutSideMainIsolate: false))
+ // // .called(2);
+ // // verify(cachedClient.getTransaction(
+ // // tx_hash:
+ // // "d0c451513bee7d96cb88824d9d720e6b5b90073721b4985b439687f894c3989c",
+ // // coinName: "bitcoincash",
+ // // callOutSideMainIsolate: false))
+ // // .called(2);
+ // // verify(client.getBatchHistory(args: historyBatchArgs0)).called(1);
+ // // verify(client.getBatchHistory(args: historyBatchArgs1)).called(1);
+ // //
+ // // expect(secureStore.interactions, 38);
+ // // expect(secureStore.writes, 13);
+ // // expect(secureStore.reads, 25);
+ // // expect(secureStore.deletes, 0);
+ // //
+ // // verifyNoMoreInteractions(client);
+ // // verifyNoMoreInteractions(cachedClient);
+ // // verifyNoMoreInteractions(priceAPI);
+ // // });
+
+ // test("fetchBuildTxData throws", () 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?.getTransaction(
+ // txHash:
+ // "2087ce09bc316877c9f10971526a2bffa3078d52ea31752639305cdcd8230703",
+ // coin: Coin.bitcoincash,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx9Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "ed32c967a0e86d51669ac21c2bb9bc9c50f0f55fbacdd8db21d0a986fba93bd7",
+ // coin: Coin.bitcoincash,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx10Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "3f0032f89ac44b281b50314cff3874c969c922839dddab77ced54e86a21c3fd4",
+ // coin: Coin.bitcoincash,
+ // callOutSideMainIsolate: false))
+ // .thenThrow(Exception("some exception"));
+ //
+ // // recover to fill data
+ // await bch?.recoverFromMnemonic(
+ // mnemonic: TEST_MNEMONIC,
+ // maxUnusedAddressGap: 2,
+ // maxNumberOfIndexesToCheck: 1000,
+ // height: 4000);
+ //
+ // bool didThrow = false;
+ // try {
+ // await bch?.fetchBuildTxData(utxoList);
+ // } catch (_) {
+ // didThrow = true;
+ // }
+ // expect(didThrow, true);
+ //
+ // verify(client?.getServerFeatures()).called(1);
+ // verify(cachedClient?.getTransaction(
+ // txHash:
+ // "2087ce09bc316877c9f10971526a2bffa3078d52ea31752639305cdcd8230703",
+ // coin: Coin.bitcoincash,
+ // callOutSideMainIsolate: false))
+ // .called(1);
+ // verify(cachedClient?.getTransaction(
+ // txHash:
+ // "ed32c967a0e86d51669ac21c2bb9bc9c50f0f55fbacdd8db21d0a986fba93bd7",
+ // coin: Coin.bitcoincash,
+ // callOutSideMainIsolate: false))
+ // .called(1);
+ // verify(cachedClient?.getTransaction(
+ // txHash:
+ // "3f0032f89ac44b281b50314cff3874c969c922839dddab77ced54e86a21c3fd4",
+ // coin: Coin.bitcoincash,
+ // callOutSideMainIsolate: false))
+ // .called(1);
+ // verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1);
+ // verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1);
+ //
+ // expect(secureStore?.interactions, 14);
+ // expect(secureStore?.writes, 7);
+ // expect(secureStore?.reads, 7);
+ // expect(secureStore?.deletes, 0);
+ //
+ // verifyNoMoreInteractions(client);
+ // verifyNoMoreInteractions(cachedClient);
+ // verifyNoMoreInteractions(priceAPI);
+ // });
+
+ // test("build transaction 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?.getTransaction(
+ // txHash:
+ // "e9673acb3bfa928f92a7d5a545151a672e9613fdf972f3849e16094c1ed28268",
+ // coin: Coin.bitcoincash,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx9Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "fa5bfa4eb581bedb28ca96a65ee77d8e81159914b70d5b7e215994221cc02a63",
+ // coin: Coin.bitcoincash,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx10Raw);
+ // when(cachedClient?.getTransaction(
+ // txHash:
+ // "694617f0000499be2f6af5f8d1ddbcf1a70ad4710c0cee6f33a13a64bba454ed",
+ // coin: Coin.bitcoincash,
+ // callOutSideMainIsolate: false))
+ // .thenAnswer((_) async => tx11Raw);
+ //
+ // // recover to fill data
+ // await bch?.recoverFromMnemonic(
+ // mnemonic: TEST_MNEMONIC,
+ // maxUnusedAddressGap: 2,
+ // maxNumberOfIndexesToCheck: 1000,
+ // height: 4000);
+ //
+ // // modify addresses to properly mock data to build a tx
+ // final rcv44 = await secureStore?.read(
+ // key: testWalletId + "_receiveDerivationsP2PKH");
+ // await secureStore?.write(
+ // key: testWalletId + "_receiveDerivationsP2PKH",
+ // value: rcv44?.replaceFirst("1RMSPixoLPuaXuhR2v4HsUMcRjLncKDaw",
+ // "D5cQWPnhM3RRJVDz8wWC5jWt3PRCfg1zA6"));
+ //
+ // final data = await bch?.fetchBuildTxData(utxoList);
+ //
+ // final txData = await bch?.buildTransaction(
+ // utxosToUse: utxoList,
+ // utxoSigningData: data!,
+ // recipients: ["DS7cKFKdfbarMrYjFBQqEcHR5km6D51c74"],
+ // satoshiAmounts: [13000]);
+ //
+ // expect(txData?.length, 2);
+ // expect(txData?["hex"], isA