ci: port tests branch CI fixes to vacay
What changed, and why it matters
This commit is almost entirely about making the automated test suite run reliably in CI. It swaps a PowerShell CI workflow for a bash one, adds test helpers and stubs, updates many generated mock files, and makes a small code change so the logger does not call itself forever when it fails. There is no obvious security vulnerability being introduced; the main risk is that a real bug in the logger could now be silently swallowed instead of reported.
No immediate security action required. Review the logger swallow change to ensure production log failures are still observable through other channels (e.g., stderr, crash reporting). Verify the CI secret handling and stub generation do not leak real keys or write unexpected files in release builds.
Security signals we found
Logger error path changed from recursive logging to silent swallowing
CI workflow now decodes only CHANGE_NOW secret; other per-coin test parameter secrets removed from workflow
New test stub script writes a generated app_config.g.dart if missing
test_node_connection refactored to use a Reader instead of WidgetRef for test overrides
Evidence from the diff
The diff is dominated by CI/test infrastructure: replacing the GitHub Actions test.yaml PowerShell steps with bash equivalents, adding scripts/ensure_test_app_config.sh to stub app_config.g.dart, adding hive_ce_test_utils.dart and mock_electrum_server.dart, regenerating mockito mocks, and updating test files. The only production code changes are in lib/utilities/logger.dart (catch block now swallows dispatch errors instead of recursively calling t()), lib/utilities/test_node_connection.dart (wraps testNodeConnection in a provider/typedef and replaces WidgetRef with a Reader for testability), and four UI call sites updated to use the new provider. Dependency pins in pubspec.lock (dart_style 3.1.3, analyzer <8.4.0) and a bitcoindart override are build/test tooling changes, not runtime security changes.
Changed components
.github/workflows/test.yamllib/utilities/logger.dartlib/utilities/test_node_connection.dartlib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dartlib/pages/settings_views/global_settings_view/manage_nodes_views/node_details_view.dartlib/widgets/node_card.dartlib/widgets/node_options_sheet.dartpubspec.lockscripts/ensure_test_app_config.shInspect captured patch +6727 / −7225
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index ee3052d..871d1e7 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -6,7 +6,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Prepare repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install Flutter
@@ -25,9 +25,10 @@ jobs:
- name: install dependencies
run: |
cargo install cargo-ndk
- rustup target add x86_64-unknown-linux-gnu
+ rustup install 1.85.1 1.89.0
+ rustup target add x86_64-unknown-linux-gnu --toolchain 1.89.0
sudo apt update
- sudo apt install -y meson ninja-build libglib2.0-dev libgcrypt20-dev libgirepository1.0-dev unzip automake build-essential file pkg-config git python3 libtool cmake openjdk-8-jre-headless libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev llvm debhelper libclang-dev opencl-headers libssl-dev ocl-icd-opencl-dev libc6-dev-i386
+ sudo apt install -y meson ninja-build libglib2.0-dev libgcrypt20-dev libgirepository1.0-dev unzip automake build-essential file pkg-config git python3 libtool cmake openjdk-8-jre-headless libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev llvm debhelper libclang-dev opencl-headers libssl-dev ocl-icd-opencl-dev libc6-dev-i386 valac libtss2-dev
# - name: Build Epic Cash
#run: |
#cd crypto_plugins/flutter_libepiccash/scripts/linux/
@@ -36,65 +37,39 @@ jobs:
- name: Configure app
run: |
cd scripts
- yes yes | ./build_app.sh -v "0.0.1" -b "1" -p "linux" -a "stack_wallet" || true
+ echo "yes" | ./build_app.sh -v "0.0.1" -b "1" -p "linux" -a "stack_wallet"
- name: Get dependencies
run: flutter pub get
- - name: Create temp files
- id: secret-file1
+ - name: Create git_versions.dart stubs
run: |
- $secretFileExchange = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "lib/external_api_keys.dart";
- $encodedBytes = [System.Convert]::FromBase64String($env:CHANGE_NOW);
- Set-Content $secretFileExchange -Value $encodedBytes -AsByteStream;
- $secretFileExchangeHash = Get-FileHash $secretFileExchange;
- Write-Output "Secret file $secretFileExchange has hash $($secretFileExchangeHash.Hash)";
-
- $secretFileBitcoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/bitcoin/bitcoin_wallet_test_parameters.dart";
- $encodedBytes = [System.Convert]::FromBase64String($env:BITCOIN_TEST);
- Set-Content $secretFileBitcoin -Value $encodedBytes -AsByteStream;
- $secretFileBitcoinHash = Get-FileHash $secretFileBitcoin;
- Write-Output "Secret file $secretFileBitcoin has hash $($secretFileBitcoinHash.Hash)";
-
- $secretFileDogecoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/dogecoin/dogecoin_wallet_test_parameters.dart";
- $encodedBytes = [System.Convert]::FromBase64String($env:DOGECOIN_TEST);
- Set-Content $secretFileDogecoin -Value $encodedBytes -AsByteStream;
- $secretFileDogecoinHash = Get-FileHash $secretFileDogecoin;
- Write-Output "Secret file $secretFileDogecoin has hash $($secretFileDogecoinHash.Hash)";
-
- $secretFileFiro = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/firo/firo_wallet_test_parameters.dart";
- $encodedBytes = [System.Convert]::FromBase64String($env:FIRO_TEST);
- Set-Content $secretFileFiro -Value $encodedBytes -AsByteStream;
- $secretFileFiroHash = Get-FileHash $secretFileFiro;
- Write-Output "Secret file $secretFileFiro has hash $($secretFileFiroHash.Hash)";
-
- $secretFileBitcoinCash = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/bitcoincash/bitcoincash_wallet_test_parameters.dart";
- $encodedBytes = [System.Convert]::FromBase64String($env:BITCOINCASH_TEST);
- Set-Content $secretFileBitcoinCash -Value $encodedBytes -AsByteStream;
- $secretFileBitcoinCashHash = Get-FileHash $secretFileBitcoinCash;
- Write-Output "Secret file $secretFileBitcoinCash has hash $($secretFileBitcoinCashHash.Hash)";
-
- $secretFileNamecoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/namecoin/namecoin_wallet_test_parameters.dart";
- $encodedBytes = [System.Convert]::FromBase64String($env:NAMECOIN_TEST);
- Set-Content $secretFileNamecoin -Value $encodedBytes -AsByteStream;
- $secretFileNamecoinHash = Get-FileHash $secretFileNamecoin;
- Write-Output "Secret file $secretFileNamecoin has hash $($secretFileNamecoinHash.Hash)";
-
- $secretFileParticl = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/particl/particl_wallet_test_parameters.dart";
- $encodedBytes = [System.Convert]::FromBase64String($env:PARTICL_TEST);
- Set-Content $secretFileParticl -Value $encodedBytes -AsByteStream;
- $secretFileParticlHash = Get-FileHash $secretFileParticl;
- Write-Output "Secret file $secretFileParticl has hash $($secretFileParticlHash.Hash)";
-
- shell: pwsh
+ mkdir -p crypto_plugins/flutter_libepiccash/lib
+ mkdir -p crypto_plugins/flutter_libmwc/lib
+
+ cat > crypto_plugins/flutter_libepiccash/lib/git_versions.dart << 'EOF'
+ String getPluginVersion() => "stub-for-tests";
+ EOF
+
+ cat > crypto_plugins/flutter_libmwc/lib/git_versions.dart << 'EOF'
+ String getPluginVersion() => "stub-for-tests";
+ EOF
+
+ - name: Decode secrets
env:
CHANGE_NOW: ${{ secrets.CHANGE_NOW }}
- BITCOIN_TEST: ${{ secrets.BITCOIN_TEST }}
- DOGECOIN_TEST: ${{ secrets.DOGECOIN_TEST }}
- FIRO_TEST: ${{ secrets.FIRO_TEST }}
- BITCOINCASH_TEST: ${{ secrets.BITCOINCASH_TEST }}
- NAMECOIN_TEST: ${{ secrets.NAMECOIN_TEST }}
- PARTICL_TEST: ${{ secrets.PARTICL_TEST }}
+ run: |
+ echo "$CHANGE_NOW" | base64 --decode > lib/external_api_keys.dart
+
+ - name: Ensure app config for tests
+ run: bash scripts/ensure_test_app_config.sh
+
+ - name: Create test stubs
+ run: bash prebuild.sh
+ working-directory: scripts
+
+ - name: Regenerate mocks
+ run: dart run build_runner build --delete-conflicting-outputs
- name: Check formatting of changed files
run: |
@@ -114,29 +89,14 @@ jobs:
# - name: Analyze
# run: flutter analyze
- name: Test
- run: flutter test --coverage
+ run: |
+ bash scripts/ensure_test_app_config.sh
+ test -s lib/app_config.g.dart
+ grep -Fq "part of 'app_config.dart';" lib/app_config.g.dart
+ flutter test --coverage
- name: Upload to code coverage
uses: codecov/codecov-action@v1.2.2
if: success() || failure()
with:
token: ${{secrets.CODECOV_TOKEN}}
file: coverage/lcov.info
- - name: Delete temp files
- run: |
- $secretFileExchange = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "lib/external_api_keys.dart";
- $secretFileBitcoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/bitcoin/bitcoin_wallet_test_parameters.dart";
- $secretFileDogecoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/dogecoin/dogecoin_wallet_test_parameters.dart";
- $secretFileFiro = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/firo/firo_wallet_test_parameters.dart";
- $secretFileBitcoinCash = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/bitcoincash/bitcoincash_wallet_test_parameters.dart";
- $secretFileNamecoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/namecoin/namecoin_wallet_test_parameters.dart";
- $secretFileParticl = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/particl/particl_wallet_test_parameters.dart";
-
- Remove-Item -Path $secretFileExchange;
- Remove-Item -Path $secretFileBitcoin;
- Remove-Item -Path $secretFileDogecoin;
- Remove-Item -Path $secretFileFiro;
- Remove-Item -Path $secretFileBitcoinCash;
- Remove-Item -Path $secretFileNamecoin;
- Remove-Item -Path $secretFileParticl;
- shell: pwsh
- if: always()
diff --git a/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart b/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart
index 75411e9..bd19059 100644
--- a/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart
+++ b/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart
@@ -97,12 +97,11 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
}
Future<void> attemptSave() async {
- final canConnect = await testNodeConnection(
+ final canConnect = await ref.read(testNodeConnectionProvider)(
context: context,
onSuccess: _onTestSuccess,
cryptoCurrency: coin,
nodeFormData: ref.read(nodeFormDataProvider),
- ref: ref,
);
bool? shouldSave;
@@ -688,12 +687,13 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
buttonHeight: isDesktop ? ButtonHeight.l : null,
onPressed: testConnectionEnabled
? () async {
- final testPassed = await testNodeConnection(
+ final testPassed = await ref.read(
+ testNodeConnectionProvider,
+ )(
context: context,
onSuccess: _onTestSuccess,
cryptoCurrency: coin,
nodeFormData: ref.read(nodeFormDataProvider),
- ref: ref,
);
if (context.mounted) {
if (testPassed) {
diff --git a/lib/pages/settings_views/global_settings_view/manage_nodes_views/node_details_view.dart b/lib/pages/settings_views/global_settings_view/manage_nodes_views/node_details_view.dart
index 481c390..b87f9fc 100644
--- a/lib/pages/settings_views/global_settings_view/manage_nodes_views/node_details_view.dart
+++ b/lib/pages/settings_views/global_settings_view/manage_nodes_views/node_details_view.dart
@@ -323,11 +323,12 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
);
if (context.mounted) {
- final testPassed = await testNodeConnection(
+ final testPassed = await ref.read(
+ testNodeConnectionProvider,
+ )(
context: context,
nodeFormData: nodeFormData,
cryptoCurrency: coin,
- ref: ref,
);
if (testPassed) {
diff --git a/lib/utilities/logger.dart b/lib/utilities/logger.dart
index b5ae5f0..44c537c 100644
--- a/lib/utilities/logger.dart
+++ b/lib/utilities/logger.dart
@@ -137,8 +137,8 @@ class Logging {
),
toFile,
));
- } catch (e, s) {
- t("Isolates suck", error: e, stackTrace: s);
+ } catch (_) {
+ // swallow: logger not initialized (e.g. tests); avoid recursive logging
}
}
diff --git a/lib/utilities/test_node_connection.dart b/lib/utilities/test_node_connection.dart
index c6b5ee0..1a94e23 100644
--- a/lib/utilities/test_node_connection.dart
+++ b/lib/utilities/test_node_connection.dart
@@ -29,6 +29,31 @@ import 'test_mwcmqs_connection.dart';
import 'test_stellar_node_connection.dart';
import 'tor_plain_net_option_enum.dart';
+typedef TestNodeConnectionCallback =
+ Future<bool> Function({
+ required BuildContext context,
+ required NodeFormData nodeFormData,
+ required CryptoCurrency cryptoCurrency,
+ void Function(NodeFormData)? onSuccess,
+ });
+
+final testNodeConnectionProvider = Provider<TestNodeConnectionCallback>((ref) {
+ return ({
+ required BuildContext context,
+ required NodeFormData nodeFormData,
+ required CryptoCurrency cryptoCurrency,
+ void Function(NodeFormData)? onSuccess,
+ }) {
+ return testNodeConnection(
+ context: context,
+ nodeFormData: nodeFormData,
+ cryptoCurrency: cryptoCurrency,
+ read: ref.read,
+ onSuccess: onSuccess,
+ );
+ };
+});
+
Future<bool> _xmrHelper(
NodeFormData nodeFormData,
BuildContext context,
@@ -93,12 +118,12 @@ Future<bool> testNodeConnection({
required BuildContext context,
required NodeFormData nodeFormData,
required CryptoCurrency cryptoCurrency,
- required WidgetRef ref,
+ required Reader read,
void Function(NodeFormData)? onSuccess,
}) async {
final formData = nodeFormData;
- if (ref.read(prefsChangeNotifierProvider).useTor) {
+ if (read(prefsChangeNotifierProvider).useTor) {
if (formData.netOption! == TorPlainNetworkOption.clear) {
Logging.instance.w(
"This node is configured for non-TOR only but TOR is enabled",
@@ -147,8 +172,8 @@ Future<bool> testNodeConnection({
try {
final proxyInfo = !AppConfig.hasFeature(AppFeature.tor)
? null
- : ref.read(prefsChangeNotifierProvider).useTor
- ? ref.read(pTorService).getProxyInfo()
+ : read(prefsChangeNotifierProvider).useTor
+ ? read(pTorService).getProxyInfo()
: null;
final url = formData.host!;
@@ -200,8 +225,8 @@ Future<bool> testNodeConnection({
host: formData.host!,
port: formData.port!,
useSSL: formData.useSSL!,
- overridePrefs: ref.read(prefsChangeNotifierProvider),
- overrideTorService: ref.read(pTorService),
+ overridePrefs: read(prefsChangeNotifierProvider),
+ overrideTorService: read(pTorService),
);
} catch (_) {
testPassed = false;
@@ -236,8 +261,8 @@ Future<bool> testNodeConnection({
body: jsonEncode({"action": "version"}),
proxyInfo: !AppConfig.hasFeature(AppFeature.tor)
? null
- : ref.read(prefsChangeNotifierProvider).useTor
- ? ref.read(pTorService).getProxyInfo()
+ : read(prefsChangeNotifierProvider).useTor
+ ? read(pTorService).getProxyInfo()
: null,
);
@@ -259,8 +284,8 @@ Future<bool> testNodeConnection({
formData.host!,
formData.port!,
formData.useSSL ?? false,
- ref.read(prefsChangeNotifierProvider),
- ref.read(pTorService),
+ read(prefsChangeNotifierProvider),
+ read(pTorService),
);
final health = await rpcClient.getHealth();
@@ -275,7 +300,7 @@ Future<bool> testNodeConnection({
try {
final client = HttpClient();
if (AppConfig.hasFeature(AppFeature.tor) &&
- ref.read(prefsChangeNotifierProvider).useTor) {
+ read(prefsChangeNotifierProvider).useTor) {
final proxyInfo = TorService.sharedInstance.getProxyInfo();
final proxySettings = ProxySettings(proxyInfo.host, proxyInfo.port);
SocksTCPClient.assignToHttpClient(client, [proxySettings]);
diff --git a/lib/widgets/node_card.dart b/lib/widgets/node_card.dart
index 85f16a1..0f63657 100644
--- a/lib/widgets/node_card.dart
+++ b/lib/widgets/node_card.dart
@@ -190,11 +190,12 @@ class _NodeCardState extends ConsumerState<NodeCard> {
);
if (context.mounted) {
- final canConnect = await testNodeConnection(
+ final canConnect = await ref.read(
+ testNodeConnectionProvider,
+ )(
context: context,
nodeFormData: nodeFormData,
cryptoCurrency: widget.coin,
- ref: ref,
);
if (!canConnect) {
diff --git a/lib/widgets/node_options_sheet.dart b/lib/widgets/node_options_sheet.dart
index 511be23..b47329e 100644
--- a/lib/widgets/node_options_sheet.dart
+++ b/lib/widgets/node_options_sheet.dart
@@ -267,7 +267,9 @@ class NodeOptionsSheet extends ConsumerWidget {
} else {
netOption = TorPlainNetworkOption.both;
}
- final canConnect = await testNodeConnection(
+ final canConnect = await ref.read(
+ testNodeConnectionProvider,
+ )(
context: context,
nodeFormData: NodeFormData()
..name = node.name
@@ -280,7 +282,6 @@ class NodeOptionsSheet extends ConsumerWidget {
..netOption = netOption
..trusted = node.trusted,
cryptoCurrency: coin,
- ref: ref,
);
if (!canConnect) {
return;
diff --git a/pubspec.lock b/pubspec.lock
index 3b1c5ec..9f4b4df 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -742,13 +742,13 @@ packages:
source: hosted
version: "0.0.6"
dart_style:
- dependency: transitive
+ dependency: "direct overridden"
description:
name: dart_style
- sha256: a4c1ccfee44c7e75ed80484071a5c142a385345e658fd8bd7c4b5c97e7198f98
+ sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b
url: "https://pub.dev"
source: hosted
- version: "3.1.8"
+ version: "3.1.3"
dartx:
dependency: transitive
description:
diff --git a/scripts/ensure_test_app_config.sh b/scripts/ensure_test_app_config.sh
new file mode 100755
index 0000000..c9dccc3
--- /dev/null
+++ b/scripts/ensure_test_app_config.sh
@@ -0,0 +1,92 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+# shellcheck disable=SC1091
+source "${SCRIPT_DIR}/env.sh"
+
+APP_CONFIG_DART_FILE="${APP_PROJECT_ROOT_DIR}/lib/app_config.g.dart"
+
+if test -f "$APP_CONFIG_DART_FILE"; then
+ echo 'ensure_test_app_config.sh: verified lib/app_config.g.dart'
+ exit 0
+fi
+
+BUILT_COMMIT_HASH="$(git -C "${APP_PROJECT_ROOT_DIR}" log -1 --pretty=format:%H 2>/dev/null || true)"
+
+cat > "$APP_CONFIG_DART_FILE" <<EOF
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'app_config.dart';
+
+const _prefix = "Stack";
+const _separator = " ";
+const _suffix = "Wallet";
+const _emptyWalletsMessage =
+ "You do not have any wallets yet. Start building your crypto Stack!";
+const _appDataDirName = "stackwallet";
+const _shortDescriptionText = "An open-source, multicoin wallet for everyone";
+const _commitHash = "$BUILT_COMMIT_HASH";
+
+const _mwebdExeHash = "";
+
+const Set<AppFeature> _features = {
+ AppFeature.themeSelection,
+ AppFeature.buy,
+ AppFeature.tor,
+ AppFeature.swap
+};
+
+const ({String light, String dark})? _appIconAsset = null;
+
+final List<CryptoCurrency> _supportedCoins = List.unmodifiable([
+ Bitcoin(CryptoCurrencyNetwork.main),
+ Monero(CryptoCurrencyNetwork.main),
+ Banano(CryptoCurrencyNetwork.main),
+ Bitcoincash(CryptoCurrencyNetwork.main),
+ BitcoinFrost(CryptoCurrencyNetwork.main),
+ Cardano(CryptoCurrencyNetwork.main),
+ Dash(CryptoCurrencyNetwork.main),
+ Dogecoin(CryptoCurrencyNetwork.main),
+ Ecash(CryptoCurrencyNetwork.main),
+ Epiccash(CryptoCurrencyNetwork.main),
+ Ethereum(CryptoCurrencyNetwork.main),
+ Fact0rn(CryptoCurrencyNetwork.main),
+ Firo(CryptoCurrencyNetwork.main),
+ Litecoin(CryptoCurrencyNetwork.main),
+ if (!Platform.isMacOS) Mimblewimblecoin(CryptoCurrencyNetwork.main),
+ Nano(CryptoCurrencyNetwork.main),
+ Namecoin(CryptoCurrencyNetwork.main),
+ Particl(CryptoCurrencyNetwork.main),
+ Peercoin(CryptoCurrencyNetwork.main),
+ Salvium(CryptoCurrencyNetwork.main),
+ Solana(CryptoCurrencyNetwork.main),
+ Stellar(CryptoCurrencyNetwork.main),
+ Tezos(CryptoCurrencyNetwork.main),
+ Wownero(CryptoCurrencyNetwork.main),
+ Xelis(CryptoCurrencyNetwork.main),
+ Bitcoin(CryptoCurrencyNetwork.test),
+ Bitcoin(CryptoCurrencyNetwork.test4),
+ Bitcoincash(CryptoCurrencyNetwork.test),
+ BitcoinFrost(CryptoCurrencyNetwork.test),
+ BitcoinFrost(CryptoCurrencyNetwork.test4),
+ Dogecoin(CryptoCurrencyNetwork.test),
+ Firo(CryptoCurrencyNetwork.test),
+ Litecoin(CryptoCurrencyNetwork.test),
+ Peercoin(CryptoCurrencyNetwork.test),
+ Salvium(CryptoCurrencyNetwork.test),
+ Stellar(CryptoCurrencyNetwork.test),
+ Xelis(CryptoCurrencyNetwork.test),
+]);
+
+final ({String from, String fromFuzzyNet, String to, String toFuzzyNet})
+_swapDefaults = (
+ from: "BTC",
+ fromFuzzyNet: "btc",
+ to: "XMR",
+ toFuzzyNet: "xmr",
+);
+EOF
+
+echo 'ensure_test_app_config.sh: created lib/app_config.g.dart'
diff --git a/test/cached_electrumx_test.dart b/test/cached_electrumx_test.dart
index 370e029..0e5bf0b 100644
--- a/test/cached_electrumx_test.dart
+++ b/test/cached_electrumx_test.dart
@@ -1,5 +1,4 @@
import 'package:flutter_test/flutter_test.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_client.dart';
@@ -8,13 +7,14 @@ import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
import 'cached_electrumx_test.mocks.dart';
+import 'hive/hive_ce_test_utils.dart';
// import 'sample_data/get_anonymity_set_sample_data.dart';
@GenerateMocks([ElectrumXClient, Prefs])
void main() {
group("tests using mock hive", () {
setUp(() async {
- await setUpTestHive();
+ await setUpHiveCeTest();
// await Hive.openBox<dynamic>(
// DB.instance.boxNameUsedSerialsCache(coin: Coin.firo));
// await Hive.openBox<dynamic>(DB.instance.boxNameSetCache(coin: Coin.firo));
@@ -117,24 +117,17 @@ void main() {
test("getTransaction throws", () async {
final client = MockElectrumXClient();
- when(
- client.getTransaction(
- txHash: "some hash",
- ),
- ).thenThrow(Exception());
+ when(client.getTransaction(txHash: "some hash")).thenThrow(Exception());
- final cachedClient = CachedElectrumXClient(
- electrumXClient: client,
- );
+ final cachedClient = CachedElectrumXClient(electrumXClient: client);
expect(
- () async => await cachedClient.getTransaction(
- txHash: "some hash",
- cryptoCurrency: Firo(
- CryptoCurrencyNetwork.main,
- ),
- ),
- throwsA(isA<Exception>()));
+ () async => await cachedClient.getTransaction(
+ txHash: "some hash",
+ cryptoCurrency: Firo(CryptoCurrencyNetwork.main),
+ ),
+ throwsA(isA<Exception>()),
+ );
});
test("clearSharedTransactionCache", () async {
@@ -145,9 +138,7 @@ void main() {
bool didThrow = false;
try {
await cachedClient.clearSharedTransactionCache(
- cryptoCurrency: Firo(
- CryptoCurrencyNetwork.main,
- ),
+ cryptoCurrency: Firo(CryptoCurrencyNetwork.main),
);
} catch (_) {
didThrow = true;
@@ -157,7 +148,7 @@ void main() {
});
tearDown(() async {
- await tearDownTestHive();
+ await tearDownHiveCeTest();
});
});
@@ -172,8 +163,9 @@ void main() {
clearnetEnabled: true,
);
- final client =
- CachedElectrumXClient.from(electrumXClient: MockElectrumXClient());
+ final client = CachedElectrumXClient.from(
+ electrumXClient: MockElectrumXClient(),
+ );
expect(client, isA<CachedElectrumXClient>());
});
diff --git a/test/cached_electrumx_test.mocks.dart b/test/cached_electrumx_test.mocks.dart
index 504934e..a622b2f 100644
--- a/test/cached_electrumx_test.mocks.dart
+++ b/test/cached_electrumx_test.mocks.dart
@@ -332,6 +332,22 @@ class MockElectrumXClient extends _i1.Mock implements _i6.ElectrumXClient {
)
as _i9.Future<Map<String, dynamic>>);
+ @override
+ _i9.Future<List<Map<String, dynamic>>> getBatchTransactions({
+ required List<String>? txHashes,
+ String? requestID,
+ }) =>
+ (super.noSuchMethod(
+ Invocation.method(#getBatchTransactions, [], {
+ #txHashes: txHashes,
+ #requestID: requestID,
+ }),
+ returnValue: _i9.Future<List<Map<String, dynamic>>>.value(
+ <Map<String, dynamic>>[],
+ ),
+ )
+ as _i9.Future<List<Map<String, dynamic>>>);
+
@override
_i9.Future<Map<String, dynamic>> getLelantusAnonymitySet({
String? groupId = '1',
diff --git a/test/electrumx_test.dart b/test/electrumx_test.dart
index 06ed611..b8c82c8 100644
--- a/test/electrumx_test.dart
+++ b/test/electrumx_test.dart
@@ -1,1778 +1,613 @@
-// import 'dart:io';
-//
-// import 'package:flutter_test/flutter_test.dart';
-// import 'package:mockito/annotations.dart';
-// import 'package:mockito/mockito.dart';
-// import 'package:stackwallet/electrumx_rpc/electrumx_client.dart';
-// import 'package:stackwallet/electrumx_rpc/rpc.dart';
-// import 'package:stackwallet/services/event_bus/events/global/tor_connection_status_changed_event.dart';
-// import 'package:stackwallet/services/tor_service.dart';
-// import 'package:stackwallet/utilities/prefs.dart';
-//
-// import 'electrumx_test.mocks.dart';
-// import 'sample_data/get_anonymity_set_sample_data.dart';
-// import 'sample_data/get_used_serials_sample_data.dart';
-// import 'sample_data/transaction_data_samples.dart';
-//
-// @GenerateMocks([JsonRPC, Prefs, TorService])
-// void main() {
-// group("factory constructors and getters", () {
-// test("electrumxnode .from factory", () {
-// final nodeA = ElectrumXNode(
-// address: "some address",
-// port: 1,
-// name: "some name",
-// id: "some ID",
-// useSSL: true,
-// );
-//
-// final nodeB = ElectrumXNode.from(nodeA);
-//
-// expect(nodeB.toString(), nodeA.toString());
-// expect(nodeA == nodeB, false);
-// });
-//
-// test("electrumx .from factory", () {
-// final node = ElectrumXNode(
-// address: "some address",
-// port: 1,
-// name: "some name",
-// id: "some ID",
-// useSSL: true,
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-//
-// final client = ElectrumXClient.from(
-// node: node,
-// failovers: [],
-// prefs: mockPrefs,
-// torService: torService,
-// );
-//
-// expect(client.useSSL, node.useSSL);
-// expect(client.host, node.address);
-// expect(client.port, node.port);
-// expect(client.rpcClient, null);
-//
-// verifyNoMoreInteractions(mockPrefs);
-// });
-// });
-//
-// test("Server error", () {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.transaction.get";
-// const jsonArgs = '["",true]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "error": {
-// "code": 1,
-// "message": "None should be a transaction hash",
-// },
-// "id": "some requestId",
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-//
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// failovers: [],
-// prefs: mockPrefs,
-// torService: torService,
-// );
-//
-// expect(() => client.getTransaction(requestID: "some requestId", txHash: ''),
-// throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// group("getBlockHeadTip", () {
-// test("getBlockHeadTip success", () async {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.headers.subscribe";
-// const jsonArgs = '[]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": {"height": 520481, "hex": "some block hex string"},
-// "id": "some requestId"
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// final result =
-// await (client.getBlockHeadTip(requestID: "some requestId"));
-//
-// expect(result["height"], 520481);
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// test("getBlockHeadTip throws/fails", () {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.headers.subscribe";
-// const jsonArgs = '[]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenThrow(Exception());
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// expect(() => client.getBlockHeadTip(requestID: "some requestId"),
-// throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-// });
-//
-// group("ping", () {
-// test("ping success", () async {
-// final mockClient = MockJsonRPC();
-// const command = "server.ping";
-// const jsonArgs = '[]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 2),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": null,
-// "id": "some requestId",
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// final result = await client.ping(requestID: "some requestId");
-//
-// expect(result, true);
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// test("ping throws/fails", () {
-// final mockClient = MockJsonRPC();
-// const command = "server.ping";
-// const jsonArgs = '[]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 2),
-// ),
-// ).thenThrow(Exception());
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// expect(() => client.ping(requestID: "some requestId"),
-// throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-// });
-//
-// group("getServerFeatures", () {
-// test("getServerFeatures success", () async {
-// final mockClient = MockJsonRPC();
-// const command = "server.features";
-// const jsonArgs = '[]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": {
-// "genesis_hash":
-// "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943",
-// "hosts": {
-// "0.0.0.0": {"tcp_port": 51001, "ssl_port": 51002}
-// },
-// "protocol_max": "1.0",
-// "protocol_min": "1.0",
-// "pruning": null,
-// "server_version": "ElectrumX 1.0.17",
-// "hash_function": "sha256"
-// },
-// "id": "some requestId"
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// final result =
-// await client.getServerFeatures(requestID: "some requestId");
-//
-// expect(result, {
-// "genesis_hash":
-// "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943",
-// "hosts": {
-// "0.0.0.0": {"tcp_port": 51001, "ssl_port": 51002}
-// },
-// "protocol_max": "1.0",
-// "protocol_min": "1.0",
-// "pruning": null,
-// "server_version": "ElectrumX 1.0.17",
-// "hash_function": "sha256",
-// });
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// test("getServerFeatures throws/fails", () {
-// final mockClient = MockJsonRPC();
-// const command = "server.features";
-// const jsonArgs = '[]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenThrow(Exception());
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// expect(() => client.getServerFeatures(requestID: "some requestId"),
-// throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-// });
-//
-// group("broadcastTransaction", () {
-// test("broadcastTransaction success", () async {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.transaction.broadcast";
-// const jsonArgs = '["some raw transaction string"]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": "the txid of the rawtx",
-// "id": "some requestId"
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// final result = await client.broadcastTransaction(
-// rawTx: "some raw transaction string", requestID: "some requestId");
-//
-// expect(result, "the txid of the rawtx");
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// test("broadcastTransaction throws/fails", () {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.transaction.broadcast";
-// const jsonArgs = '["some raw transaction string"]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenThrow(Exception());
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// expect(
-// () => client.broadcastTransaction(
-// rawTx: "some raw transaction string",
-// requestID: "some requestId"),
-// throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-// });
-//
-// group("getBalance", () {
-// test("getBalance success", () async {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.scripthash.get_balance";
-// const jsonArgs = '["dummy hash"]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": {
-// "confirmed": 103873966,
-// "unconfirmed": 23684400,
-// },
-// "id": "some requestId"
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// final result = await client.getBalance(
-// scripthash: "dummy hash", requestID: "some requestId");
-//
-// expect(result, {"confirmed": 103873966, "unconfirmed": 23684400});
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// test("getBalance throws/fails", () {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.scripthash.get_balance";
-// const jsonArgs = '["dummy hash"]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenThrow(Exception());
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// expect(
-// () => client.getBalance(
-// scripthash: "dummy hash", requestID: "some requestId"),
-// throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-// });
-//
-// group("getHistory", () {
-// test("getHistory success", () async {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.scripthash.get_history";
-// const jsonArgs = '["dummy hash"]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(minutes: 5),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": [
-// {
-// "height": 200004,
-// "tx_hash":
-// "acc3758bd2a26f869fcc67d48ff30b96464d476bca82c1cd6656e7d506816412"
-// },
-// {
-// "height": 215008,
-// "tx_hash":
-// "f3e1bf48975b8d6060a9de8884296abb80be618dc00ae3cb2f6cee3085e09403"
-// }
-// ],
-// "id": "some requestId"
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// final result = await client.getHistory(
-// scripthash: "dummy hash", requestID: "some requestId");
-//
-// expect(result, [
-// {
-// "height": 200004,
-// "tx_hash":
-// "acc3758bd2a26f869fcc67d48ff30b96464d476bca82c1cd6656e7d506816412"
-// },
-// {
-// "height": 215008,
-// "tx_hash":
-// "f3e1bf48975b8d6060a9de8884296abb80be618dc00ae3cb2f6cee3085e09403"
-// }
-// ]);
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// test("getHistory throws/fails", () {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.scripthash.get_history";
-// const jsonArgs = '["dummy hash"]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(minutes: 5),
-// ),
-// ).thenThrow(Exception());
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// expect(
-// () => client.getHistory(
-// scripthash: "dummy hash", requestID: "some requestId"),
-// throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-// });
-//
-// group("getUTXOs", () {
-// test("getUTXOs success", () async {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.scripthash.listunspent";
-// const jsonArgs = '["dummy hash"]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": [
-// {
-// "tx_pos": 0,
-// "value": 45318048,
-// "tx_hash":
-// "9f2c45a12db0144909b5db269415f7319179105982ac70ed80d76ea79d923ebf",
-// "height": 437146
-// },
-// {
-// "tx_pos": 0,
-// "value": 919195,
-// "tx_hash":
-// "3d2290c93436a3e964cfc2f0950174d8847b1fbe3946432c4784e168da0f019f",
-// "height": 441696
-// }
-// ],
-// "id": "some requestId"
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// final result = await client.getUTXOs(
-// scripthash: "dummy hash", requestID: "some requestId");
-//
-// expect(result, [
-// {
-// "tx_pos": 0,
-// "value": 45318048,
-// "tx_hash":
-// "9f2c45a12db0144909b5db269415f7319179105982ac70ed80d76ea79d923ebf",
-// "height": 437146
-// },
-// {
-// "tx_pos": 0,
-// "value": 919195,
-// "tx_hash":
-// "3d2290c93436a3e964cfc2f0950174d8847b1fbe3946432c4784e168da0f019f",
-// "height": 441696
-// }
-// ]);
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// test("getUTXOs throws/fails", () {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.scripthash.listunspent";
-// const jsonArgs = '["dummy hash"]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenThrow(Exception());
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// expect(
-// () => client.getUTXOs(
-// scripthash: "dummy hash", requestID: "some requestId"),
-// throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-// });
-//
-// group("getTransaction", () {
-// test("getTransaction success", () async {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.transaction.get";
-// const jsonArgs = '["${SampleGetTransactionData.txHash0}",true]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": SampleGetTransactionData.txData0,
-// "id": "some requestId"
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// final result = await client.getTransaction(
-// txHash: SampleGetTransactionData.txHash0,
-// verbose: true,
-// requestID: "some requestId");
-//
-// expect(result, SampleGetTransactionData.txData0);
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// test("getTransaction throws/fails", () {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.transaction.get";
-// const jsonArgs = '["${SampleGetTransactionData.txHash0}",true]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenThrow(Exception());
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// expect(
-// () => client.getTransaction(
-// txHash: SampleGetTransactionData.txHash0,
-// requestID: "some requestId"),
-// throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-// });
-//
-// group("getAnonymitySet", () {
-// test("getAnonymitySet success", () async {
-// final mockClient = MockJsonRPC();
-// const command = "lelantus.getanonymityset";
-// const jsonArgs = '["1",""]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": GetAnonymitySetSampleData.data,
-// "id": "some requestId"
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// final result = await client.getLelantusAnonymitySet(
-// groupId: "1", blockhash: "", requestID: "some requestId");
-//
-// expect(result, GetAnonymitySetSampleData.data);
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// test("getAnonymitySet throws/fails", () {
-// final mockClient = MockJsonRPC();
-// const command = "lelantus.getanonymityset";
-// const jsonArgs = '["1",""]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenThrow(Exception());
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// expect(
-// () => client.getLelantusAnonymitySet(
-// groupId: "1", requestID: "some requestId"),
-// throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-// });
-//
-// group("getMintData", () {
-// test("getMintData success", () async {
-// final mockClient = MockJsonRPC();
-// const command = "lelantus.getmintmetadata";
-// const jsonArgs = '["some mints"]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": "mint meta data",
-// "id": "some requestId"
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// final result = await client.getLelantusMintData(
-// mints: "some mints", requestID: "some requestId");
-//
-// expect(result, "mint meta data");
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// test("getMintData throws/fails", () {
-// final mockClient = MockJsonRPC();
-// const command = "lelantus.getmintmetadata";
-// const jsonArgs = '["some mints"]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenThrow(Exception());
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// expect(
-// () => client.getLelantusMintData(
-// mints: "some mints", requestID: "some requestId"),
-// throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-// });
-//
-// group("getUsedCoinSerials", () {
-// test("getUsedCoinSerials success", () async {
-// final mockClient = MockJsonRPC();
-// const command = "lelantus.getusedcoinserials";
-// const jsonArgs = '["0"]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(minutes: 2),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": GetUsedSerialsSampleData.serials,
-// "id": "some requestId"
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// final result = await client.getLelantusUsedCoinSerials(
-// requestID: "some requestId", startNumber: 0);
-//
-// expect(result, GetUsedSerialsSampleData.serials);
-//
-// verify(mockPrefs.wifiOnly).called(3);
-// verify(mockPrefs.useTor).called(3);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// test("getUsedCoinSerials throws/fails", () {
-// final mockClient = MockJsonRPC();
-// const command = "lelantus.getusedcoinserials";
-// const jsonArgs = '["0"]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(minutes: 2),
-// ),
-// ).thenThrow(Exception());
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// expect(
-// () => client.getLelantusUsedCoinSerials(
-// requestID: "some requestId", startNumber: 0),
-// throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-// });
-//
-// group("getLatestCoinId", () {
-// test("getLatestCoinId success", () async {
-// final mockClient = MockJsonRPC();
-// const command = "lelantus.getlatestcoinid";
-// const jsonArgs = '[]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": 1,
-// "id": "some requestId",
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// final result =
-// await client.getLelantusLatestCoinId(requestID: "some requestId");
-//
-// expect(result, 1);
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// test("getLatestCoinId throws/fails", () {
-// final mockClient = MockJsonRPC();
-// const command = "lelantus.getlatestcoinid";
-// const jsonArgs = '[]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenThrow(Exception());
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// expect(
-// () => client.getLelantusLatestCoinId(
-// requestID: "some requestId",
-// ),
-// throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-// });
-//
-// group("getCoinsForRecovery", () {
-// test("getCoinsForRecovery success", () async {
-// final mockClient = MockJsonRPC();
-// const command = "lelantus.getanonymityset";
-// const jsonArgs = '["1",""]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": GetAnonymitySetSampleData.data,
-// "id": "some requestId"
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// final result = await client.getLelantusAnonymitySet(
-// groupId: "1", blockhash: "", requestID: "some requestId");
-//
-// expect(result, GetAnonymitySetSampleData.data);
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// test("getAnonymitySet throws/fails", () {
-// final mockClient = MockJsonRPC();
-// const command = "lelantus.getanonymityset";
-// const jsonArgs = '["1",""]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenThrow(Exception());
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// expect(
-// () => client.getLelantusAnonymitySet(
-// groupId: "1",
-// requestID: "some requestId",
-// ),
-// throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-// });
-//
-// group("getMintData", () {
-// test("getMintData success", () async {
-// final mockClient = MockJsonRPC();
-// const command = "lelantus.getmintmetadata";
-// const jsonArgs = '["some mints"]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": "mint meta data",
-// "id": "some requestId"
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// final result = await client.getLelantusMintData(
-// mints: "some mints", requestID: "some requestId");
-//
-// expect(result, "mint meta data");
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// test("getMintData throws/fails", () {
-// final mockClient = MockJsonRPC();
-// const command = "lelantus.getmintmetadata";
-// const jsonArgs = '["some mints"]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenThrow(Exception());
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// expect(
-// () => client.getLelantusMintData(
-// mints: "some mints",
-// requestID: "some requestId",
-// ),
-// throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-// });
-//
-// group("getUsedCoinSerials", () {
-// test("getUsedCoinSerials success", () async {
-// final mockClient = MockJsonRPC();
-// const command = "lelantus.getusedcoinserials";
-// const jsonArgs = '["0"]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(minutes: 2),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": GetUsedSerialsSampleData.serials,
-// "id": "some requestId"
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// final result = await client.getLelantusUsedCoinSerials(
-// requestID: "some requestId", startNumber: 0);
-//
-// expect(result, GetUsedSerialsSampleData.serials);
-//
-// verify(mockPrefs.wifiOnly).called(3);
-// verify(mockPrefs.useTor).called(3);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// test("getUsedCoinSerials throws/fails", () {
-// final mockClient = MockJsonRPC();
-// const command = "lelantus.getusedcoinserials";
-// const jsonArgs = '["0"]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(minutes: 2),
-// ),
-// ).thenThrow(Exception());
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// expect(
-// () => client.getLelantusUsedCoinSerials(
-// requestID: "some requestId", startNumber: 0),
-// throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-// });
-//
-// group("getLatestCoinId", () {
-// test("getLatestCoinId success", () async {
-// final mockClient = MockJsonRPC();
-// const command = "lelantus.getlatestcoinid";
-// const jsonArgs = '[]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": 1,
-// "id": "some requestId",
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// final result =
-// await client.getLelantusLatestCoinId(requestID: "some requestId");
-//
-// expect(result, 1);
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// test("getLatestCoinId throws/fails", () {
-// final mockClient = MockJsonRPC();
-// const command = "lelantus.getlatestcoinid";
-// const jsonArgs = '[]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenThrow(Exception());
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// expect(() => client.getLelantusLatestCoinId(requestID: "some requestId"),
-// throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-// });
-//
-// group("getFeeRate", () {
-// test("getFeeRate success", () async {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.getfeerate";
-// const jsonArgs = '[]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": {
-// "rate": 1000,
-// },
-// "id": "some requestId"
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// final result = await client.getFeeRate(requestID: "some requestId");
-//
-// expect(result, {"rate": 1000});
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// test("getFeeRate throws/fails", () {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.getfeerate";
-// const jsonArgs = '[]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenThrow(Exception());
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: []);
-//
-// expect(() => client.getFeeRate(requestID: "some requestId"),
-// throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-// });
-//
-// test("rpcClient is null throws with bad server info", () {
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((realInvocation) => false);
-// final torService = MockTorService();
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final client = ElectrumXClient(
-// client: null,
-// port: -10,
-// host: "_ :sa %",
-// useSSL: false,
-// prefs: mockPrefs,
-// torService: torService,
-// failovers: [],
-// );
-//
-// expect(() => client.getFeeRate(), throwsA(isA<Exception>()));
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// });
-//
-// group("Tor tests", () {
-// // useTor is false, so no TorService calls should be made.
-// test("Tor not in use", () async {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.transaction.get";
-// const jsonArgs = '["${SampleGetTransactionData.txHash0}",true]';
-// when(mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId","method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// )).thenAnswer((_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": SampleGetTransactionData.txData0,
-// "id": "some requestId",
-// }));
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((_) => false);
-// when(mockPrefs.torKillSwitch)
-// .thenAnswer((_) => false); // Or true, shouldn't matter.
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final mockTorService = MockTorService();
-// when(mockTorService.status)
-// .thenAnswer((_) => TorConnectionStatus.disconnected);
-//
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// failovers: [],
-// prefs: mockPrefs,
-// torService: mockTorService,
-// );
-//
-// final result = await client.getTransaction(
-// txHash: SampleGetTransactionData.txHash0,
-// verbose: true,
-// requestID: "some requestId");
-//
-// expect(result, SampleGetTransactionData.txData0);
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verifyNever(mockPrefs.torKillSwitch);
-// verifyNoMoreInteractions(mockPrefs);
-// verifyNever(mockTorService.status);
-// verifyNoMoreInteractions(mockTorService);
-// });
-//
-// // useTor is true, but TorService is not enabled and the killswitch is off, so a clearnet call should be made.
-// test("Tor in use but Tor unavailable and killswitch off", () async {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.transaction.get";
-// const jsonArgs = '["${SampleGetTransactionData.txHash0}",true]';
-// when(mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId","method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// )).thenAnswer((_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": SampleGetTransactionData.txData0,
-// "id": "some requestId",
-// }));
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((_) => true);
-// when(mockPrefs.torKillSwitch).thenAnswer((_) => false);
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-//
-// final mockTorService = MockTorService();
-// when(mockTorService.status)
-// .thenAnswer((_) => TorConnectionStatus.disconnected);
-// when(mockTorService.getProxyInfo()).thenAnswer((_) => (
-// host: InternetAddress('1.2.3.4'),
-// port: -1
-// )); // Port is set to -1 until Tor is enabled.
-//
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: mockTorService,
-// failovers: []);
-//
-// final result = await client.getTransaction(
-// txHash: SampleGetTransactionData.txHash0,
-// verbose: true,
-// requestID: "some requestId");
-//
-// expect(result, SampleGetTransactionData.txData0);
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verify(mockPrefs.torKillSwitch).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// verify(mockTorService.status).called(1);
-// verifyNever(mockTorService.getProxyInfo());
-// verifyNoMoreInteractions(mockTorService);
-// });
-//
-// // useTor is true and TorService is enabled, so a TorService call should be made.
-// test("Tor in use and available", () async {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.transaction.get";
-// const jsonArgs = '["${SampleGetTransactionData.txHash0}",true]';
-// when(mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId","method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// )).thenAnswer((_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": SampleGetTransactionData.txData0,
-// "id": "some requestId",
-// }));
-// when(mockClient.proxyInfo)
-// .thenAnswer((_) => (host: InternetAddress('1.2.3.4'), port: 42));
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((_) => true);
-// when(mockPrefs.torKillSwitch).thenAnswer((_) => false); // Or true.
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-//
-// final mockTorService = MockTorService();
-// when(mockTorService.status)
-// .thenAnswer((_) => TorConnectionStatus.connected);
-// when(mockTorService.getProxyInfo())
-// .thenAnswer((_) => (host: InternetAddress('1.2.3.4'), port: 42));
-//
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// prefs: mockPrefs,
-// torService: mockTorService,
-// failovers: []);
-//
-// final result = await client.getTransaction(
-// txHash: SampleGetTransactionData.txHash0,
-// verbose: true,
-// requestID: "some requestId");
-//
-// expect(result, SampleGetTransactionData.txData0);
-//
-// verify(mockClient.proxyInfo).called(1);
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verifyNever(mockPrefs.torKillSwitch);
-// verifyNoMoreInteractions(mockPrefs);
-// verify(mockTorService.status).called(1);
-// verify(mockTorService.getProxyInfo()).called(1);
-// verifyNoMoreInteractions(mockTorService);
-// });
-//
-// // useTor is true, but TorService is not enabled and the killswitch is on, so no TorService calls should be made.
-// test("killswitch enabled", () async {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.transaction.get";
-// const jsonArgs = '["",true]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "error": {
-// "code": 1,
-// "message": "None should be a transaction hash",
-// },
-// "id": "some requestId",
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((_) => true);
-// when(mockPrefs.torKillSwitch).thenAnswer((_) => true);
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final mockTorService = MockTorService();
-// when(mockTorService.status)
-// .thenAnswer((_) => TorConnectionStatus.disconnected);
-//
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// failovers: [],
-// prefs: mockPrefs,
-// torService: mockTorService,
-// );
-//
-// try {
-// var result = await client.getTransaction(
-// requestID: "some requestId", txHash: '');
-// } catch (e) {
-// expect(e, isA<Exception>());
-// expect(
-// e.toString(),
-// equals(
-// "Exception: Tor preference and killswitch set but Tor is not enabled, not connecting to ElectrumX"));
-// }
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verify(mockPrefs.torKillSwitch).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// verify(mockTorService.status).called(1);
-// verifyNoMoreInteractions(mockTorService);
-// });
-//
-// // useTor is true but Tor is not enabled, but because the killswitch is off, a clearnet call should be made.
-// test("killswitch disabled", () async {
-// final mockClient = MockJsonRPC();
-// const command = "blockchain.transaction.get";
-// const jsonArgs = '["${SampleGetTransactionData.txHash0}",true]';
-// when(
-// mockClient.request(
-// '{"jsonrpc": "2.0", "id": "some requestId",'
-// '"method": "$command","params": $jsonArgs}',
-// const Duration(seconds: 60),
-// ),
-// ).thenAnswer(
-// (_) async => JsonRPCResponse(data: {
-// "jsonrpc": "2.0",
-// "result": SampleGetTransactionData.txData0,
-// "id": "some requestId"
-// }),
-// );
-//
-// final mockPrefs = MockPrefs();
-// when(mockPrefs.useTor).thenAnswer((_) => true);
-// when(mockPrefs.torKillSwitch).thenAnswer((_) => false);
-// when(mockPrefs.wifiOnly).thenAnswer((_) => false);
-// final mockTorService = MockTorService();
-// when(mockTorService.status)
-// .thenAnswer((_) => TorConnectionStatus.disconnected);
-//
-// final client = ElectrumXClient(
-// host: "some server",
-// port: 0,
-// useSSL: true,
-// client: mockClient,
-// failovers: [],
-// prefs: mockPrefs,
-// torService: mockTorService,
-// );
-//
-// final result = await client.getTransaction(
-// txHash: SampleGetTransactionData.txHash0,
-// verbose: true,
-// requestID: "some requestId");
-//
-// expect(result, SampleGetTransactionData.txData0);
-//
-// verify(mockPrefs.wifiOnly).called(1);
-// verify(mockPrefs.useTor).called(1);
-// verify(mockPrefs.torKillSwitch).called(1);
-// verifyNoMoreInteractions(mockPrefs);
-// verify(mockTorService.status).called(1);
-// verifyNoMoreInteractions(mockTorService);
-// });
-// });
-// }
+import 'dart:io';
+
+import 'package:decimal/decimal.dart';
+import 'package:event_bus/event_bus.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:logger/logger.dart' show Level;
+import 'package:mockito/mockito.dart';
+import 'package:stackwallet/electrumx_rpc/electrumx_client.dart';
+import 'package:stackwallet/services/event_bus/events/global/tor_connection_status_changed_event.dart';
+import 'package:stackwallet/services/tor_service.dart';
+import 'package:stackwallet/utilities/logger.dart';
+import 'package:stackwallet/utilities/prefs.dart';
+import 'package:stackwallet/utilities/tor_plain_net_option_enum.dart';
+import 'package:stackwallet/wallets/crypto_currency/coins/bitcoin.dart';
+import 'package:stackwallet/wallets/crypto_currency/coins/firo.dart';
+import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
+
+import 'sample_data/get_anonymity_set_sample_data.dart';
+import 'sample_data/get_used_serials_sample_data.dart';
+import 'sample_data/gethistory_samples.dart';
+import 'sample_data/transaction_data_samples.dart';
+import 'utilities/mock_electrum_server.dart';
+
+class MockPrefs extends Mock implements Prefs {
+ @override
+ bool get wifiOnly =>
+ super.noSuchMethod(
+ Invocation.getter(#wifiOnly),
+ returnValue: false,
+ returnValueForMissingStub: false,
+ )
+ as bool;
+
+ @override
+ bool get useTor =>
+ super.noSuchMethod(
+ Invocation.getter(#useTor),
+ returnValue: false,
+ returnValueForMissingStub: false,
+ )
+ as bool;
+
+ @override
+ bool get torKillSwitch =>
+ super.noSuchMethod(
+ Invocation.getter(#torKillSwitch),
+ returnValue: false,
+ returnValueForMissingStub: false,
+ )
+ as bool;
+}
+
+class FakeTorService implements TorService {
+ FakeTorService({
+ this.currentStatus = TorConnectionStatus.disconnected,
+ ({InternetAddress host, int port})? proxyInfo,
+ }) : _proxyInfo =
+ proxyInfo ?? (host: InternetAddress.loopbackIPv4, port: 9050);
+
+ TorConnectionStatus currentStatus;
+ ({InternetAddress host, int port}) _proxyInfo;
+ int statusReads = 0;
+ int proxyInfoReads = 0;
+
+ @override
+ TorConnectionStatus get status {
+ statusReads++;
+ return currentStatus;
+ }
+
+ void setProxyInfo(({InternetAddress host, int port}) proxyInfo) {
+ _proxyInfo = proxyInfo;
+ }
+
+ @override
+ ({InternetAddress host, int port}) getProxyInfo() {
+ proxyInfoReads++;
+ return _proxyInfo;
+ }
+
+ @override
+ Future<void> disable() async {}
+
+ @override
+ void init({required String torDataDirPath}) {}
+
+ @override
+ Future<void> start() async {}
+}
+
+void main() {
+ late Directory logDir;
+ late MockPrefs prefs;
+ late FakeTorService torService;
+ late EventBus eventBus;
+ final servers = <MockElectrumServer>[];
+
+ setUpAll(() async {
+ logDir = await Directory.systemTemp.createTemp('electrumx_test_logs');
+ await Logging.instance.initialize(logDir.path, level: Level.off);
+ });
+
+ Bitcoin bitcoin() => Bitcoin(CryptoCurrencyNetwork.main);
+ Firo firo() => Firo(CryptoCurrencyNetwork.main);
+
+ MockElectrumServer registerServer({
+ Map<String, MockElectrumHandler> handlers = const {},
+ }) {
+ final server = MockElectrumServer(handlers: handlers);
+ servers.add(server);
+ return server;
+ }
+
+ ManagedElectrumXClient buildClient({
+ required MockElectrumServer clearServer,
+ MockElectrumServer? torServer,
+ required CryptoCurrency coin,
+ TorPlainNetworkOption netType = TorPlainNetworkOption.both,
+ }) {
+ return ManagedElectrumXClient(
+ host: 'mock.stackwallet.dev',
+ port: 50002,
+ useSSL: true,
+ prefs: prefs,
+ torService: torService,
+ failovers: [],
+ cryptoCurrency: coin,
+ netType: netType,
+ clearServer: clearServer,
+ torServer: torServer,
+ globalEventBusForTesting: eventBus,
+ );
+ }
+
+ Matcher throwsCurrentCastError() => throwsA(
+ isA<Object>().having(
+ (error) => error.toString(),
+ 'message',
+ contains('is not a subtype'),
+ ),
+ );
+
+ setUp(() {
+ prefs = MockPrefs();
+ torService = FakeTorService();
+ eventBus = EventBus();
+ servers.clear();
+
+ when(prefs.wifiOnly).thenReturn(false);
+ when(prefs.useTor).thenReturn(false);
+ when(prefs.torKillSwitch).thenReturn(false);
+ });
+
+ tearDown(() async {
+ await tearDownManagedElectrum(servers: servers);
+ });
+
+ group('factory constructors and getters', () {
+ test('electrumxnode .from factory copies current fields', () {
+ final nodeA = ElectrumXNode(
+ address: 'some address',
+ port: 50002,
+ name: 'some name',
+ id: 'some ID',
+ useSSL: true,
+ torEnabled: true,
+ clearnetEnabled: false,
+ );
+
+ final nodeB = ElectrumXNode.from(nodeA);
+
+ expect(nodeB.toString(), nodeA.toString());
+ expect(nodeA == nodeB, false);
+ expect(nodeB.torEnabled, isTrue);
+ expect(nodeB.clearnetEnabled, isFalse);
+ });
+
+ test('electrumx .from factory uses current constructor inputs', () {
+ final node = ElectrumXNode(
+ address: 'some address',
+ port: 60001,
+ name: 'some name',
+ id: 'some ID',
+ useSSL: false,
+ torEnabled: false,
+ clearnetEnabled: true,
+ );
+
+ final client = ElectrumXClient.from(
+ node: node,
+ failovers: [],
+ prefs: prefs,
+ torService: torService,
+ globalEventBusForTesting: eventBus,
+ cryptoCurrency: bitcoin(),
+ );
+
+ expect(client.useSSL, isFalse);
+ expect(client.host, node.address);
+ expect(client.port, node.port);
+ expect(client.netType, TorPlainNetworkOption.clear);
+ expect(client.getElectrumAdapter(), isNull);
+ verifyNever(prefs.useTor);
+ expect(torService.statusReads, 0);
+ });
+ });
+
+ group('generic request wrappers', () {
+ test('ping success uses the live adapter client', () async {
+ final server = registerServer(handlers: {'server.ping': (_) => null});
+ final client = buildClient(clearServer: server, coin: bitcoin());
+
+ final result = await client.ping(requestID: 'ping-1');
+
+ expect(result, isTrue);
+ expect(server.requestCount('blockchain.headers.subscribe'), 1);
+ expect(server.requestCount('server.ping'), 1);
+ });
+
+ test('server.features success returns a parsed map', () async {
+ final expected = {
+ 'genesis_hash': 'genesis',
+ 'hosts': {
+ '0.0.0.0': {'tcp_port': 51001, 'ssl_port': 51002},
+ },
+ 'protocol_max': '1.4',
+ 'protocol_min': '1.0',
+ 'server_version': 'ElectrumX 1.0.17',
+ 'hash_function': 'sha256',
+ };
+ final server = registerServer(
+ handlers: {'server.features': (_) => expected},
+ );
+ final client = buildClient(clearServer: server, coin: bitcoin());
+
+ final result = await client.getServerFeatures(requestID: 'features-1');
+
+ expect(result, expected);
+ expect(server.requestCount('server.features'), 1);
+ });
+
+ test('getTransaction supports verbose and raw responses', () async {
+ final server = registerServer(
+ handlers: {
+ 'blockchain.transaction.get': (params) {
+ if (params.last == false) {
+ return 'raw-transaction-hex';
+ }
+ return SampleGetTransactionData.txData0;
+ },
+ },
+ );
+ final client = buildClient(clearServer: server, coin: firo());
+
+ final verbose = await client.getTransaction(
+ txHash: SampleGetTransactionData.txHash0,
+ verbose: true,
+ requestID: 'tx-verbose',
+ );
+ final raw = await client.getTransaction(
+ txHash: SampleGetTransactionData.txHash0,
+ verbose: false,
+ requestID: 'tx-raw',
+ );
+
+ expect(verbose, SampleGetTransactionData.txData0);
+ expect(raw, {'rawtx': 'raw-transaction-hex'});
+ });
+
+ test('request surfaces server errors for malformed inputs', () async {
+ final server = registerServer(
+ handlers: {
+ 'blockchain.transaction.get': (_) => {
+ 'error': {
+ 'code': 1,
+ 'message': 'None should be a transaction hash',
+ },
+ },
+ },
+ );
+ final client = buildClient(clearServer: server, coin: bitcoin());
+
+ await expectLater(
+ () => client.request(
+ command: 'blockchain.transaction.get',
+ args: const ['', true],
+ requestID: 'bad-tx',
+ ),
+ throwsA(isA<Exception>()),
+ );
+ });
+
+ test('getHistory uses the current list payload', () async {
+ final server = registerServer(
+ handlers: {
+ 'blockchain.scripthash.get_history': (_) =>
+ SampleGetHistoryData.data1,
+ },
+ );
+ final client = buildClient(clearServer: server, coin: firo());
+
+ final history = await client.getHistory(
+ scripthash: SampleGetHistoryData.scripthash1,
+ requestID: 'history-1',
+ );
+
+ expect(history, SampleGetHistoryData.data1);
+ expect(server.requestCount('blockchain.scripthash.get_history'), 1);
+ });
+
+ test('getHistory throws after retrying malformed payloads', () async {
+ final server = registerServer(
+ handlers: {
+ 'blockchain.scripthash.get_history': (_) => {'unexpected': true},
+ },
+ );
+ final client = buildClient(clearServer: server, coin: firo());
+
+ await expectLater(
+ () => client.getHistory(
+ scripthash: SampleGetHistoryData.scripthash1,
+ requestID: 'history-bad',
+ ),
+ throwsCurrentCastError(),
+ );
+ expect(server.requestCount('blockchain.scripthash.get_history'), 3);
+ });
+
+ test('fee wrappers use the current adapter command names', () async {
+ final server = registerServer(
+ handlers: {
+ 'blockchain.getfeerate': (_) => {'rate': 1000},
+ 'blockchain.estimatefee': (params) {
+ expect(params, [5]);
+ return '0.00001000';
+ },
+ 'blockchain.relayfee': (_) => '0.00002000',
+ },
+ );
+ final client = buildClient(clearServer: server, coin: firo());
+
+ final feeRate = await client.getFeeRate(requestID: 'fee-rate');
+ final estimate = await client.estimateFee(
+ requestID: 'estimate-1',
+ blocks: 5,
+ );
+ final relay = await client.relayFee(requestID: 'relay-1');
+
+ expect(feeRate, {'rate': 1000});
+ expect(estimate, Decimal.parse('0.00001000'));
+ expect(relay, Decimal.parse('0.00002000'));
+ expect(server.requestCount('blockchain.getfeerate'), 1);
+ expect(server.requestCount('blockchain.estimatefee'), 1);
+ expect(server.requestCount('blockchain.relayfee'), 1);
+ });
+
+ test('bad server exceptions bubble from current public wrappers', () async {
+ final server = registerServer(
+ handlers: {
+ 'server.features': (_) => throw Exception('mock bad server'),
+ },
+ );
+ final client = buildClient(clearServer: server, coin: bitcoin());
+
+ await expectLater(
+ () => client.getServerFeatures(requestID: 'features-bad'),
+ throwsA(
+ isA<Exception>().having(
+ (error) => error.toString(),
+ 'message',
+ contains('mock bad server'),
+ ),
+ ),
+ );
+ });
+ });
+
+ group('Firo-specific wrappers', () {
+ test(
+ 'Lelantus wrappers use the current payloads and request shapes',
+ () async {
+ const requestedMints = ['mint-a', 'mint-b'];
+ final mintMetadata = {
+ 'mint-a': {'groupId': 1, 'height': 455866},
+ 'mint-b': {'groupId': 2, 'height': 455876},
+ };
+ final server = registerServer(
+ handlers: {
+ 'lelantus.getanonymityset': (params) {
+ expect(params, ['1', '']);
+ return GetAnonymitySetSampleData.data;
+ },
+ 'lelantus.getmintmetadata': (params) {
+ expect(params, [requestedMints]);
+ return mintMetadata;
+ },
+ 'lelantus.getusedcoinserials': (params) {
+ expect(params, ['0']);
+ return GetUsedSerialsSampleData.serials;
+ },
+ 'lelantus.getlatestcoinid': (_) => 42,
+ },
+ );
+ final client = buildClient(clearServer: server, coin: firo());
+
+ final anonymitySet = await client.getLelantusAnonymitySet(
+ groupId: '1',
+ blockhash: '',
+ requestID: 'set-1',
+ );
+ final mintData = await client.getLelantusMintData(
+ mints: requestedMints,
+ requestID: 'mint-1',
+ );
+ final serials = await client.getLelantusUsedCoinSerials(
+ requestID: 'serials-1',
+ startNumber: 0,
+ );
+ final latest = await client.getLelantusLatestCoinId(requestID: 'id-1');
+
+ expect(anonymitySet, GetAnonymitySetSampleData.data);
+ expect(mintData, mintMetadata);
+ expect(serials, GetUsedSerialsSampleData.serials);
+ expect(latest, 42);
+ expect(server.requestCount('lelantus.getanonymityset'), 1);
+ expect(server.requestCount('lelantus.getmintmetadata'), 1);
+ expect(server.requestCount('lelantus.getusedcoinserials'), 3);
+ expect(server.requestCount('lelantus.getlatestcoinid'), 1);
+ },
+ );
+
+ test('Lelantus wrappers surface current failure modes', () async {
+ final server = registerServer(
+ handlers: {
+ 'lelantus.getmintmetadata': (_) =>
+ throw Exception('mint metadata unavailable'),
+ 'lelantus.getusedcoinserials': (_) => ['not-a-map'],
+ 'lelantus.getlatestcoinid': (_) => 'forty-two',
+ },
+ );
+ final client = buildClient(clearServer: server, coin: firo());
+
+ await expectLater(
+ () => client.getLelantusMintData(
+ mints: const ['mint-a'],
+ requestID: 'mint-bad',
+ ),
+ throwsA(
+ isA<Exception>().having(
+ (error) => error.toString(),
+ 'message',
+ contains('mint metadata unavailable'),
+ ),
+ ),
+ );
+ await expectLater(
+ () => client.getLelantusUsedCoinSerials(
+ requestID: 'serials-bad',
+ startNumber: 0,
+ ),
+ throwsCurrentCastError(),
+ );
+ await expectLater(
+ () => client.getLelantusLatestCoinId(requestID: 'id-bad'),
+ throwsCurrentCastError(),
+ );
+ expect(server.requestCount('lelantus.getusedcoinserials'), 1);
+ });
+ });
+
+ group('Tor tests', () {
+ test('Tor not in use', () async {
+ when(prefs.useTor).thenReturn(false);
+ when(prefs.torKillSwitch).thenReturn(false);
+
+ final clearServer = registerServer(
+ handlers: {
+ 'blockchain.transaction.get': (_) => SampleGetTransactionData.txData0,
+ },
+ );
+ final torServer = registerServer(
+ handlers: {
+ 'blockchain.transaction.get': (_) => {'unexpected': true},
+ },
+ );
+
+ final client = buildClient(
+ clearServer: clearServer,
+ torServer: torServer,
+ coin: firo(),
+ );
+
+ final result = await client.getTransaction(
+ txHash: SampleGetTransactionData.txHash0,
+ verbose: true,
+ requestID: 'tor-off',
+ );
+
+ expect(result, SampleGetTransactionData.txData0);
+ expect(clearServer.requestCount('blockchain.transaction.get'), 1);
+ expect(torServer.requestCount('blockchain.transaction.get'), 0);
+ verify(prefs.useTor).called(greaterThanOrEqualTo(1));
+ expect(torService.statusReads, 0);
+ expect(torService.proxyInfoReads, 0);
+ });
+
+ test(
+ 'Tor in use but unavailable and killswitch off uses clearnet',
+ () async {
+ when(prefs.useTor).thenReturn(true);
+ when(prefs.torKillSwitch).thenReturn(false);
+ torService.currentStatus = TorConnectionStatus.disconnected;
+
+ final clearServer = registerServer(
+ handlers: {
+ 'blockchain.transaction.get': (_) =>
+ SampleGetTransactionData.txData0,
+ },
+ );
+ final torServer = registerServer(
+ handlers: {
+ 'blockchain.transaction.get': (_) => {'unexpected': true},
+ },
+ );
+
+ final client = buildClient(
+ clearServer: clearServer,
+ torServer: torServer,
+ coin: firo(),
+ );
+
+ final result = await client.getTransaction(
+ txHash: SampleGetTransactionData.txHash0,
+ verbose: true,
+ requestID: 'tor-fallback',
+ );
+
+ expect(result, SampleGetTransactionData.txData0);
+ expect(clearServer.requestCount('blockchain.transaction.get'), 1);
+ expect(torServer.requestCount('blockchain.transaction.get'), 0);
+ expect(torService.statusReads, greaterThanOrEqualTo(1));
+ expect(torService.proxyInfoReads, 0);
+ },
+ );
+
+ test('Tor in use and available uses the tor-backed adapter', () async {
+ when(prefs.useTor).thenReturn(true);
+ torService.currentStatus = TorConnectionStatus.connected;
+ torService.setProxyInfo((host: InternetAddress.loopbackIPv4, port: 9050));
+
+ final clearServer = registerServer(
+ handlers: {
+ 'blockchain.transaction.get': (_) => {'unexpected': true},
+ },
+ );
+ final torServer = registerServer(
+ handlers: {
+ 'blockchain.transaction.get': (_) => SampleGetTransactionData.txData0,
+ },
+ );
+
+ final client = buildClient(
+ clearServer: clearServer,
+ torServer: torServer,
+ coin: firo(),
+ );
+
+ final result = await client.getTransaction(
+ txHash: SampleGetTransactionData.txHash0,
+ verbose: true,
+ requestID: 'tor-on',
+ );
+
+ expect(result, SampleGetTransactionData.txData0);
+ expect(clearServer.requestCount('blockchain.transaction.get'), 0);
+ expect(torServer.requestCount('blockchain.transaction.get'), 1);
+ expect(torService.statusReads, greaterThanOrEqualTo(1));
+ expect(torService.proxyInfoReads, greaterThanOrEqualTo(1));
+ });
+
+ test('killswitch enabled throws before any adapter request', () async {
+ when(prefs.useTor).thenReturn(true);
+ when(prefs.torKillSwitch).thenReturn(true);
+ torService.currentStatus = TorConnectionStatus.disconnected;
+
+ final clearServer = registerServer(
+ handlers: {
+ 'blockchain.transaction.get': (_) => SampleGetTransactionData.txData0,
+ },
+ );
+
+ final client = buildClient(clearServer: clearServer, coin: firo());
+
+ await expectLater(
+ () => client.getTransaction(
+ txHash: SampleGetTransactionData.txHash0,
+ verbose: true,
+ requestID: 'tor-killswitch',
+ ),
+ throwsA(
+ isA<Exception>().having(
+ (error) => error.toString(),
+ 'message',
+ contains(
+ 'Tor preference and killswitch set but Tor is not enabled',
+ ),
+ ),
+ ),
+ );
+ expect(clearServer.requestCount('blockchain.transaction.get'), 0);
+ });
+ });
+}
diff --git a/test/hive/hive_ce_test_utils.dart b/test/hive/hive_ce_test_utils.dart
new file mode 100644
index 0000000..3ac973c
--- /dev/null
+++ b/test/hive/hive_ce_test_utils.dart
@@ -0,0 +1,82 @@
+import 'dart:async';
+import 'dart:io';
+
+import 'package:hive_ce/hive.dart';
+import 'package:stackwallet/db/hive/db.dart';
+
+const _helperPath = 'test/hive/hive_ce_test_utils.dart';
+const _defaultHiveCeTestTimeout = Duration(seconds: 15);
+
+Directory? _testHiveDirectory;
+
+Future<void> setUpHiveCeTest({
+ Duration timeout = _defaultHiveCeTestTimeout,
+}) async {
+ if (_testHiveDirectory != null) {
+ throw StateError(
+ '$_helperPath [init]: previous Hive CE temp directory '
+ '"${_testHiveDirectory!.path}" was not cleaned up before reinitialization.',
+ );
+ }
+
+ try {
+ await (() async {
+ final tempDirectory = await Directory.systemTemp.createTemp(
+ 'stack_wallet_hive_ce_test_',
+ );
+ Hive.init(tempDirectory.path);
+ DB.instance.hive.init(tempDirectory.path);
+ _testHiveDirectory = tempDirectory;
+ })().timeout(
+ timeout,
+ onTimeout: () => throw TimeoutException(
+ '$_helperPath [init]: timed out after ${timeout.inSeconds}s.',
+ ),
+ );
+ } catch (error, stackTrace) {
+ final tempDirectory = _testHiveDirectory;
+ _testHiveDirectory = null;
+
+ if (tempDirectory != null && await tempDirectory.exists()) {
+ await tempDirectory.delete(recursive: true);
+ }
+
+ Error.throwWithStackTrace(
+ StateError('$_helperPath [init]: $error'),
+ stackTrace,
+ );
+ }
+}
+
+Future<void> tearDownHiveCeTest({
+ Duration timeout = _defaultHiveCeTestTimeout,
+}) async {
+ final tempDirectory = _testHiveDirectory;
+ if (tempDirectory == null) {
+ throw StateError(
+ '$_helperPath [cleanup]: called before setUpHiveCeTest().',
+ );
+ }
+
+ _testHiveDirectory = null;
+
+ try {
+ await (() async {
+ await DB.instance.hive.close();
+ await Hive.close();
+ if (await tempDirectory.exists()) {
+ await tempDirectory.delete(recursive: true);
+ }
+ })().timeout(
+ timeout,
+ onTimeout: () => throw TimeoutException(
+ '$_helperPath [cleanup]: timed out after ${timeout.inSeconds}s.',
+ ),
+ );
+ } catch (error, stackTrace) {
+ Error.throwWithStackTrace(
+ StateError('$_helperPath [cleanup]: $error'),
+ stackTrace,
+ );
+ }
+}
diff --git a/test/pages/send_view/send_view_test.mocks.dart b/test/pages/send_view/send_view_test.mocks.dart
index 36200d3..531bdde 100644
--- a/test/pages/send_view/send_view_test.mocks.dart
+++ b/test/pages/send_view/send_view_test.mocks.dart
@@ -4,23 +4,24 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i10;
-import 'dart:typed_data' as _i19;
-import 'dart:ui' as _i14;
+import 'dart:typed_data' as _i20;
+import 'dart:ui' as _i15;
-import 'package:logger/logger.dart' as _i22;
+import 'package:logger/logger.dart' as _i23;
import 'package:mockito/mockito.dart' as _i1;
-import 'package:mockito/src/dummies.dart' as _i16;
+import 'package:mockito/src/dummies.dart' as _i17;
import 'package:stackwallet/db/isar/main_db.dart' as _i3;
-import 'package:stackwallet/models/isar/stack_theme.dart' as _i18;
+import 'package:stackwallet/models/epicbox_server_model.dart' as _i14;
+import 'package:stackwallet/models/isar/stack_theme.dart' as _i19;
import 'package:stackwallet/models/node_model.dart' as _i13;
import 'package:stackwallet/networking/http.dart' as _i7;
-import 'package:stackwallet/services/locale_service.dart' as _i15;
+import 'package:stackwallet/services/locale_service.dart' as _i16;
import 'package:stackwallet/services/node_service.dart' as _i2;
import 'package:stackwallet/services/wallets.dart' as _i9;
-import 'package:stackwallet/themes/theme_service.dart' as _i17;
-import 'package:stackwallet/utilities/amount/amount_unit.dart' as _i23;
-import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i21;
-import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i20;
+import 'package:stackwallet/themes/theme_service.dart' as _i18;
+import 'package:stackwallet/utilities/amount/amount_unit.dart' as _i24;
+import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i22;
+import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i21;
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
as _i6;
import 'package:stackwallet/utilities/prefs.dart' as _i12;
@@ -320,6 +321,64 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService {
)
as _i10.Future<void>);
+ @override
+ _i10.Future<void> updateDefaultEpicBoxes() =>
+ (super.noSuchMethod(
+ Invocation.method(#updateDefaultEpicBoxes, []),
+ returnValue: _i10.Future<void>.value(),
+ returnValueForMissingStub: _i10.Future<void>.value(),
+ )
+ as _i10.Future<void>);
+
+ @override
+ _i10.Future<void> setPrimaryEpicBox({
+ required _i14.EpicBoxServerModel? epicBox,
+ bool? shouldNotifyListeners = false,
+ }) =>
+ (super.noSuchMethod(
+ Invocation.method(#setPrimaryEpicBox, [], {
+ #epicBox: epicBox,
+ #shouldNotifyListeners: shouldNotifyListeners,
+ }),
+ returnValue: _i10.Future<void>.value(),
+ returnValueForMissingStub: _i10.Future<void>.value(),
+ )
+ as _i10.Future<void>);
+
+ @override
+ List<_i14.EpicBoxServerModel> getEpicBoxes() =>
+ (super.noSuchMethod(
+ Invocation.method(#getEpicBoxes, []),
+ returnValue: <_i14.EpicBoxServerModel>[],
+ )
+ as List<_i14.EpicBoxServerModel>);
+
+ @override
+ _i14.EpicBoxServerModel? getEpicBoxById({required String? id}) =>
+ (super.noSuchMethod(Invocation.method(#getEpicBoxById, [], {#id: id}))
+ as _i14.EpicBoxServerModel?);
+
+ @override
+ _i10.Future<void> addEpicBox(
+ _i14.EpicBoxServerModel? epicBox,
+ bool? shouldNotifyListeners,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(#addEpicBox, [epicBox, shouldNotifyListeners]),
+ returnValue: _i10.Future<void>.value(),
+ returnValueForMissingStub: _i10.Future<void>.value(),
+ )
+ as _i10.Future<void>);
+
+ @override
+ _i10.Future<void> deleteEpicBox(String? id, bool? shouldNotifyListeners) =>
+ (super.noSuchMethod(
+ Invocation.method(#deleteEpicBox, [id, shouldNotifyListeners]),
+ returnValue: _i10.Future<void>.value(),
+ returnValueForMissingStub: _i10.Future<void>.value(),
+ )
+ as _i10.Future<void>);
+
@override
_i10.Future<void> updateCommunityNodes() =>
(super.noSuchMethod(
@@ -330,13 +389,13 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService {
as _i10.Future<void>);
@override
- void addListener(_i14.VoidCallback? listener) => super.noSuchMethod(
+ void addListener(_i15.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null,
);
@override
- void removeListener(_i14.VoidCallback? listener) => super.noSuchMethod(
+ void removeListener(_i15.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(#removeListener, [listener]),
returnValueForMissingStub: null,
);
@@ -357,7 +416,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService {
/// A class which mocks [LocaleService].
///
/// See the documentation for Mockito's code generation for more information.
-class MockLocaleService extends _i1.Mock implements _i15.LocaleService {
+class MockLocaleService extends _i1.Mock implements _i16.LocaleService {
MockLocaleService() {
_i1.throwOnMissingStub(this);
}
@@ -366,7 +425,7 @@ class MockLocaleService extends _i1.Mock implements _i15.LocaleService {
String get locale =>
(super.noSuchMethod(
Invocation.getter(#locale),
- returnValue: _i16.dummyValue<String>(
+ returnValue: _i17.dummyValue<String>(
this,
Invocation.getter(#locale),
),
@@ -388,13 +447,13 @@ class MockLocaleService extends _i1.Mock implements _i15.LocaleService {
as _i10.Future<void>);
@override
- void addListener(_i14.VoidCallback? listener) => super.noSuchMethod(
+ void addListener(_i15.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null,
);
@override
- void removeListener(_i14.VoidCallback? listener) => super.noSuchMethod(
+ void removeListener(_i15.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(#removeListener, [listener]),
returnValueForMissingStub: null,
);
@@ -415,7 +474,7 @@ class MockLocaleService extends _i1.Mock implements _i15.LocaleService {
/// A class which mocks [ThemeService].
///
/// See the documentation for Mockito's code generation for more information.
-class MockThemeService extends _i1.Mock implements _i17.ThemeService {
+class MockThemeService extends _i1.Mock implements _i18.ThemeService {
MockThemeService() {
_i1.throwOnMissingStub(this);
}
@@ -437,12 +496,12 @@ class MockThemeService extends _i1.Mock implements _i17.ThemeService {
as _i7.HTTP);
@override
- List<_i18.StackTheme> get installedThemes =>
+ List<_i19.StackTheme> get installedThemes =>
(super.noSuchMethod(
Invocation.getter(#installedThemes),
- returnValue: <_i18.StackTheme>[],
+ returnValue: <_i19.StackTheme>[],
)
- as List<_i18.StackTheme>);
+ as List<_i19.StackTheme>);
@override
set client(_i7.HTTP? value) => super.noSuchMethod(
@@ -457,7 +516,7 @@ class MockThemeService extends _i1.Mock implements _i17.ThemeService {
);
@override
- _i10.Future<void> install({required _i19.Uint8List? themeArchiveData}) =>
+ _i10.Future<void> install({required _i20.Uint8List? themeArchiveData}) =>
(super.noSuchMethod(
Invocation.method(#install, [], {
#themeArchiveData: themeArchiveData,
@@ -494,29 +553,29 @@ class MockThemeService extends _i1.Mock implements _i17.ThemeService {
as _i10.Future<bool>);
@override
- _i10.Future<List<_i17.StackThemeMetaData>> fetchThemes() =>
+ _i10.Future<List<_i18.StackThemeMetaData>> fetchThemes() =>
(super.noSuchMethod(
Invocation.method(#fetchThemes, []),
- returnValue: _i10.Future<List<_i17.StackThemeMetaData>>.value(
- <_i17.StackThemeMetaData>[],
+ returnValue: _i10.Future<List<_i18.StackThemeMetaData>>.value(
+ <_i18.StackThemeMetaData>[],
),
)
- as _i10.Future<List<_i17.StackThemeMetaData>>);
+ as _i10.Future<List<_i18.StackThemeMetaData>>);
@override
- _i10.Future<_i19.Uint8List> fetchTheme({
- required _i17.StackThemeMetaData? themeMetaData,
+ _i10.Future<_i20.Uint8List> fetchTheme({
+ required _i18.StackThemeMetaData? themeMetaData,
}) =>
(super.noSuchMethod(
Invocation.method(#fetchTheme, [], {#themeMetaData: themeMetaData}),
- returnValue: _i10.Future<_i19.Uint8List>.value(_i19.Uint8List(0)),
+ returnValue: _i10.Future<_i20.Uint8List>.value(_i20.Uint8List(0)),
)
- as _i10.Future<_i19.Uint8List>);
+ as _i10.Future<_i20.Uint8List>);
@override
- _i18.StackTheme? getTheme({required String? themeId}) =>
+ _i19.StackTheme? getTheme({required String? themeId}) =>
(super.noSuchMethod(Invocation.method(#getTheme, [], {#themeId: themeId}))
- as _i18.StackTheme?);
+ as _i19.StackTheme?);
}
/// A class which mocks [Prefs].
@@ -562,12 +621,12 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs {
as List<String>);
@override
- _i20.SyncingType get syncType =>
+ _i21.SyncingType get syncType =>
(super.noSuchMethod(
Invocation.getter(#syncType),
- returnValue: _i20.SyncingType.currentWalletOnly,
+ returnValue: _i21.SyncingType.currentWalletOnly,
)
- as _i20.SyncingType);
+ as _i21.SyncingType);
@override
bool get wifiOnly =>
@@ -586,7 +645,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs {
String get language =>
(super.noSuchMethod(
Invocation.getter(#language),
- returnValue: _i16.dummyValue<String>(
+ returnValue: _i17.dummyValue<String>(
this,
Invocation.getter(#language),
),
@@ -597,7 +656,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs {
String get currency =>
(super.noSuchMethod(
Invocation.getter(#currency),
- returnValue: _i16.dummyValue<String>(
+ returnValue: _i17.dummyValue<String>(
this,
Invocation.getter(#currency),
),
@@ -659,12 +718,12 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs {
as bool);
@override
- _i21.BackupFrequencyType get backupFrequencyType =>
+ _i22.BackupFrequencyType get backupFrequencyType =>
(super.noSuchMethod(
Invocation.getter(#backupFrequencyType),
- returnValue: _i21.BackupFrequencyType.everyTenMinutes,
+ returnValue: _i22.BackupFrequencyType.everyTenMinutes,
)
- as _i21.BackupFrequencyType);
+ as _i22.BackupFrequencyType);
@override
bool get hideBlockExplorerWarning =>
@@ -707,7 +766,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs {
String get themeId =>
(super.noSuchMethod(
Invocation.getter(#themeId),
- returnValue: _i16.dummyValue<String>(
+ returnValue: _i17.dummyValue<String>(
this,
Invocation.getter(#themeId),
),
@@ -718,7 +777,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs {
String get systemBrightnessLightThemeId =>
(super.noSuchMethod(
Invocation.getter(#systemBrightnessLightThemeId),
- returnValue: _i16.dummyValue<String>(
+ returnValue: _i17.dummyValue<String>(
this,
Invocation.getter(#systemBrightnessLightThemeId),
),
@@ -729,7 +788,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs {
String get systemBrightnessDarkThemeId =>
(super.noSuchMethod(
Invocation.getter(#systemBrightnessDarkThemeId),
- returnValue: _i16.dummyValue<String>(
+ returnValue: _i17.dummyValue<String>(
this,
Invocation.getter(#systemBrightnessDarkThemeId),
),
@@ -763,12 +822,12 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs {
as bool);
@override
- _i22.Level get logLevel =>
+ _i23.Level get logLevel =>
(super.noSuchMethod(
Invocation.getter(#logLevel),
- returnValue: _i22.Level.all,
+ returnValue: _i23.Level.all,
)
- as _i22.Level);
+ as _i23.Level);
@override
({bool enabled, int minutes}) get autoLockInfo =>
@@ -811,7 +870,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs {
);
@override
- set syncType(_i20.SyncingType? syncType) => super.noSuchMethod(
+ set syncType(_i21.SyncingType? syncType) => super.noSuchMethod(
Invocation.setter(#syncType, syncType),
returnValueForMissingStub: null,
);
@@ -901,7 +960,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs {
);
@override
- set backupFrequencyType(_i21.BackupFrequencyType? backupFrequencyType) =>
+ set backupFrequencyType(_i22.BackupFrequencyType? backupFrequencyType) =>
super.noSuchMethod(
Invocation.setter(#backupFrequencyType, backupFrequencyType),
returnValueForMissingStub: null,
@@ -1008,7 +1067,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs {
);
@override
- set logLevel(_i22.Level? logLevel) => super.noSuchMethod(
+ set logLevel(_i23.Level? logLevel) => super.noSuchMethod(
Invocation.setter(#logLevel, logLevel),
returnValueForMissingStub: null,
);
@@ -1082,17 +1141,17 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs {
as _i10.Future<void>);
@override
- _i23.AmountUnit amountUnit(_i4.CryptoCurrency? coin) =>
+ _i24.AmountUnit amountUnit(_i4.CryptoCurrency? coin) =>
(super.noSuchMethod(
Invocation.method(#amountUnit, [coin]),
- returnValue: _i23.AmountUnit.normal,
+ returnValue: _i24.AmountUnit.normal,
)
- as _i23.AmountUnit);
+ as _i24.AmountUnit);
@override
void updateAmountUnit({
required _i4.CryptoCurrency? coin,
- required _i23.AmountUnit? amountUnit,
+ required _i24.AmountUnit? amountUnit,
}) => super.noSuchMethod(
Invocation.method(#updateAmountUnit, [], {
#coin: coin,
@@ -1142,13 +1201,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs {
);
@override
- void addListener(_i14.VoidCallback? listener) => super.noSuchMethod(
+ void addListener(_i15.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null,
);
@override
- void removeListener(_i14.VoidCallback? listener) => super.noSuchMethod(
+ void removeListener(_i15.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(#removeListener, [listener]),
returnValueForMissingStub: null,
);
diff --git a/test/price_test.dart b/test/price_test.dart
index e7a8b40..468295b 100644
--- a/test/price_test.dart
+++ b/test/price_test.dart
@@ -4,22 +4,21 @@ import 'dart:convert';
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
-import 'package:hive_ce/hive.dart';
-import 'package:hive_test/hive_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/networking/http.dart';
import 'package:stackwallet/services/price.dart';
+import 'hive/hive_ce_test_utils.dart';
import 'price_test.mocks.dart';
@GenerateMocks([HTTP])
void main() {
setUp(() async {
- await setUpTestHive();
- await Hive.openBox<dynamic>(DB.boxNamePriceCache);
- await Hive.openBox<dynamic>(DB.boxNamePrefs);
+ await setUpHiveCeTest();
+ await DB.instance.hive.openBox<dynamic>(DB.boxNamePriceCache);
+ await DB.instance.hive.openBox<dynamic>(DB.boxNamePrefs);
await DB.instance.put<dynamic>(
boxName: DB.boxNamePrefs,
key: "externalCalls",
@@ -27,19 +26,71 @@ void main() {
);
});
+ void expectFetchedPriceSnapshot(String prices) {
+ expect(
+ prices,
+ contains("Instance of 'Bitcoin': (change24h: 0.0, value: 1)"),
+ );
+ expect(
+ prices,
+ contains(
+ "Instance of 'Monero': (change24h: -0.77656, value: 0.00717236)",
+ ),
+ );
+ expect(
+ prices,
+ contains(
+ "Instance of 'Dogecoin': (change24h: -2.68533, value: 0.00000315)",
+ ),
+ );
+ expect(
+ prices,
+ contains(
+ "Instance of 'Epiccash': (change24h: 7.27524, value: 0.00002803)",
+ ),
+ );
+ expect(
+ prices,
+ contains("Instance of 'Firo': (change24h: -0.89304, value: 0.0001096)"),
+ );
+ expect(
+ prices,
+ contains("Instance of 'Xelis': (change24h: 5.67, value: 0.00001234)"),
+ );
+ expect(
+ prices,
+ contains("Instance of 'Cardano': (change24h: 0.0, value: 0)"),
+ );
+ expect(
+ prices,
+ contains("Instance of 'Fact0rn': (change24h: 0.0, value: 0)"),
+ );
+ expect(
+ prices,
+ contains("Instance of 'Peercoin': (change24h: 0.0, value: 0)"),
+ );
+ expect(
+ prices,
+ contains("Instance of 'Salvium': (change24h: 0.0, value: 0)"),
+ );
+ expect(
+ prices,
+ contains("Instance of 'Solana': (change24h: 0.0, value: 0)"),
+ );
+ expect(prices, isNot('{}'));
+ }
+
+ void expectEmptyPriceSnapshot(String prices) {
+ expect(prices, '{}');
+ }
+
test("getPricesAnd24hChange fetch", () async {
final client = MockHTTP();
when(
client.get(
proxyInfo: null,
- url: Uri.parse(
- "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids"
- "=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,bitcoin-cash"
- ",namecoin,wownero,ethereum,particl,nano,banano,stellar,tezos,xelis"
- "&order=market_cap_desc&per_page=50"
- "&page=1&sparkline=false",
- ),
+ url: anyNamed('url'),
headers: {'Content-Type': 'application/json'},
),
).thenAnswer(
@@ -115,44 +166,11 @@ void main() {
final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
- expect(
- price.toString(),
- '{'
- 'Coin.bitcoin: [1, 0.0], '
- 'Coin.monero: [0.00717236, -0.77656], '
- 'Coin.banano: [0, 0.0], '
- 'Coin.bitcoincash: [0, 0.0], '
- 'Coin.dogecoin: [0.00000315, -2.68533], '
- 'Coin.eCash: [0, 0.0], '
- 'Coin.epicCash: [0.00002803, 7.27524], '
- 'Coin.ethereum: [0, 0.0], '
- 'Coin.firo: [0.0001096, -0.89304], '
- 'Coin.litecoin: [0, 0.0], '
- 'Coin.namecoin: [0, 0.0], '
- 'Coin.nano: [0, 0.0], '
- 'Coin.particl: [0, 0.0], '
- 'Coin.stellar: [0, 0.0], '
- 'Coin.tezos: [0, 0.0], '
- 'Coin.wownero: [0, 0.0], '
- 'Coin.bitcoinTestNet: [0, 0.0], '
- 'Coin.bitcoincashTestnet: [0, 0.0], '
- 'Coin.dogecoinTestNet: [0, 0.0], '
- 'Coin.firoTestNet: [0, 0.0], '
- 'Coin.litecoinTestNet: [0, 0.0], '
- 'Coin.stellarTestnet: [0, 0.0], '
- 'Coin.xelis: [0.00001234, 5.67]'
- '}',
- );
+ expectFetchedPriceSnapshot(price.toString());
verify(
client.get(
proxyInfo: null,
- url: Uri.parse(
- "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc"
- "&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
- "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano,stellar"
- ",tezos,xelis"
- "&order=market_cap_desc&per_page=50&page=1&sparkline=false",
- ),
+ url: anyNamed('url'),
headers: {'Content-Type': 'application/json'},
),
).called(1);
@@ -166,13 +184,7 @@ void main() {
when(
client.get(
proxyInfo: null,
- url: Uri.parse(
- "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&"
- "ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
- "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano,stellar"
- ",tezos,xelis"
- "&order=market_cap_desc&per_page=50&page=1&sparkline=false",
- ),
+ url: anyNamed('url'),
headers: {'Content-Type': 'application/json'},
),
).thenAnswer(
@@ -254,43 +266,13 @@ void main() {
baseCurrency: "btc",
);
- expect(
- cachedPrice.toString(),
- '{'
- 'Coin.bitcoin: [1, 0.0], '
- 'Coin.monero: [0.00717236, -0.77656], '
- 'Coin.banano: [0, 0.0], Coin.bitcoincash: [0, 0.0], '
- 'Coin.dogecoin: [0.00000315, -2.68533], '
- 'Coin.eCash: [0, 0.0], '
- 'Coin.epicCash: [0.00002803, 7.27524], Coin.ethereum: [0, 0.0], '
- 'Coin.firo: [0.0001096, -0.89304], '
- 'Coin.litecoin: [0, 0.0], '
- 'Coin.namecoin: [0, 0.0], '
- 'Coin.nano: [0, 0.0], '
- 'Coin.particl: [0, 0.0], '
- 'Coin.stellar: [0, 0.0], '
- 'Coin.tezos: [0, 0.0], '
- 'Coin.wownero: [0, 0.0], '
- 'Coin.bitcoinTestNet: [0, 0.0], '
- 'Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], '
- 'Coin.firoTestNet: [0, 0.0], '
- 'Coin.litecoinTestNet: [0, 0.0], '
- 'Coin.stellarTestnet: [0, 0.0], '
- 'Coin.xelis: [0.00001234, 5.67]'
- '}',
- );
+ expectFetchedPriceSnapshot(cachedPrice.toString());
// verify only called once during filling of cache
verify(
client.get(
proxyInfo: null,
- url: Uri.parse(
- "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids"
- "=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
- "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano,stellar"
- ",tezos,xelis"
- "&order=market_cap_desc&per_page=50&page=1&sparkline=false",
- ),
+ url: anyNamed('url'),
headers: {'Content-Type': 'application/json'},
),
).called(1);
@@ -304,13 +286,7 @@ void main() {
when(
client.get(
proxyInfo: null,
- url: Uri.parse(
- "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc"
- "&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
- "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano,stellar"
- ",tezos,xelis"
- "&order=market_cap_desc&per_page=50&page=1&sparkline=false",
- ),
+ url: anyNamed('url'),
headers: {'Content-Type': 'application/json'},
),
).thenAnswer(
@@ -386,33 +362,7 @@ void main() {
final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
- expect(
- price.toString(),
- '{'
- 'Coin.bitcoin: [0, 0.0], Coin.monero: [0, 0.0], '
- 'Coin.banano: [0, 0.0], '
- 'Coin.bitcoincash: [0, 0.0], '
- 'Coin.dogecoin: [0, 0.0], '
- 'Coin.eCash: [0, 0.0], '
- 'Coin.epicCash: [0, 0.0], '
- 'Coin.ethereum: [0, 0.0], '
- 'Coin.firo: [0, 0.0], '
- 'Coin.litecoin: [0, 0.0], '
- 'Coin.namecoin: [0, 0.0], '
- 'Coin.nano: [0, 0.0], '
- 'Coin.particl: [0, 0.0], '
- 'Coin.stellar: [0, 0.0], '
- 'Coin.tezos: [0, 0.0], '
- 'Coin.wownero: [0, 0.0], '
- 'Coin.bitcoinTestNet: [0, 0.0], '
- 'Coin.bitcoincashTestnet: [0, 0.0], '
- 'Coin.dogecoinTestNet: [0, 0.0], '
- 'Coin.firoTestNet: [0, 0.0], '
- 'Coin.litecoinTestNet: [0, 0.0], '
- 'Coin.stellarTestnet: [0, 0.0], '
- 'Coin.xelis: [0, 0.0]'
- '}',
- );
+ expectEmptyPriceSnapshot(price.toString());
});
test("no internet available", () async {
@@ -421,13 +371,7 @@ void main() {
when(
client.get(
proxyInfo: null,
- url: Uri.parse(
- "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc"
- "&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
- "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano,stellar"
- ",tezos,xelis"
- "&order=market_cap_desc&per_page=50&page=1&sparkline=false",
- ),
+ url: anyNamed('url'),
headers: {'Content-Type': 'application/json'},
),
).thenThrow(
@@ -441,37 +385,10 @@ void main() {
final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
- expect(
- price.toString(),
- '{'
- 'Coin.bitcoin: [0, 0.0], '
- 'Coin.monero: [0, 0.0], '
- 'Coin.banano: [0, 0.0], '
- 'Coin.bitcoincash: [0, 0.0], '
- 'Coin.dogecoin: [0, 0.0], '
- 'Coin.eCash: [0, 0.0], '
- 'Coin.epicCash: [0, 0.0], '
- 'Coin.ethereum: [0, 0.0], '
- 'Coin.firo: [0, 0.0], '
- 'Coin.litecoin: [0, 0.0], '
- 'Coin.namecoin: [0, 0.0], '
- 'Coin.nano: [0, 0.0], '
- 'Coin.particl: [0, 0.0], '
- 'Coin.stellar: [0, 0.0], '
- 'Coin.tezos: [0, 0.0], '
- 'Coin.wownero: [0, 0.0], '
- 'Coin.bitcoinTestNet: [0, 0.0], '
- 'Coin.bitcoincashTestnet: [0, 0.0], '
- 'Coin.dogecoinTestNet: [0, 0.0], '
- 'Coin.firoTestNet: [0, 0.0], '
- 'Coin.litecoinTestNet: [0, 0.0], '
- 'Coin.stellarTestnet: [0, 0.0], '
- 'Coin.xelis: [0, 0.0]'
- '}',
- );
+ expectEmptyPriceSnapshot(price.toString());
});
tearDown(() async {
- await tearDownTestHive();
+ await tearDownHiveCeTest();
});
}
diff --git a/test/price_test.mocks.dart b/test/price_test.mocks.dart
index 36619fe..a7a491f 100644
--- a/test/price_test.mocks.dart
+++ b/test/price_test.mocks.dart
@@ -43,12 +43,14 @@ class MockHTTP extends _i1.Mock implements _i2.HTTP {
required Uri? url,
Map<String, String>? headers,
required ({_i4.InternetAddress host, int port})? proxyInfo,
+ Duration? connectionTimeout,
}) =>
(super.noSuchMethod(
Invocation.method(#get, [], {
#url: url,
#headers: headers,
#proxyInfo: proxyInfo,
+ #connectionTimeout: connectionTimeout,
}),
returnValue: _i3.Future<_i2.Response>.value(
_FakeResponse_0(
@@ -57,6 +59,7 @@ class MockHTTP extends _i1.Mock implements _i2.HTTP {
#url: url,
#headers: headers,
#proxyInfo: proxyInfo,
+ #connectionTimeout: connectionTimeout,
}),
),
),
@@ -93,4 +96,57 @@ class MockHTTP extends _i1.Mock implements _i2.HTTP {
),
)
as _i3.Future<_i2.Response>);
+
+ @override
+ _i3.Future<_i2.Response> patch({
+ required Uri? url,
+ Map<String, String>? headers,
+ Object? body,
+ required ({_i4.InternetAddress host, int port})? proxyInfo,
+ }) =>
+ (super.noSuchMethod(
+ Invocation.method(#patch, [], {
+ #url: url,
+ #headers: headers,
+ #body: body,
+ #proxyInfo: proxyInfo,
+ }),
+ returnValue: _i3.Future<_i2.Response>.value(
+ _FakeResponse_0(
+ this,
+ Invocation.method(#patch, [], {
+ #url: url,
+ #headers: headers,
+ #body: body,
+ #proxyInfo: proxyInfo,
+ }),
+ ),
+ ),
+ )
+ as _i3.Future<_i2.Response>);
+
+ @override
+ _i3.Future<_i2.Response> delete({
+ required Uri? url,
+ Map<String, String>? headers,
+ required ({_i4.InternetAddress host, int port})? proxyInfo,
+ }) =>
+ (super.noSuchMethod(
+ Invocation.method(#delete, [], {
+ #url: url,
+ #headers: headers,
+ #proxyInfo: proxyInfo,
+ }),
+ returnValue: _i3.Future<_i2.Response>.value(
+ _FakeResponse_0(
+ this,
+ Invocation.method(#delete, [], {
+ #url: url,
+ #headers: headers,
+ #proxyInfo: proxyInfo,
+ }),
+ ),
+ ),
+ )
+ as _i3.Future<_i2.Response>);
}
diff --git a/test/screen_tests/lockscreen_view_screen_test.dart b/test/screen_tests/lockscreen_view_screen_test.dart
index 8019ad2..c513f71 100644
--- a/test/screen_tests/lockscreen_view_screen_test.dart
+++ b/test/screen_tests/lockscreen_view_screen_test.dart
@@ -1,312 +1,227 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/annotations.dart';
-// import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart';
-
-import 'package:stackwallet/services/node_service.dart';
-import 'package:stackwallet/services/wallets_service.dart';
-
-@GenerateMocks(
- [],
- customMocks: [
- MockSpec<WalletsService>(),
- MockSpec<NodeService>(),
- ],
-)
+import 'package:mockito/mockito.dart';
+import 'package:stackwallet/models/isar/stack_theme.dart';
+import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart';
+import 'package:stackwallet/providers/global/duress_provider.dart';
+import 'package:stackwallet/providers/global/prefs_provider.dart';
+import 'package:stackwallet/themes/stack_colors.dart';
+import 'package:stackwallet/themes/theme_service.dart';
+import 'package:stackwallet/utilities/biometrics.dart';
+import 'package:stackwallet/widgets/custom_pin_put/pin_keyboard.dart';
+
+import '../sample_data/theme_json.dart';
+import '../widget_tests/custom_loading_overlay_test.mocks.dart';
+import '../widget_tests/node_options_sheet_test.mocks.dart';
+import '../widget_tests/support/platform_test_overrides.dart';
+
+class SpyBiometrics extends Biometrics {
+ SpyBiometrics({this.result = false});
+
+ final bool result;
+ int calls = 0;
+
+ @override
+ Future<bool> authenticate({
+ required String cancelButtonText,
+ required String localizedReason,
+ required String title,
+ }) async {
+ calls += 1;
+ return result;
+ }
+}
+
void main() {
- testWidgets("LockscreenView builds correctly", (tester) async {
- // final navigator = mockingjay.MockNavigator();
- // final walletsService = MockWalletsService();
- // final nodeService = MockNodeService();
- // final wallet = MockManager();
- // final secureStore = FakeSecureStorage();
- //
- // secureStore.write(key: "walletID", value: "1234");
- //
- // when(walletsService.getWalletId("My Firo Wallet"))
- // .thenAnswer((_) async => "walletID");
- //
- // await tester.pumpWidget(
- // MaterialApp(
- // home: mockingjay.MockNavigatorProvider(
- // navigator: navigator,
- // child: MultiProvider(
- // providers: [
- // ChangeNotifierProvider<WalletsService>(
- // create: (_) => walletsService,
- // ),
- // ChangeNotifierProvider<NodeService>(
- // create: (_) => nodeService,
- // ),
- // ChangeNotifierProvider<Manager>(
- // create: (_) => manager,
- // ),
- // ],
- // child: LockscreenView(
- // routeOnSuccess: "/mainview",
- // secureStore: secureStore,
- // ),
- // ),
- // ),
- // ),
- // );
- //
- // await tester.pumpAndSettle();
- //
- // expect(find.byType(AppBarIconButton), findsOneWidget);
- // expect(find.byType(SvgPicture), findsOneWidget);
- //
- // expect(find.text("My Firo Wallet"), findsOneWidget);
- // expect(find.text("Enter PIN"), findsOneWidget);
- //
- // expect(find.byType(CustomPinPut), findsOneWidget);
- });
+ ThemeData buildTheme() {
+ return ThemeData(
+ extensions: [
+ StackColors.fromStackColorTheme(
+ StackTheme.fromJson(json: lightThemeJsonMap),
+ ),
+ ],
+ );
+ }
- testWidgets("enter valid pin", (tester) async {
- // final navigator = mockingjay.MockNavigator();
- // final walletsService = MockWalletsService();
- // final nodeService = MockNodeService();
- // final wallet = MockManager();
- // final secureStore = FakeSecureStorage();
- //
- // secureStore.write(key: "walletID_pin", value: "1234");
- //
- // when(walletsService.getWalletId("My Firo Wallet"))
- // .thenAnswer((_) async => "walletID");
- //
- // mockingjay
- // .when(() => navigator.pushReplacementNamed("/mainview"))
- // .thenAnswer((_) async => {});
- //
- // await tester.pumpWidget(
- // MaterialApp(
- // home: mockingjay.MockNavigatorProvider(
- // navigator: navigator,
- // child: MultiProvider(
- // providers: [
- // ChangeNotifierProvider<WalletsService>(
- // create: (_) => walletsService,
- // ),
- // ChangeNotifierProvider<NodeService>(
- // create: (_) => nodeService,
- // ),
- // ChangeNotifierProvider<Manager>(
- // create: (_) => manager,
- // ),
- // ],
- // child: LockscreenView(
- // routeOnSuccess: "/mainview",
- // secureStore: secureStore,
- // ),
- // ),
- // ),
- // ),
- // );
- //
- // await tester.pumpAndSettle();
- //
- // await tester.tap(find.byWidgetPredicate(
- // (widget) => widget is NumberKey && widget.number == "1"));
- // await tester.pump(const Duration(milliseconds: 200));
- // await tester.tap(find.byWidgetPredicate(
- // (widget) => widget is NumberKey && widget.number == "2"));
- // await tester.pump(const Duration(milliseconds: 200));
- // await tester.tap(find.byWidgetPredicate(
- // (widget) => widget is NumberKey && widget.number == "3"));
- // await tester.pump(const Duration(milliseconds: 200));
- // await tester.tap(find.byWidgetPredicate(
- // (widget) => widget is NumberKey && widget.number == "4"));
- // await tester.pump(const Duration(milliseconds: 500));
- //
- // expect(find.text("PIN code correct. Unlocking wallet..."), findsOneWidget);
- //
- // await tester.pump(const Duration(seconds: 2));
- //
- // mockingjay
- // .verify(() => navigator.pushReplacementNamed("/mainview"))
- // .called(1);
- });
+ void stubPrefs(MockPrefs prefs) {
+ when(prefs.isInitialized).thenReturn(true);
+ when(prefs.randomizePIN).thenReturn(false);
+ when(prefs.autoPin).thenReturn(false);
+ when(prefs.useBiometrics).thenReturn(false);
+ when(prefs.biometricsDuress).thenReturn(false);
+ when(prefs.lastUnlocked).thenReturn(0);
+ }
+
+ Future<ProviderContainer> pumpLockscreenView(
+ WidgetTester tester, {
+ required MockPrefs prefs,
+ required SpyBiometrics biometrics,
+ required List<Override> overrides,
+ required bool isDuress,
+ VoidCallback? onSuccess,
+ }) async {
+ final mockThemeService = MockThemeService();
+ final theme = StackTheme.fromJson(json: lightThemeJsonMap);
+
+ when(mockThemeService.getTheme(themeId: 'light')).thenReturn(theme);
+
+ final container = ProviderContainer(
+ overrides: [
+ pThemeService.overrideWithValue(mockThemeService),
+ prefsChangeNotifierProvider.overrideWithValue(prefs),
+ ...overrides,
+ ],
+ );
+
+ addTearDown(container.dispose);
+ container.read(pDuress.notifier).state = isDuress;
+
+ await tester.pumpWidget(
+ UncontrolledProviderScope(
+ container: container,
+ child: MaterialApp(
+ theme: buildTheme(),
+ routes: {
+ '/unlocked': (_) => const Scaffold(body: Text('unlocked route')),
+ },
+ home: LockscreenView(
+ routeOnSuccess: '/unlocked',
+ biometricsAuthenticationTitle: 'Unlock wallet',
+ biometricsLocalizedReason: 'Unlock Stack Wallet',
+ biometricsCancelButtonString: 'Cancel',
+ biometrics: biometrics,
+ onSuccess: onSuccess,
+ ),
+ ),
+ ),
+ );
+
+ await tester.pumpAndSettle();
+ return container;
+ }
+
+ Future<void> tapDigit(WidgetTester tester, String digit) async {
+ await tester.tap(
+ find.byWidgetPredicate(
+ (widget) => widget is NumberKey && widget.number == digit,
+ ),
+ );
+ await tester.pump(const Duration(milliseconds: 250));
+ }
+
+ Future<void> enterAndSubmitPin(WidgetTester tester, String pin) async {
+ for (final digit in pin.split('')) {
+ await tapDigit(tester, digit);
+ }
+
+ await tester.tap(find.byType(SubmitKey));
+ await tester.pump();
+ }
- testWidgets("wallet initialization fails", (tester) async {
- // final navigator = mockingjay.MockNavigator();
- // final walletsService = MockWalletsService();
- // final nodeService = MockNodeService();
- // final wallet = MockManager();
- // final secureStore = FakeSecureStorage();
- //
- // secureStore.write(key: "walletID_pin", value: "1234");
- //
- // when(walletsService.getWalletId("My Firo Wallet"))
- // .thenAnswer((_) async => "walletID");
- //
- // mockingjay
- // .when(() => navigator.pushReplacementNamed("/mainview"))
- // .thenAnswer((_) async => {});
- //
- // await tester.pumpWidget(
- // MaterialApp(
- // home: mockingjay.MockNavigatorProvider(
- // navigator: navigator,
- // child: MultiProvider(
- // providers: [
- // ChangeNotifierProvider<WalletsService>(
- // create: (_) => walletsService,
- // ),
- // ChangeNotifierProvider<NodeService>(
- // create: (_) => nodeService,
- // ),
- // ChangeNotifierProvider<Manager>(
- // create: (_) => manager,
- // ),
- // ],
- // child: LockscreenView(
- // routeOnSuccess: "/mainview",
- // secureStore: secureStore,
- // ),
- // ),
- // ),
- // ),
- // );
- //
- // await tester.pumpAndSettle();
- //
- // await tester.tap(find.byWidgetPredicate(
- // (widget) => widget is NumberKey && widget.number == "1"));
- // await tester.pump(const Duration(milliseconds: 200));
- // await tester.tap(find.byWidgetPredicate(
- // (widget) => widget is NumberKey && widget.number == "2"));
- // await tester.pump(const Duration(milliseconds: 200));
- // await tester.tap(find.byWidgetPredicate(
- // (widget) => widget is NumberKey && widget.number == "3"));
- // await tester.pump(const Duration(milliseconds: 200));
- // await tester.tap(find.byWidgetPredicate(
- // (widget) => widget is NumberKey && widget.number == "4"));
- // await tester.pump(const Duration(milliseconds: 500));
- //
- // expect(find.text("PIN code correct. Unlocking wallet..."), findsOneWidget);
- //
- // await tester.pump(const Duration(seconds: 2));
- //
- // expect(
- // find.text(
- // "Failed to connect to network. Check your internet connection and make sure the Electrum X node you are connected to is not having any issues."),
- // findsOneWidget);
- //
- // await tester.tap(find.byKey(Key("campfireAlertOKButtonKey")));
- // await tester.pump(const Duration(seconds: 2));
- // await tester.pump(const Duration(seconds: 2));
- //
- // expect(
- // find.text(
- // "Failed to connect to network. Check your internet connection and make sure the Electrum X node you are connected to is not having any issues."),
- // findsNothing);
- //
- // mockingjay
- // .verify(() => navigator.pushReplacementNamed("/mainview"))
- // .called(1);
+ testWidgets('valid standard PIN unlocks through fake storage seam', (
+ tester,
+ ) async {
+ final prefs = MockPrefs();
+ final biometrics = SpyBiometrics();
+ final platformOverrides = await createPlatformTestOverrides(
+ secureStorageEntries: {kPinKey: '1234', kDuressPinKey: '9876'},
+ );
+ var onSuccessCalls = 0;
+
+ stubPrefs(prefs);
+
+ await pumpLockscreenView(
+ tester,
+ prefs: prefs,
+ biometrics: biometrics,
+ overrides: platformOverrides.overrides,
+ isDuress: false,
+ onSuccess: () => onSuccessCalls += 1,
+ );
+
+ expect(find.text('Enter PIN'), findsOneWidget);
+ expect(
+ platformOverrides.secureStorage.writtenKeys,
+ containsAll(<String>[kPinKey, kDuressPinKey]),
+ );
+
+ await enterAndSubmitPin(tester, '1234');
+ await tester.pumpAndSettle(const Duration(milliseconds: 200));
+
+ expect(platformOverrides.secureStorage.readKeys, <String>[kPinKey]);
+ expect(platformOverrides.secureStorage.reads, 1);
+ expect(onSuccessCalls, 1);
+ expect(biometrics.calls, 0);
+ expect(find.text('unlocked route'), findsOneWidget);
+
+ verify(prefs.lastUnlocked = any).called(1);
});
- testWidgets("enter invalid pin", (tester) async {
- // final navigator = mockingjay.MockNavigator();
- // final walletsService = MockWalletsService();
- // final nodeService = MockNodeService();
- // final wallet = MockManager();
- // final secureStore = FakeSecureStorage();
- //
- // secureStore.write(key: "walletID_pin", value: "1234");
- //
- // when(walletsService.getWalletId("My Firo Wallet"))
- // .thenAnswer((_) async => "walletID");
- //
- // mockingjay
- // .when(() => navigator.pushReplacementNamed("/mainview"))
- // .thenAnswer((_) async => {});
-
- // await tester.pumpWidget(
- // MaterialApp(
- // home: mockingjay.MockNavigatorProvider(
- // navigator: navigator,
- // child: MultiProvider(
- // providers: [
- // ChangeNotifierProvider<WalletsService>(
- // create: (_) => walletsService,
- // ),
- // ChangeNotifierProvider<NodeService>(
- // create: (_) => nodeService,
- // ),
- // ChangeNotifierProvider<Manager>(
- // create: (_) => manager,
- // ),
- // ],
- // child: LockscreenView(
- // routeOnSuccess: "/mainview",
- // secureStore: secureStore,
- // ),
- // ),
- // ),
- // ),
- // );
-
- // await tester.pumpAndSettle();
- //
- // await tester.tap(find.byWidgetPredicate(
- // (widget) => widget is NumberKey && widget.number == "1"));
- // await tester.pump(const Duration(milliseconds: 200));
- // await tester.tap(find.byWidgetPredicate(
- // (widget) => widget is NumberKey && widget.number == "1"));
- // await tester.pump(const Duration(milliseconds: 200));
- // await tester.tap(find.byWidgetPredicate(
- // (widget) => widget is NumberKey && widget.number == "3"));
- // await tester.pump(const Duration(milliseconds: 200));
- // await tester.tap(find.byWidgetPredicate(
- // (widget) => widget is NumberKey && widget.number == "4"));
- // await tester.pump(const Duration(milliseconds: 500));
- //
- // expect(find.text("Incorrect PIN. Please try again"), findsOneWidget);
- //
- // await tester.pump(const Duration(seconds: 2));
- //
- // mockingjay.verifyNever(() => navigator.pushReplacementNamed("/mainview"));
+ testWidgets('duress mode unlocks with the duress PIN only', (tester) async {
+ final prefs = MockPrefs();
+ final biometrics = SpyBiometrics();
+ final platformOverrides = await createPlatformTestOverrides(
+ secureStorageEntries: {kPinKey: '1234', kDuressPinKey: '9876'},
+ );
+ var onSuccessCalls = 0;
+
+ stubPrefs(prefs);
+
+ final container = await pumpLockscreenView(
+ tester,
+ prefs: prefs,
+ biometrics: biometrics,
+ overrides: platformOverrides.overrides,
+ isDuress: true,
+ onSuccess: () => onSuccessCalls += 1,
+ );
+
+ await enterAndSubmitPin(tester, '9876');
+ await tester.pumpAndSettle(const Duration(milliseconds: 200));
+
+ expect(platformOverrides.secureStorage.readKeys, <String>[kDuressPinKey]);
+ expect(platformOverrides.secureStorage.reads, 1);
+ expect(container.read(pDuress), isTrue);
+ expect(onSuccessCalls, 1);
+ expect(biometrics.calls, 0);
+ expect(find.text('unlocked route'), findsOneWidget);
+
+ verify(prefs.lastUnlocked = any).called(1);
});
- testWidgets("tap back", (tester) async {
- // final navigator = mockingjay.MockNavigator();
- // final walletsService = MockWalletsService();
- // final nodeService = MockNodeService();
- // final wallet = MockManager();
- // final secureStore = FakeSecureStorage();
- //
- // mockingjay.when(() => navigator.pop()).thenAnswer((_) async => {});
-
- // await tester.pumpWidget(
- // MaterialApp(
- // home: mockingjay.MockNavigatorProvider(
- // navigator: navigator,
- // child: MultiProvider(
- // providers: [
- // ChangeNotifierProvider<WalletsService>(
- // create: (_) => walletsService,
- // ),
- // ChangeNotifierProvider<NodeService>(
- // create: (_) => nodeService,
- // ),
- // ChangeNotifierProvider<Manager>(
- // create: (_) => manager,
- // ),
- // ],
- // child: LockscreenView(
- // routeOnSuccess: "/mainview",
- // secureStore: secureStore,
- // ),
- // ),
- // ),
- // ),
- // );
-
- // await tester.pumpAndSettle();
- //
- // await tester.tap(find.byType(AppBarIconButton));
- // await tester.pumpAndSettle();
- //
- // mockingjay.verify(() => navigator.pop()).called(1);
+ testWidgets('duress mode rejects the standard PIN without unlocking', (
+ tester,
+ ) async {
+ final prefs = MockPrefs();
+ final biometrics = SpyBiometrics();
+ final platformOverrides = await createPlatformTestOverrides(
+ secureStorageEntries: {kPinKey: '1234', kDuressPinKey: '9876'},
+ );
+ var onSuccessCalls = 0;
+
+ stubPrefs(prefs);
+
+ final container = await pumpLockscreenView(
+ tester,
+ prefs: prefs,
+ biometrics: biometrics,
+ overrides: platformOverrides.overrides,
+ isDuress: true,
+ onSuccess: () => onSuccessCalls += 1,
+ );
+
+ await enterAndSubmitPin(tester, '1234');
+ await tester.pump(const Duration(milliseconds: 900));
+
+ expect(platformOverrides.secureStorage.readKeys, <String>[kDuressPinKey]);
+ expect(platformOverrides.secureStorage.reads, 1);
+ expect(container.read(pDuress), isTrue);
+ expect(onSuccessCalls, 0);
+ expect(biometrics.calls, 0);
+ expect(find.text('Enter PIN'), findsOneWidget);
+ expect(find.text('unlocked route'), findsNothing);
+
+ verifyNever(prefs.lastUnlocked = any);
});
}
diff --git a/test/screen_tests/lockscreen_view_screen_test.mocks.dart b/test/screen_tests/lockscreen_view_screen_test.mocks.dart
deleted file mode 100644
index c54cd49..0000000
--- a/test/screen_tests/lockscreen_view_screen_test.mocks.dart
+++ /dev/null
@@ -1,254 +0,0 @@
-// Mocks generated by Mockito 5.4.6 from annotations
-// in stackwallet/test/screen_tests/lockscreen_view_screen_test.dart.
-// Do not manually edit this file.
-
-// ignore_for_file: no_leading_underscores_for_library_prefixes
-import 'dart:async' as _i4;
-import 'dart:ui' as _i5;
-
-import 'package:mockito/mockito.dart' as _i1;
-import 'package:stackwallet/models/node_model.dart' as _i7;
-import 'package:stackwallet/services/node_service.dart' as _i6;
-import 'package:stackwallet/services/wallets_service.dart' as _i3;
-import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
- as _i2;
-import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'
- as _i8;
-
-// ignore_for_file: type=lint
-// ignore_for_file: avoid_redundant_argument_values
-// ignore_for_file: avoid_setters_without_getters
-// ignore_for_file: comment_references
-// ignore_for_file: deprecated_member_use
-// ignore_for_file: deprecated_member_use_from_same_package
-// ignore_for_file: implementation_imports
-// ignore_for_file: invalid_use_of_visible_for_testing_member
-// ignore_for_file: must_be_immutable
-// ignore_for_file: prefer_const_constructors
-// ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: camel_case_types
-// ignore_for_file: subtype_of_sealed_class
-// ignore_for_file: invalid_use_of_internal_member
-
-class _FakeSecureStorageInterface_0 extends _i1.SmartFake
- implements _i2.SecureStorageInterface {
- _FakeSecureStorageInterface_0(Object parent, Invocation parentInvocation)
- : super(parent, parentInvocation);
-}
-
-/// A class which mocks [WalletsService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockWalletsService extends _i1.Mock implements _i3.WalletsService {
- MockWalletsService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i4.Future<Map<String, _i3.WalletInfo>> get walletNames =>
- (super.noSuchMethod(
- Invocation.getter(#walletNames),
- returnValue: _i4.Future<Map<String, _i3.WalletInfo>>.value(
- <String, _i3.WalletInfo>{},
- ),
- )
- as _i4.Future<Map<String, _i3.WalletInfo>>);
-
- @override
- bool get hasListeners =>
- (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false)
- as bool);
-
- @override
- void addListener(_i5.VoidCallback? listener) => super.noSuchMethod(
- Invocation.method(#addListener, [listener]),
- returnValueForMissingStub: null,
- );
-
- @override
- void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod(
- Invocation.method(#removeListener, [listener]),
- returnValueForMissingStub: null,
- );
-
- @override
- void dispose() => super.noSuchMethod(
- Invocation.method(#dispose, []),
- returnValueForMissingStub: null,
- );
-
- @override
- void notifyListeners() => super.noSuchMethod(
- Invocation.method(#notifyListeners, []),
- returnValueForMissingStub: null,
- );
-}
-
-/// A class which mocks [NodeService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockNodeService extends _i1.Mock implements _i6.NodeService {
- MockNodeService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i2.SecureStorageInterface get secureStorageInterface =>
- (super.noSuchMethod(
- Invocation.getter(#secureStorageInterface),
- returnValue: _FakeSecureStorageInterface_0(
- this,
- Invocation.getter(#secureStorageInterface),
- ),
- )
- as _i2.SecureStorageInterface);
-
- @override
- List<_i7.NodeModel> get primaryNodes =>
- (super.noSuchMethod(
- Invocation.getter(#primaryNodes),
- returnValue: <_i7.NodeModel>[],
- )
- as List<_i7.NodeModel>);
-
- @override
- List<_i7.NodeModel> get nodes =>
- (super.noSuchMethod(
- Invocation.getter(#nodes),
- returnValue: <_i7.NodeModel>[],
- )
- as List<_i7.NodeModel>);
-
- @override
- bool get hasListeners =>
- (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false)
- as bool);
-
- @override
- _i4.Future<void> updateDefaults() =>
- (super.noSuchMethod(
- Invocation.method(#updateDefaults, []),
- returnValue: _i4.Future<void>.value(),
- returnValueForMissingStub: _i4.Future<void>.value(),
- )
- as _i4.Future<void>);
-
- @override
- _i4.Future<void> setPrimaryNodeFor({
- required _i8.CryptoCurrency? coin,
- required _i7.NodeModel? node,
- bool? shouldNotifyListeners = false,
- }) =>
- (super.noSuchMethod(
- Invocation.method(#setPrimaryNodeFor, [], {
- #coin: coin,
- #node: node,
- #shouldNotifyListeners: shouldNotifyListeners,
- }),
- returnValue: _i4.Future<void>.value(),
- returnValueForMissingStub: _i4.Future<void>.value(),
- )
- as _i4.Future<void>);
-
- @override
- _i7.NodeModel? getPrimaryNodeFor({required _i8.CryptoCurrency? currency}) =>
- (super.noSuchMethod(
- Invocation.method(#getPrimaryNodeFor, [], {#currency: currency}),
- )
- as _i7.NodeModel?);
-
- @override
- List<_i7.NodeModel> getNodesFor(_i8.CryptoCurrency? coin) =>
- (super.noSuchMethod(
- Invocation.method(#getNodesFor, [coin]),
- returnValue: <_i7.NodeModel>[],
- )
- as List<_i7.NodeModel>);
-
- @override
- _i7.NodeModel? getNodeById({required String? id}) =>
- (super.noSuchMethod(Invocation.method(#getNodeById, [], {#id: id}))
- as _i7.NodeModel?);
-
- @override
- List<_i7.NodeModel> failoverNodesFor({
- required _i8.CryptoCurrency? currency,
- }) =>
- (super.noSuchMethod(
- Invocation.method(#failoverNodesFor, [], {#currency: currency}),
- returnValue: <_i7.NodeModel>[],
- )
- as List<_i7.NodeModel>);
-
- @override
- _i4.Future<void> save(
- _i7.NodeModel? node,
- String? password,
- bool? shouldNotifyListeners,
- ) =>
- (super.noSuchMethod(
- Invocation.method(#save, [node, password, shouldNotifyListeners]),
- returnValue: _i4.Future<void>.value(),
- returnValueForMissingStub: _i4.Future<void>.value(),
- )
- as _i4.Future<void>);
-
- @override
- _i4.Future<void> delete(String? id, bool? shouldNotifyListeners) =>
- (super.noSuchMethod(
- Invocation.method(#delete, [id, shouldNotifyListeners]),
- returnValue: _i4.Future<void>.value(),
- returnValueForMissingStub: _i4.Future<void>.value(),
- )
- as _i4.Future<void>);
-
- @override
- _i4.Future<void> setEnabledState(
- String? id,
- bool? enabled,
- bool? shouldNotifyListeners,
- ) =>
- (super.noSuchMethod(
- Invocation.method(#setEnabledState, [
- id,
- enabled,
- shouldNotifyListeners,
- ]),
- returnValue: _i4.Future<void>.value(),
- returnValueForMissingStub: _i4.Future<void>.value(),
- )
- as _i4.Future<void>);
-
- @override
- _i4.Future<void> updateCommunityNodes() =>
- (super.noSuchMethod(
- Invocation.method(#updateCommunityNodes, []),
- returnValue: _i4.Future<void>.value(),
- returnValueForMissingStub: _i4.Future<void>.value(),
- )
- as _i4.Future<void>);
-
- @override
- void addListener(_i5.VoidCallback? listener) => super.noSuchMethod(
- Invocation.method(#addListener, [listener]),
- returnValueForMissingStub: null,
- );
-
- @override
- void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod(
- Invocation.method(#removeListener, [listener]),
- returnValueForMissingStub: null,
- );
-
- @override
- void dispose() => super.noSuchMethod(
- Invocation.method(#dispose, []),
- returnValueForMissingStub: null,
- );
-
- @override
- void notifyListeners() => super.noSuchMethod(
- Invocation.method(#notifyListeners, []),
- returnValueForMissingStub: null,
- );
-}
diff --git a/test/screen_tests/onboarding/create_pin_view_screen_test.dart b/test/screen_tests/onboarding/create_pin_view_screen_test.dart
index fcd0aa2..455d347 100644
--- a/test/screen_tests/onboarding/create_pin_view_screen_test.dart
+++ b/test/screen_tests/onboarding/create_pin_view_screen_test.dart
@@ -1,382 +1,207 @@
-// import 'package:flutter/material.dart';
-// import 'package:flutter_test/flutter_test.dart';
-// import 'package:mockingjay/mockingjay.dart' as mockingjay;
-import 'package:mockito/annotations.dart';
-// import 'package:mockito/mockito.dart';
-// import 'package:stackwallet/pages/onboarding_view/create_pin_view.dart';
-// import 'package:stackwallet/pages/onboarding_view/helpers/create_wallet_type.dart';
-
-import 'package:stackwallet/services/node_service.dart';
-import 'package:stackwallet/services/wallets_service.dart';
-// import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
-// import 'package:stackwallet/utilities/misc_global_constants.dart';
-// import 'package:stackwallet/widgets/custom_buttons/gradient_button.dart';
-// import 'package:stackwallet/widgets/custom_pin_put/custom_pin_put.dart';
-// import 'package:stackwallet/widgets/custom_pin_put/pin_keyboard.dart';
-// import 'package:provider/provider.dart';
-//
-// import 'create_pin_view_screen_test.mocks.dart';
-
-@GenerateMocks([], customMocks: [
- MockSpec<WalletsService>(),
- MockSpec<NodeService>(),
-])
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/mockito.dart';
+import 'package:stackwallet/models/isar/stack_theme.dart';
+import 'package:stackwallet/pages/home_view/home_view.dart';
+import 'package:stackwallet/pages/pinpad_views/create_pin_view.dart';
+import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart';
+import 'package:stackwallet/providers/global/prefs_provider.dart';
+import 'package:stackwallet/themes/stack_colors.dart';
+import 'package:stackwallet/themes/theme_service.dart';
+import 'package:stackwallet/utilities/biometrics.dart';
+import 'package:stackwallet/utilities/prefs.dart';
+import 'package:stackwallet/widgets/custom_pin_put/pin_keyboard.dart';
+
+import '../../sample_data/theme_json.dart';
+import '../../widget_tests/custom_loading_overlay_test.mocks.dart';
+import '../../widget_tests/node_options_sheet_test.mocks.dart';
+import '../../widget_tests/support/platform_test_overrides.dart';
+
+class SpyBiometrics extends Biometrics {
+ SpyBiometrics({this.result = false});
+
+ final bool result;
+ int calls = 0;
+
+ @override
+ Future<bool> authenticate({
+ required String cancelButtonText,
+ required String localizedReason,
+ required String title,
+ }) async {
+ calls += 1;
+ return result;
+ }
+}
+
void main() {
-// testWidgets("CreatePinView builds correctly", (tester) async {
-// await tester.pumpWidget(
-// MaterialApp(
-// home: CreatePinView(
-// type: CreateWalletType.NEW,
-// walletName: "My Firo Wallet",
-// useTestNet: false,
-// ),
-// ),
-// );
-//
-// expect(find.byKey(Key("onboardingAppBarBackButton")), findsOneWidget);
-// expect(find.byKey(Key("onboardingAppBarBackButtonChevronSvg")),
-// findsOneWidget);
-//
-// final imageFinder = find.byType(Image);
-// expect(imageFinder, findsOneWidget);
-//
-// final imageSource =
-// ((imageFinder.evaluate().single.widget as Image).image as AssetImage)
-// .assetName;
-// expect(imageSource, "assets/images/logo.png");
-//
-// expect(find.text("Create a PIN"), findsOneWidget);
-//
-// expect(find.byType(CustomPinPut), findsOneWidget);
-// });
-//
-// testWidgets("back button test", (tester) async {
-// final navigator = mockingjay.MockNavigator();
-//
-// mockingjay.when(() => navigator.pop()).thenAnswer((_) async => {});
-//
-// await tester.pumpWidget(
-// MaterialApp(
-// home: mockingjay.MockNavigatorProvider(
-// navigator: navigator,
-// child: CreatePinView(
-// type: CreateWalletType.NEW,
-// walletName: "My Firo Wallet",
-// useTestNet: false,
-// ),
-// ),
-// ),
-// );
-//
-// await tester.tap(find.byKey(Key("onboardingAppBarBackButton")));
-// await tester.pumpAndSettle();
-//
-// mockingjay.verify(() => navigator.pop()).called(1);
-// });
-//
-// testWidgets("Entering unmatched PINs", (tester) async {
-// await tester.pumpWidget(
-// MaterialApp(
-// home: CreatePinView(
-// type: CreateWalletType.NEW,
-// walletName: "My Firo Wallet",
-// useTestNet: false,
-// ),
-// ),
-// );
-//
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "1"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "2"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "3"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester
-// .tap(find.byWidgetPredicate((widget) => widget is BackspaceKey));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "6"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "4"));
-// await tester.pumpAndSettle(Duration(seconds: 1));
-//
-// expect(find.text("Confirm PIN"), findsOneWidget);
-//
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "7"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "9"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "8"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "5"));
-// await tester.pumpAndSettle(Duration(seconds: 2));
-//
-// expect(find.text("Create a PIN"), findsOneWidget);
-// });
-//
-// testWidgets("Entering matched PINs on a new wallet", (tester) async {
-// final navigator = mockingjay.MockNavigator();
-// final walletsService = MockWalletsService();
-// final wallet = MockManager();
-// final nodeService = MockNodeService();
-//
-// final store = FakeSecureStorage();
-//
-// mockingjay
-// .when(() => navigator.push(mockingjay.any()))
-// .thenAnswer((_) async => {});
-//
-// when(walletsService.addNewWalletName("My Firo Wallet", "main"))
-// .thenAnswer((_) async => true);
-// when(walletsService.getWalletId("My Firo Wallet"))
-// .thenAnswer((_) async => "walletID");
-//
-// when(nodeService.reInit()).thenAnswer((_) => {});
-// when(
-// nodeService.createNode(
-// name: CampfireConstants.defaultNodeName,
-// ipAddress: CampfireConstants.defaultIpAddress,
-// port: CampfireConstants.defaultPort.toString(),
-// useSSL: CampfireConstants.defaultUseSSL,
-// ),
-// ).thenAnswer((_) async => true);
-//
-// when(manager.initializeWallet()).thenAnswer((_) async => true);
-//
-// await tester.pumpWidget(
-// MaterialApp(
-// home: mockingjay.MockNavigatorProvider(
-// navigator: navigator,
-// child: MultiProvider(
-// // home: MultiProvider(
-// providers: [
-// ChangeNotifierProvider<WalletsService>(
-// create: (_) => walletsService,
-// ),
-// ChangeNotifierProvider<Manager>(
-// create: (_) => manager,
-// ),
-// ChangeNotifierProvider<NodeService>(
-// create: (_) => nodeService,
-// ),
-// ],
-// child: CreatePinView(
-// type: CreateWalletType.NEW,
-// walletName: "My Firo Wallet",
-// useTestNet: false,
-// secureStore: store,
-// ),
-// ),
-// ),
-// ),
-// );
-//
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "1"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "2"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "3"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "4"));
-// await tester.pumpAndSettle(Duration(seconds: 1));
-//
-// expect(find.text("Confirm PIN"), findsOneWidget);
-//
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "1"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "2"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "3"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "4"));
-// await tester.pump(Duration(seconds: 2));
-//
-// expect(find.byType(CircularProgressIndicator), findsOneWidget);
-//
-// await tester.pump(Duration(seconds: 20));
-//
-// mockingjay.verify(() => navigator.push(mockingjay.any())).called(1);
-// });
-//
-// testWidgets("Wallet init fails on entering matched PINs", (tester) async {
-// final navigator = mockingjay.MockNavigator();
-// final walletsService = MockWalletsService();
-// final wallet = MockManager();
-// final nodeService = MockNodeService();
-//
-// final store = FakeSecureStorage();
-//
-// mockingjay.when(() => navigator.pop()).thenAnswer((_) async => {});
-//
-// when(walletsService.addNewWalletName("My Firo Wallet", "main"))
-// .thenAnswer((_) async => true);
-// when(walletsService.getWalletId("My Firo Wallet"))
-// .thenAnswer((_) async => "walletID");
-//
-// when(nodeService.reInit()).thenAnswer((_) => {});
-// when(
-// nodeService.createNode(
-// name: CampfireConstants.defaultNodeNameTestNet,
-// ipAddress: CampfireConstants.defaultIpAddressTestNet,
-// port: CampfireConstants.defaultPortTestNet.toString(),
-// useSSL: CampfireConstants.defaultUseSSLTestNet,
-// ),
-// ).thenAnswer((_) async => true);
-//
-// when(manager.initializeWallet()).thenAnswer((_) async => false);
-// when(manager.exitCurrentWallet()).thenAnswer((_) async => {});
-// when(walletsService.deleteWallet("My Firo Wallet"))
-// .thenAnswer((_) async => 0);
-//
-// await tester.pumpWidget(
-// MaterialApp(
-// home: mockingjay.MockNavigatorProvider(
-// navigator: navigator,
-// child: MultiProvider(
-// // home: MultiProvider(
-// providers: [
-// ChangeNotifierProvider<WalletsService>(
-// create: (_) => walletsService,
-// ),
-// ChangeNotifierProvider<Manager>(
-// create: (_) => manager,
-// ),
-// ChangeNotifierProvider<NodeService>(
-// create: (_) => nodeService,
-// ),
-// ],
-// child: CreatePinView(
-// type: CreateWalletType.NEW,
-// walletName: "My Firo Wallet",
-// useTestNet: true,
-// secureStore: store,
-// ),
-// ),
-// ),
-// ),
-// );
-//
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "1"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "2"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "3"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "4"));
-// await tester.pumpAndSettle(Duration(seconds: 1));
-//
-// expect(find.text("Confirm PIN"), findsOneWidget);
-//
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "1"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "2"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "3"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "4"));
-// await tester.pump(Duration(seconds: 2));
-//
-// expect(
-// find.text(
-// "Failed to connect to network. Check your internet connection."),
-// findsOneWidget);
-// expect(find.text("OK"), findsOneWidget);
-//
-// await tester.tap(find.byType(GradientButton));
-// await tester.pump(Duration(seconds: 1));
-//
-// mockingjay.verify(() => navigator.pop()).called(4);
-// });
-//
-// testWidgets("Entering matched PINs on a restore", (tester) async {
-// final navigator = mockingjay.MockNavigator();
-// final walletsService = MockWalletsService();
-//
-// final store = FakeSecureStorage();
-//
-// mockingjay
-// .when(() => navigator.push(mockingjay.any()))
-// .thenAnswer((_) async => {});
-//
-// when(walletsService.addNewWalletName("My Firo Wallet", "main"))
-// .thenAnswer((_) async => true);
-// when(walletsService.getWalletId("My Firo Wallet"))
-// .thenAnswer((_) async => "walletID");
-//
-// await tester.pumpWidget(
-// MaterialApp(
-// home: mockingjay.MockNavigatorProvider(
-// navigator: navigator,
-// child: MultiProvider(
-// // home: MultiProvider(
-// providers: [
-// ChangeNotifierProvider<WalletsService>(
-// create: (_) => walletsService,
-// ),
-// ],
-// child: CreatePinView(
-// type: CreateWalletType.RESTORE,
-// walletName: "My Firo Wallet",
-// useTestNet: false,
-// secureStore: store,
-// ),
-// ),
-// ),
-// ),
-// );
-//
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "1"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "2"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "3"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "4"));
-// await tester.pumpAndSettle(Duration(seconds: 1));
-//
-// expect(find.text("Confirm PIN"), findsOneWidget);
-//
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "1"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "2"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "3"));
-// await tester.pumpAndSettle(Duration(milliseconds: 100));
-// await tester.tap(find.byWidgetPredicate(
-// (widget) => widget is NumberKey && widget.number == "4"));
-// await tester.pumpAndSettle(Duration(seconds: 6));
-//
-// mockingjay.verify(() => navigator.push(mockingjay.any())).called(1);
-// });
+ ThemeData buildTheme() {
+ return ThemeData(
+ extensions: [
+ StackColors.fromStackColorTheme(
+ StackTheme.fromJson(json: lightThemeJsonMap),
+ ),
+ ],
+ );
+ }
+
+ void stubPrefs(MockPrefs prefs) {
+ when(prefs.randomizePIN).thenReturn(false);
+ when(prefs.hasPin).thenReturn(false);
+ }
+
+ Future<void> pumpCreatePinView(
+ WidgetTester tester, {
+ required MockPrefs prefs,
+ required SpyBiometrics biometrics,
+ required List<Override> overrides,
+ }) async {
+ final mockThemeService = MockThemeService();
+ final theme = StackTheme.fromJson(json: lightThemeJsonMap);
+
+ when(mockThemeService.getTheme(themeId: 'light')).thenReturn(theme);
+
+ await tester.pumpWidget(
+ ProviderScope(
+ overrides: [
+ pThemeService.overrideWithValue(mockThemeService),
+ prefsChangeNotifierProvider.overrideWithValue(prefs),
+ ...overrides,
+ ],
+ child: MaterialApp(
+ theme: buildTheme(),
+ routes: {
+ HomeView.routeName: (_) => const Scaffold(body: Text('home route')),
+ },
+ home: CreatePinView(biometrics: biometrics),
+ ),
+ ),
+ );
+
+ await tester.pumpAndSettle();
+ }
+
+ Future<void> tapDigit(WidgetTester tester, String digit) async {
+ await tester.tap(
+ find.byWidgetPredicate(
+ (widget) => widget is NumberKey && widget.number == digit,
+ ),
+ );
+ await tester.pump(const Duration(milliseconds: 250));
+ }
+
+ Future<void> enterPin(WidgetTester tester, String pin) async {
+ for (final digit in pin.split('')) {
+ await tapDigit(tester, digit);
+ }
+ }
+
+ Future<void> submitCurrentPin(WidgetTester tester) async {
+ await tester.tap(find.byType(SubmitKey));
+ await tester.pump();
+ }
+
+ testWidgets('matching PIN persists through fake secure storage seam', (
+ tester,
+ ) async {
+ final prefs = MockPrefs();
+ final biometrics = SpyBiometrics();
+ final platformOverrides = await createPlatformTestOverrides();
+
+ stubPrefs(prefs);
+
+ await pumpCreatePinView(
+ tester,
+ prefs: prefs,
+ biometrics: biometrics,
+ overrides: platformOverrides.overrides,
+ );
+
+ expect(find.text('Create a PIN'), findsOneWidget);
+
+ await enterPin(tester, '1234');
+ await submitCurrentPin(tester);
+ await tester.pumpAndSettle();
+
+ expect(find.text('Confirm PIN'), findsOneWidget);
+
+ await enterPin(tester, '1234');
+ await submitCurrentPin(tester);
+ await tester.pumpAndSettle(const Duration(milliseconds: 300));
+
+ expect(await platformOverrides.secureStorage.read(key: kPinKey), '1234');
+ expect(platformOverrides.secureStorage.writes, 1);
+ expect(biometrics.calls, 0);
+
+ verify(prefs.useBiometrics = false).called(1);
+ verify(prefs.hasPin = true).called(1);
+ expect(find.text('home route'), findsOneWidget);
+ });
+
+ testWidgets('short PIN submission is blocked before confirmation page', (
+ tester,
+ ) async {
+ final prefs = MockPrefs();
+ final biometrics = SpyBiometrics();
+ final platformOverrides = await createPlatformTestOverrides();
+
+ stubPrefs(prefs);
+
+ await pumpCreatePinView(
+ tester,
+ prefs: prefs,
+ biometrics: biometrics,
+ overrides: platformOverrides.overrides,
+ );
+
+ await enterPin(tester, '123');
+ await submitCurrentPin(tester);
+ await tester.pumpAndSettle();
+
+ expect(find.text('Create a PIN'), findsOneWidget);
+ expect(find.text('Confirm PIN'), findsNothing);
+ expect(await platformOverrides.secureStorage.read(key: kPinKey), isNull);
+ expect(platformOverrides.secureStorage.writes, 0);
+ expect(biometrics.calls, 0);
+
+ verifyNever(prefs.useBiometrics = false);
+ verifyNever(prefs.hasPin = true);
+ });
+
+ testWidgets('mismatched confirmation resets flow without storage writes', (
+ tester,
+ ) async {
+ final prefs = MockPrefs();
+ final biometrics = SpyBiometrics();
+ final platformOverrides = await createPlatformTestOverrides();
+
+ stubPrefs(prefs);
+
+ await pumpCreatePinView(
+ tester,
+ prefs: prefs,
+ biometrics: biometrics,
+ overrides: platformOverrides.overrides,
+ );
+
+ await enterPin(tester, '1234');
+ await submitCurrentPin(tester);
+ await tester.pumpAndSettle();
+
+ expect(find.text('Confirm PIN'), findsOneWidget);
+
+ await enterPin(tester, '9876');
+ await submitCurrentPin(tester);
+ await tester.pumpAndSettle();
+
+ expect(find.text('Create a PIN'), findsOneWidget);
+ expect(find.text('Confirm PIN'), findsNothing);
+ expect(await platformOverrides.secureStorage.read(key: kPinKey), isNull);
+ expect(platformOverrides.secureStorage.writes, 0);
+ expect(biometrics.calls, 0);
+
+ verifyNever(prefs.useBiometrics = false);
+ verifyNever(prefs.hasPin = true);
+ });
}
diff --git a/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart b/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart
deleted file mode 100644
index de530da..0000000
--- a/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart
+++ /dev/null
@@ -1,254 +0,0 @@
-// Mocks generated by Mockito 5.4.6 from annotations
-// in stackwallet/test/screen_tests/onboarding/create_pin_view_screen_test.dart.
-// Do not manually edit this file.
-
-// ignore_for_file: no_leading_underscores_for_library_prefixes
-import 'dart:async' as _i4;
-import 'dart:ui' as _i5;
-
-import 'package:mockito/mockito.dart' as _i1;
-import 'package:stackwallet/models/node_model.dart' as _i7;
-import 'package:stackwallet/services/node_service.dart' as _i6;
-import 'package:stackwallet/services/wallets_service.dart' as _i3;
-import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
- as _i2;
-import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'
- as _i8;
-
-// ignore_for_file: type=lint
-// ignore_for_file: avoid_redundant_argument_values
-// ignore_for_file: avoid_setters_without_getters
-// ignore_for_file: comment_references
-// ignore_for_file: deprecated_member_use
-// ignore_for_file: deprecated_member_use_from_same_package
-// ignore_for_file: implementation_imports
-// ignore_for_file: invalid_use_of_visible_for_testing_member
-// ignore_for_file: must_be_immutable
-// ignore_for_file: prefer_const_constructors
-// ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: camel_case_types
-// ignore_for_file: subtype_of_sealed_class
-// ignore_for_file: invalid_use_of_internal_member
-
-class _FakeSecureStorageInterface_0 extends _i1.SmartFake
- implements _i2.SecureStorageInterface {
- _FakeSecureStorageInterface_0(Object parent, Invocation parentInvocation)
- : super(parent, parentInvocation);
-}
-
-/// A class which mocks [WalletsService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockWalletsService extends _i1.Mock implements _i3.WalletsService {
- MockWalletsService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i4.Future<Map<String, _i3.WalletInfo>> get walletNames =>
- (super.noSuchMethod(
- Invocation.getter(#walletNames),
- returnValue: _i4.Future<Map<String, _i3.WalletInfo>>.value(
- <String, _i3.WalletInfo>{},
- ),
- )
- as _i4.Future<Map<String, _i3.WalletInfo>>);
-
- @override
- bool get hasListeners =>
- (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false)
- as bool);
-
- @override
- void addListener(_i5.VoidCallback? listener) => super.noSuchMethod(
- Invocation.method(#addListener, [listener]),
- returnValueForMissingStub: null,
- );
-
- @override
- void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod(
- Invocation.method(#removeListener, [listener]),
- returnValueForMissingStub: null,
- );
-
- @override
- void dispose() => super.noSuchMethod(
- Invocation.method(#dispose, []),
- returnValueForMissingStub: null,
- );
-
- @override
- void notifyListeners() => super.noSuchMethod(
- Invocation.method(#notifyListeners, []),
- returnValueForMissingStub: null,
- );
-}
-
-/// A class which mocks [NodeService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockNodeService extends _i1.Mock implements _i6.NodeService {
- MockNodeService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i2.SecureStorageInterface get secureStorageInterface =>
- (super.noSuchMethod(
- Invocation.getter(#secureStorageInterface),
- returnValue: _FakeSecureStorageInterface_0(
- this,
- Invocation.getter(#secureStorageInterface),
- ),
- )
- as _i2.SecureStorageInterface);
-
- @override
- List<_i7.NodeModel> get primaryNodes =>
- (super.noSuchMethod(
- Invocation.getter(#primaryNodes),
- returnValue: <_i7.NodeModel>[],
- )
- as List<_i7.NodeModel>);
-
- @override
- List<_i7.NodeModel> get nodes =>
- (super.noSuchMethod(
- Invocation.getter(#nodes),
- returnValue: <_i7.NodeModel>[],
- )
- as List<_i7.NodeModel>);
-
- @override
- bool get hasListeners =>
- (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false)
- as bool);
-
- @override
- _i4.Future<void> updateDefaults() =>
- (super.noSuchMethod(
- Invocation.method(#updateDefaults, []),
- returnValue: _i4.Future<void>.value(),
- returnValueForMissingStub: _i4.Future<void>.value(),
- )
- as _i4.Future<void>);
-
- @override
- _i4.Future<void> setPrimaryNodeFor({
- required _i8.CryptoCurrency? coin,
- required _i7.NodeModel? node,
- bool? shouldNotifyListeners = false,
- }) =>
- (super.noSuchMethod(
- Invocation.method(#setPrimaryNodeFor, [], {
- #coin: coin,
- #node: node,
- #shouldNotifyListeners: shouldNotifyListeners,
- }),
- returnValue: _i4.Future<void>.value(),
- returnValueForMissingStub: _i4.Future<void>.value(),
- )
- as _i4.Future<void>);
-
- @override
- _i7.NodeModel? getPrimaryNodeFor({required _i8.CryptoCurrency? currency}) =>
- (super.noSuchMethod(
- Invocation.method(#getPrimaryNodeFor, [], {#currency: currency}),
- )
- as _i7.NodeModel?);
-
- @override
- List<_i7.NodeModel> getNodesFor(_i8.CryptoCurrency? coin) =>
- (super.noSuchMethod(
- Invocation.method(#getNodesFor, [coin]),
- returnValue: <_i7.NodeModel>[],
- )
- as List<_i7.NodeModel>);
-
- @override
- _i7.NodeModel? getNodeById({required String? id}) =>
- (super.noSuchMethod(Invocation.method(#getNodeById, [], {#id: id}))
- as _i7.NodeModel?);
-
- @override
- List<_i7.NodeModel> failoverNodesFor({
- required _i8.CryptoCurrency? currency,
- }) =>
- (super.noSuchMethod(
- Invocation.method(#failoverNodesFor, [], {#currency: currency}),
- returnValue: <_i7.NodeModel>[],
- )
- as List<_i7.NodeModel>);
-
- @override
- _i4.Future<void> save(
- _i7.NodeModel? node,
- String? password,
- bool? shouldNotifyListeners,
- ) =>
- (super.noSuchMethod(
- Invocation.method(#save, [node, password, shouldNotifyListeners]),
- returnValue: _i4.Future<void>.value(),
- returnValueForMissingStub: _i4.Future<void>.value(),
- )
- as _i4.Future<void>);
-
- @override
- _i4.Future<void> delete(String? id, bool? shouldNotifyListeners) =>
- (super.noSuchMethod(
- Invocation.method(#delete, [id, shouldNotifyListeners]),
- returnValue: _i4.Future<void>.value(),
- returnValueForMissingStub: _i4.Future<void>.value(),
- )
- as _i4.Future<void>);
-
- @override
- _i4.Future<void> setEnabledState(
- String? id,
- bool? enabled,
- bool? shouldNotifyListeners,
- ) =>
- (super.noSuchMethod(
- Invocation.method(#setEnabledState, [
- id,
- enabled,
- shouldNotifyListeners,
- ]),
- returnValue: _i4.Future<void>.value(),
- returnValueForMissingStub: _i4.Future<void>.value(),
- )
- as _i4.Future<void>);
-
- @override
- _i4.Future<void> updateCommunityNodes() =>
- (super.noSuchMethod(
- Invocation.method(#updateCommunityNodes, []),
- returnValue: _i4.Future<void>.value(),
- returnValueForMissingStub: _i4.Future<void>.value(),
- )
- as _i4.Future<void>);
-
- @override
- void addListener(_i5.VoidCallback? listener) => super.noSuchMethod(
- Invocation.method(#addListener, [listener]),
- returnValueForMissingStub: null,
- );
-
- @override
- void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod(
- Invocation.method(#removeListener, [listener]),
- returnValueForMissingStub: null,
- );
-
- @override
- void dispose() => super.noSuchMethod(
- Invocation.method(#dispose, []),
- returnValueForMissingStub: null,
- );
-
- @override
- void notifyListeners() => super.noSuchMethod(
- Invocation.method(#notifyListeners, []),
- returnValueForMissingStub: null,
- );
-}
diff --git a/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart b/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart
index 1b9698d..4f133dc 100644
--- a/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart
+++ b/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart
@@ -8,6 +8,7 @@ import 'dart:ui' as _i7;
import 'package:flutter/material.dart' as _i5;
import 'package:mockito/mockito.dart' as _i1;
+import 'package:stackwallet/models/epicbox_server_model.dart' as _i11;
import 'package:stackwallet/models/node_model.dart' as _i9;
import 'package:stackwallet/services/node_service.dart' as _i8;
import 'package:stackwallet/services/wallets_service.dart' as _i6;
@@ -249,6 +250,64 @@ class MockNodeService extends _i1.Mock implements _i8.NodeService {
)
as _i4.Future<void>);
+ @override
+ _i4.Future<void> updateDefaultEpicBoxes() =>
+ (super.noSuchMethod(
+ Invocation.method(#updateDefaultEpicBoxes, []),
+ returnValue: _i4.Future<void>.value(),
+ returnValueForMissingStub: _i4.Future<void>.value(),
+ )
+ as _i4.Future<void>);
+
+ @override
+ _i4.Future<void> setPrimaryEpicBox({
+ required _i11.EpicBoxServerModel? epicBox,
+ bool? shouldNotifyListeners = false,
+ }) =>
+ (super.noSuchMethod(
+ Invocation.method(#setPrimaryEpicBox, [], {
+ #epicBox: epicBox,
+ #shouldNotifyListeners: shouldNotifyListeners,
+ }),
+ returnValue: _i4.Future<void>.value(),
+ returnValueForMissingStub: _i4.Future<void>.value(),
+ )
+ as _i4.Future<void>);
+
+ @override
+ List<_i11.EpicBoxServerModel> getEpicBoxes() =>
+ (super.noSuchMethod(
+ Invocation.method(#getEpicBoxes, []),
+ returnValue: <_i11.EpicBoxServerModel>[],
+ )
+ as List<_i11.EpicBoxServerModel>);
+
+ @override
+ _i11.EpicBoxServerModel? getEpicBoxById({required String? id}) =>
+ (super.noSuchMethod(Invocation.method(#getEpicBoxById, [], {#id: id}))
+ as _i11.EpicBoxServerModel?);
+
+ @override
+ _i4.Future<void> addEpicBox(
+ _i11.EpicBoxServerModel? epicBox,
+ bool? shouldNotifyListeners,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(#addEpicBox, [epicBox, shouldNotifyListeners]),
+ returnValue: _i4.Future<void>.value(),
+ returnValueForMissingStub: _i4.Future<void>.value(),
+ )
+ as _i4.Future<void>);
+
+ @override
+ _i4.Future<void> deleteEpicBox(String? id, bool? shouldNotifyListeners) =>
+ (super.noSuchMethod(
+ Invocation.method(#deleteEpicBox, [id, shouldNotifyListeners]),
+ returnValue: _i4.Future<void>.value(),
+ returnValueForMissingStub: _i4.Future<void>.value(),
+ )
+ as _i4.Future<void>);
+
@override
_i4.Future<void> updateCommunityNodes() =>
(super.noSuchMethod(
diff --git a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart
index 7448120..2e86d9e 100644
--- a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart
+++ b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart
@@ -4,9 +4,10 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i5;
-import 'dart:ui' as _i7;
+import 'dart:ui' as _i8;
import 'package:mockito/mockito.dart' as _i1;
+import 'package:stackwallet/models/epicbox_server_model.dart' as _i7;
import 'package:stackwallet/models/node_model.dart' as _i4;
import 'package:stackwallet/services/node_service.dart' as _i3;
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
@@ -170,6 +171,64 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
)
as _i5.Future<void>);
+ @override
+ _i5.Future<void> updateDefaultEpicBoxes() =>
+ (super.noSuchMethod(
+ Invocation.method(#updateDefaultEpicBoxes, []),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ )
+ as _i5.Future<void>);
+
+ @override
+ _i5.Future<void> setPrimaryEpicBox({
+ required _i7.EpicBoxServerModel? epicBox,
+ bool? shouldNotifyListeners = false,
+ }) =>
+ (super.noSuchMethod(
+ Invocation.method(#setPrimaryEpicBox, [], {
+ #epicBox: epicBox,
+ #shouldNotifyListeners: shouldNotifyListeners,
+ }),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ )
+ as _i5.Future<void>);
+
+ @override
+ List<_i7.EpicBoxServerModel> getEpicBoxes() =>
+ (super.noSuchMethod(
+ Invocation.method(#getEpicBoxes, []),
+ returnValue: <_i7.EpicBoxServerModel>[],
+ )
+ as List<_i7.EpicBoxServerModel>);
+
+ @override
+ _i7.EpicBoxServerModel? getEpicBoxById({required String? id}) =>
+ (super.noSuchMethod(Invocation.method(#getEpicBoxById, [], {#id: id}))
+ as _i7.EpicBoxServerModel?);
+
+ @override
+ _i5.Future<void> addEpicBox(
+ _i7.EpicBoxServerModel? epicBox,
+ bool? shouldNotifyListeners,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(#addEpicBox, [epicBox, shouldNotifyListeners]),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ )
+ as _i5.Future<void>);
+
+ @override
+ _i5.Future<void> deleteEpicBox(String? id, bool? shouldNotifyListeners) =>
+ (super.noSuchMethod(
+ Invocation.method(#deleteEpicBox, [id, shouldNotifyListeners]),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ )
+ as _i5.Future<void>);
+
@override
_i5.Future<void> updateCommunityNodes() =>
(super.noSuchMethod(
@@ -180,13 +239,13 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
as _i5.Future<void>);
@override
- void addListener(_i7.VoidCallback? listener) => super.noSuchMethod(
+ void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null,
);
@override
- void removeListener(_i7.VoidCallback? listener) => super.noSuchMethod(
+ void removeListener(_i8.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(#removeListener, [listener]),
returnValueForMissingStub: null,
);
diff --git a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart
index da7af34..eeb6f02 100644
--- a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart
+++ b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart
@@ -4,9 +4,10 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i5;
-import 'dart:ui' as _i7;
+import 'dart:ui' as _i8;
import 'package:mockito/mockito.dart' as _i1;
+import 'package:stackwallet/models/epicbox_server_model.dart' as _i7;
import 'package:stackwallet/models/node_model.dart' as _i4;
import 'package:stackwallet/services/node_service.dart' as _i3;
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
@@ -170,6 +171,64 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
)
as _i5.Future<void>);
+ @override
+ _i5.Future<void> updateDefaultEpicBoxes() =>
+ (super.noSuchMethod(
+ Invocation.method(#updateDefaultEpicBoxes, []),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ )
+ as _i5.Future<void>);
+
+ @override
+ _i5.Future<void> setPrimaryEpicBox({
+ required _i7.EpicBoxServerModel? epicBox,
+ bool? shouldNotifyListeners = false,
+ }) =>
+ (super.noSuchMethod(
+ Invocation.method(#setPrimaryEpicBox, [], {
+ #epicBox: epicBox,
+ #shouldNotifyListeners: shouldNotifyListeners,
+ }),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ )
+ as _i5.Future<void>);
+
+ @override
+ List<_i7.EpicBoxServerModel> getEpicBoxes() =>
+ (super.noSuchMethod(
+ Invocation.method(#getEpicBoxes, []),
+ returnValue: <_i7.EpicBoxServerModel>[],
+ )
+ as List<_i7.EpicBoxServerModel>);
+
+ @override
+ _i7.EpicBoxServerModel? getEpicBoxById({required String? id}) =>
+ (super.noSuchMethod(Invocation.method(#getEpicBoxById, [], {#id: id}))
+ as _i7.EpicBoxServerModel?);
+
+ @override
+ _i5.Future<void> addEpicBox(
+ _i7.EpicBoxServerModel? epicBox,
+ bool? shouldNotifyListeners,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(#addEpicBox, [epicBox, shouldNotifyListeners]),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ )
+ as _i5.Future<void>);
+
+ @override
+ _i5.Future<void> deleteEpicBox(String? id, bool? shouldNotifyListeners) =>
+ (super.noSuchMethod(
+ Invocation.method(#deleteEpicBox, [id, shouldNotifyListeners]),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ )
+ as _i5.Future<void>);
+
@override
_i5.Future<void> updateCommunityNodes() =>
(super.noSuchMethod(
@@ -180,13 +239,13 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
as _i5.Future<void>);
@override
- void addListener(_i7.VoidCallback? listener) => super.noSuchMethod(
+ void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null,
);
@override
- void removeListener(_i7.VoidCallback? listener) => super.noSuchMethod(
+ void removeListener(_i8.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(#removeListener, [listener]),
returnValueForMissingStub: null,
);
diff --git a/test/screen_tests/settings_view/settings_subviews/network_settings_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/network_settings_view_screen_test.mocks.dart
index 241eb0d..0d75015 100644
--- a/test/screen_tests/settings_view/settings_subviews/network_settings_view_screen_test.mocks.dart
+++ b/test/screen_tests/settings_view/settings_subviews/network_settings_view_screen_test.mocks.dart
@@ -4,9 +4,10 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i5;
-import 'dart:ui' as _i7;
+import 'dart:ui' as _i8;
import 'package:mockito/mockito.dart' as _i1;
+import 'package:stackwallet/models/epicbox_server_model.dart' as _i7;
import 'package:stackwallet/models/node_model.dart' as _i4;
import 'package:stackwallet/services/node_service.dart' as _i3;
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
@@ -170,6 +171,64 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
)
as _i5.Future<void>);
+ @override
+ _i5.Future<void> updateDefaultEpicBoxes() =>
+ (super.noSuchMethod(
+ Invocation.method(#updateDefaultEpicBoxes, []),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ )
+ as _i5.Future<void>);
+
+ @override
+ _i5.Future<void> setPrimaryEpicBox({
+ required _i7.EpicBoxServerModel? epicBox,
+ bool? shouldNotifyListeners = false,
+ }) =>
+ (super.noSuchMethod(
+ Invocation.method(#setPrimaryEpicBox, [], {
+ #epicBox: epicBox,
+ #shouldNotifyListeners: shouldNotifyListeners,
+ }),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ )
+ as _i5.Future<void>);
+
+ @override
+ List<_i7.EpicBoxServerModel> getEpicBoxes() =>
+ (super.noSuchMethod(
+ Invocation.method(#getEpicBoxes, []),
+ returnValue: <_i7.EpicBoxServerModel>[],
+ )
+ as List<_i7.EpicBoxServerModel>);
+
+ @override
+ _i7.EpicBoxServerModel? getEpicBoxById({required String? id}) =>
+ (super.noSuchMethod(Invocation.method(#getEpicBoxById, [], {#id: id}))
+ as _i7.EpicBoxServerModel?);
+
+ @override
+ _i5.Future<void> addEpicBox(
+ _i7.EpicBoxServerModel? epicBox,
+ bool? shouldNotifyListeners,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(#addEpicBox, [epicBox, shouldNotifyListeners]),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ )
+ as _i5.Future<void>);
+
+ @override
+ _i5.Future<void> deleteEpicBox(String? id, bool? shouldNotifyListeners) =>
+ (super.noSuchMethod(
+ Invocation.method(#deleteEpicBox, [id, shouldNotifyListeners]),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ )
+ as _i5.Future<void>);
+
@override
_i5.Future<void> updateCommunityNodes() =>
(super.noSuchMethod(
@@ -180,13 +239,13 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
as _i5.Future<void>);
@override
- void addListener(_i7.VoidCallback? listener) => super.noSuchMethod(
+ void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null,
);
@override
- void removeListener(_i7.VoidCallback? listener) => super.noSuchMethod(
+ void removeListener(_i8.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(#removeListener, [listener]),
returnValueForMissingStub: null,
);
diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart
index 6105ab2..453540f 100644
--- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart
+++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart
@@ -100,6 +100,22 @@ class MockCachedElectrumXClient extends _i1.Mock
)
as _i5.Future<Map<String, dynamic>>);
+ @override
+ _i5.Future<List<Map<String, dynamic>>> getBatchTransactions({
+ required List<String>? txHashes,
+ required _i6.CryptoCurrency? cryptoCurrency,
+ }) =>
+ (super.noSuchMethod(
+ Invocation.method(#getBatchTransactions, [], {
+ #txHashes: txHashes,
+ #cryptoCurrency: cryptoCurrency,
+ }),
+ returnValue: _i5.Future<List<Map<String, dynamic>>>.value(
+ <Map<String, dynamic>>[],
+ ),
+ )
+ as _i5.Future<List<Map<String, dynamic>>>);
+
@override
_i5.Future<void> clearSharedTransactionCache({
required _i6.CryptoCurrency? cryptoCurrency,
diff --git a/test/services/change_now/change_now_sample_data.dart b/test/services/change_now/change_now_sample_data.dart
index 4e63dbf..b396edb 100644
--- a/test/services/change_now/change_now_sample_data.dart
+++ b/test/services/change_now/change_now_sample_data.dart
@@ -7,7 +7,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": true,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "eth",
@@ -17,7 +17,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": true,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "ethbsc",
@@ -27,7 +27,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "usdt",
@@ -37,7 +37,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": true,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "usdterc20",
@@ -48,7 +48,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": true,
"isStable": true,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "usdttrc20",
@@ -59,7 +59,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": true,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "usdtbsc",
@@ -69,7 +69,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "usdc",
@@ -79,7 +79,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": true,
"isStable": true,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "usdcmatic",
@@ -90,7 +90,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": true,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "bnbmainnet",
@@ -101,7 +101,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "bnbbsc",
@@ -111,7 +111,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "busd",
@@ -121,7 +121,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": true,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "busdbsc",
@@ -131,7 +131,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "xrp",
@@ -141,7 +141,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": true,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "xrpbsc",
@@ -151,7 +151,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "ada",
@@ -161,7 +161,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": true,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "adabsc",
@@ -171,7 +171,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "sol",
@@ -181,7 +181,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "doge",
@@ -191,7 +191,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "dot",
@@ -201,7 +201,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": true,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "dotbsc",
@@ -211,7 +211,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "dai",
@@ -221,7 +221,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": true,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "matic",
@@ -231,7 +231,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "maticmainnet",
@@ -242,7 +242,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "shib",
@@ -252,7 +252,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "shibbsc",
@@ -262,7 +262,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "trx",
@@ -272,7 +272,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": true,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "avax",
@@ -282,7 +282,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "avaxc",
@@ -292,7 +292,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "wbtc",
@@ -302,7 +302,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "leo",
@@ -312,7 +312,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "uni",
@@ -322,7 +322,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "etc",
@@ -332,7 +332,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "ltc",
@@ -342,7 +342,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": true,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "ltcbsc",
@@ -352,7 +352,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "ftt",
@@ -362,7 +362,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "atom",
@@ -372,7 +372,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "link",
@@ -382,7 +382,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "cro",
@@ -392,7 +392,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "near",
@@ -402,7 +402,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "xmr",
@@ -412,7 +412,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": true,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "xlm",
@@ -422,7 +422,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "bch",
@@ -432,7 +432,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "algo",
@@ -442,7 +442,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "flow",
@@ -452,7 +452,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "vet",
@@ -462,7 +462,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": true,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "icp",
@@ -472,7 +472,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "fil",
@@ -482,7 +482,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "ape",
@@ -492,7 +492,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "eos",
@@ -502,7 +502,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "mana",
@@ -512,7 +512,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "sand",
@@ -522,7 +522,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "hbar",
@@ -532,7 +532,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "xtz",
@@ -542,7 +542,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "xtzbsc",
@@ -552,7 +552,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "chz",
@@ -562,7 +562,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "qnt",
@@ -572,7 +572,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "egld",
@@ -582,7 +582,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "aave",
@@ -592,7 +592,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "theta",
@@ -602,7 +602,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "axs",
@@ -612,7 +612,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "tusd",
@@ -622,7 +622,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": true,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "bsv",
@@ -632,7 +632,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "okb",
@@ -642,7 +642,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "galabsc",
@@ -652,7 +652,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "zec",
@@ -662,7 +662,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "usdp",
@@ -672,7 +672,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "bttbsc",
@@ -682,7 +682,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "iota",
@@ -692,7 +692,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "mkr",
@@ -702,7 +702,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "hnt",
@@ -712,7 +712,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "ht",
@@ -722,7 +722,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "snx",
@@ -732,7 +732,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "grt",
@@ -742,7 +742,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "ftm",
@@ -752,7 +752,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "ftmmainnet",
@@ -762,7 +762,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "klay",
@@ -772,7 +772,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "neo",
@@ -782,7 +782,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "rune",
@@ -792,7 +792,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": false
+ "supportsFixedRate": false,
},
{
"ticker": "paxg",
@@ -802,7 +802,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "ldo",
@@ -812,7 +812,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "cake",
@@ -822,7 +822,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "crv",
@@ -832,7 +832,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "nexo",
@@ -842,7 +842,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "bat",
@@ -852,7 +852,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "dash",
@@ -862,7 +862,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "waves",
@@ -872,7 +872,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "zil",
@@ -882,7 +882,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": true,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "lrc",
@@ -892,7 +892,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "enj",
@@ -902,7 +902,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "ksm",
@@ -912,7 +912,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "dcr",
@@ -922,7 +922,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "btg",
@@ -932,7 +932,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "gmt",
@@ -942,7 +942,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "twt",
@@ -952,7 +952,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "gno",
@@ -962,7 +962,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "xem",
@@ -972,7 +972,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "1inch",
@@ -982,7 +982,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "1inchbsc",
@@ -992,7 +992,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "celo",
@@ -1002,7 +1002,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "hot",
@@ -1012,7 +1012,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "ust",
@@ -1023,7 +1023,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": false
+ "supportsFixedRate": false,
},
{
"ticker": "galaerc20",
@@ -1034,7 +1034,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "ankr",
@@ -1044,7 +1044,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "comp",
@@ -1054,7 +1054,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "gt",
@@ -1064,7 +1064,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": false
+ "supportsFixedRate": false,
},
{
"ticker": "cvx",
@@ -1074,7 +1074,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "qtum",
@@ -1084,7 +1084,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "yfi",
@@ -1094,7 +1094,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "xdc",
@@ -1104,7 +1104,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "kda",
@@ -1114,7 +1114,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": false
+ "supportsFixedRate": false,
},
{
"ticker": "iotx",
@@ -1124,7 +1124,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "cel",
@@ -1134,7 +1134,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "gusd",
@@ -1144,7 +1144,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": true,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "tfuel",
@@ -1154,7 +1154,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "rvn",
@@ -1164,7 +1164,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "flux",
@@ -1174,7 +1174,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "bal",
@@ -1184,7 +1184,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "amp",
@@ -1194,7 +1194,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "op",
@@ -1204,7 +1204,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": false
+ "supportsFixedRate": false,
},
{
"ticker": "omg",
@@ -1214,7 +1214,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "one",
@@ -1224,7 +1224,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "zrx",
@@ -1234,7 +1234,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "rsr",
@@ -1244,7 +1244,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "jst",
@@ -1254,7 +1254,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "icx",
@@ -1264,7 +1264,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "xym",
@@ -1274,7 +1274,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "iost",
@@ -1284,7 +1284,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "ens",
@@ -1294,7 +1294,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "lpt",
@@ -1304,7 +1304,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "glm",
@@ -1314,7 +1314,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "audio",
@@ -1324,7 +1324,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "storj",
@@ -1334,7 +1334,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "ont",
@@ -1344,7 +1344,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "ontbsc",
@@ -1354,7 +1354,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "waxp",
@@ -1364,7 +1364,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "srm",
@@ -1374,7 +1374,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiat": false,
"featured": false,
"isStable": false,
- "supportsFixedRate": true
+ "supportsFixedRate": true,
},
{
"ticker": "sc",
@@ -1384,7 +1384,7 @@ const List<Map<String, dynamic>> availableCurrenciesJSON = [
"isFiaWhy this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.