diff --git a/.gitignore b/.gitignore index efa6e25e7..597ed4be4 100644 --- a/.gitignore +++ b/.gitignore @@ -101,21 +101,7 @@ pubspec.yaml # FVM Version Cache .fvm/ -android/app/src/main/jniLibs/arm64-v8a/libwownero_wallet2_api_c.so -android/app/src/main/jniLibs/arm64-v8a/libmonero_wallet2_api_c.so -android/app/src/main/jniLibs/armeabi-v7a/libmonero_wallet2_api_c.so -android/app/src/main/jniLibs/armeabi-v7a/libwownero_wallet2_api_c.so -android/app/src/main/jniLibs/x86_64/libmonero_wallet2_api_c.so -android/app/src/main/jniLibs/x86_64/libwownero_wallet2_api_c.so -macos/monero_wallet2_api_c.dylib -macos/wownero_wallet2_api_c.dylib -/macos/monero_libwallet2_api_c.dylib -/macos/wownero_libwallet2_api_c.dylib -/ios/monero_libwallet2_api_c.dylib -/ios/wownero_libwallet2_api_c.dylib -/android/app/src/main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so -/android/app/src/main/jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so -/android/app/src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so -/android/app/src/main/jniLibs/arm64-v8a/libwownero_libwallet2_api_c.so -/android/app/src/main/jniLibs/armeabi-v7a/libwownero_libwallet2_api_c.so -/android/app/src/main/jniLibs/x86_64/libwownero_libwallet2_api_c.so +scripts/linux/build/libsecret/subprojects/gi-docgen/.meson-subproject-wrap-hash.txt + +crypto_plugins/cs_monero/built_outputs +crypto_plugins/cs_monero/build diff --git a/.gitmodules b/.gitmodules index 925be21c0..2186826df 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "crypto_plugins/flutter_libepiccash"] path = crypto_plugins/flutter_libepiccash url = https://github.com/cypherstack/flutter_libepiccash.git -[submodule "crypto_plugins/flutter_libmonero"] - path = crypto_plugins/flutter_libmonero - url = https://github.com/cypherstack/flutter_libmonero.git [submodule "crypto_plugins/flutter_liblelantus"] path = crypto_plugins/flutter_liblelantus url = https://github.com/cypherstack/flutter_liblelantus.git diff --git a/android/app/src/main/jniLibs/arm64-v8a/.gitkeep b/android/app/src/main/jniLibs/arm64-v8a/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/android/app/src/main/jniLibs/armeabi-v7a/.gitkeep b/android/app/src/main/jniLibs/armeabi-v7a/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/android/app/src/main/jniLibs/x86_64/.gitkeep b/android/app/src/main/jniLibs/x86_64/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/android/build.gradle b/android/build.gradle index baa7dd294..d2ffbffa4 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,16 +1,3 @@ -buildscript { - ext.kotlin_version = '1.8.0' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.3.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - allprojects { repositories { google() @@ -18,12 +5,12 @@ allprojects { } } -rootProject.buildDir = '../build' +rootProject.buildDir = "../build" subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" } subprojects { - project.evaluationDependsOn(':app') + project.evaluationDependsOn(":app") } tasks.register("clean", Delete) { diff --git a/android/gradle.properties b/android/gradle.properties index 38c8d4544..7803bf46c 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,4 +1,3 @@ -org.gradle.jvmargs=-Xmx1536M -android.enableR8=true +org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G android.useAndroidX=true -android.enableJetifier=true +android.enableJetifier=true \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 6b665338b..3c85cfe05 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index 44e62bcf0..a38bba1fb 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,11 +1,25 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version '8.6.0' apply false + id "org.jetbrains.kotlin.android" version "1.8.22" apply false +} + +include ":app" diff --git a/asset_sources/default_themes/campfire/light.zip b/asset_sources/default_themes/campfire/light.zip index 0b3dd09b1..ca0b9f2f7 100644 Binary files a/asset_sources/default_themes/campfire/light.zip and b/asset_sources/default_themes/campfire/light.zip differ diff --git a/asset_sources/default_themes/stack_wallet/dark.zip b/asset_sources/default_themes/stack_wallet/dark.zip index 1e5f6136e..df24199c4 100644 Binary files a/asset_sources/default_themes/stack_wallet/dark.zip and b/asset_sources/default_themes/stack_wallet/dark.zip differ diff --git a/asset_sources/default_themes/stack_wallet/light.zip b/asset_sources/default_themes/stack_wallet/light.zip index bc7a95771..9e0c82d9d 100644 Binary files a/asset_sources/default_themes/stack_wallet/light.zip and b/asset_sources/default_themes/stack_wallet/light.zip differ diff --git a/asset_sources/svg/campfire/churn.svg b/asset_sources/svg/campfire/churn.svg new file mode 100644 index 000000000..12929f15b --- /dev/null +++ b/asset_sources/svg/campfire/churn.svg @@ -0,0 +1,3 @@ + + + diff --git a/asset_sources/svg/stack_duo/churn.svg b/asset_sources/svg/stack_duo/churn.svg new file mode 100644 index 000000000..12929f15b --- /dev/null +++ b/asset_sources/svg/stack_duo/churn.svg @@ -0,0 +1,3 @@ + + + diff --git a/asset_sources/svg/stack_wallet/churn.svg b/asset_sources/svg/stack_wallet/churn.svg new file mode 100644 index 000000000..12929f15b --- /dev/null +++ b/asset_sources/svg/stack_wallet/churn.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/gif/monero-chan-dance.gif b/assets/gif/monero-chan-dance.gif new file mode 100644 index 000000000..d0b303045 Binary files /dev/null and b/assets/gif/monero-chan-dance.gif differ diff --git a/crypto_plugins/flutter_libepiccash b/crypto_plugins/flutter_libepiccash index 19c76409e..0bb1b1ced 160000 --- a/crypto_plugins/flutter_libepiccash +++ b/crypto_plugins/flutter_libepiccash @@ -1 +1 @@ -Subproject commit 19c76409e55f1bfed58855eb767574604376edb6 +Subproject commit 0bb1b1ced6e0d3c66e383698f89825754c692986 diff --git a/crypto_plugins/flutter_liblelantus b/crypto_plugins/flutter_liblelantus index b654bf448..5b08645a5 160000 --- a/crypto_plugins/flutter_liblelantus +++ b/crypto_plugins/flutter_liblelantus @@ -1 +1 @@ -Subproject commit b654bf4488357c8a104900e11f9468d54a39f22b +Subproject commit 5b08645a5b5d30955f4bde2a624ff89ef516e452 diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero deleted file mode 160000 index 982f5ab19..000000000 --- a/crypto_plugins/flutter_libmonero +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 982f5ab19fe0dd3dd3f6be2c46f8dff13d49027c diff --git a/crypto_plugins/frostdart b/crypto_plugins/frostdart index d539de234..2451deab8 160000 --- a/crypto_plugins/frostdart +++ b/crypto_plugins/frostdart @@ -1 +1 @@ -Subproject commit d539de2348bdbb87bac341dcaa6a0755f21d48e2 +Subproject commit 2451deab817b456ad93d5579c0d0687cb681392a diff --git a/docs/building.md b/docs/building.md index 3d35acea2..492639b05 100644 --- a/docs/building.md +++ b/docs/building.md @@ -13,12 +13,12 @@ Here you will find instructions on how to install the necessary tools for buildi The following instructions are for building and running on a Linux host. Alternatively, see the [Mac](#mac-host) and/or [Windows](#windows-host) section. This entire section (except for the Android Studio section) needs to be completed in WSL if building on a Windows host. ### Flutter -Install Flutter 3.22.1 by [following their guide](https://docs.flutter.dev/get-started/install/linux/desktop?tab=download#install-the-flutter-sdk). You can also clone https://github.com/flutter/flutter, check out the `3.22.1` tag, and add its `flutter/bin` folder to your PATH as in +Install Flutter 3.24.3 by [following their guide](https://docs.flutter.dev/get-started/install/linux/desktop?tab=download#install-the-flutter-sdk). You can also clone https://github.com/flutter/flutter, check out the `3.24.3` tag, and add its `flutter/bin` folder to your PATH as in ```sh FLUTTER_DIR="$HOME/development/flutter" git clone https://github.com/flutter/flutter.git "$FLUTTER_DIR" cd "$FLUTTER_DIR" -git checkout 3.22.1 +git checkout 3.24.3 echo 'export PATH="$PATH:'"$FLUTTER_DIR"'/bin"' >> "$HOME/.profile" source "$HOME/.profile" flutter precache @@ -53,14 +53,33 @@ sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 libbz2- ### Build dependencies Install basic dependencies ``` -sudo apt-get install libssl-dev curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev llvm python3-distutils g++ gcc gperf +sudo apt-get install libssl-dev curl unzip automake build-essential file pkg-config git python3 libtool libtinfo6 cmake libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev llvm g++ gcc gperf libopencv-dev python3-typogrify xsltproc valac gobject-introspection meson ``` -Install [Rust](https://www.rust-lang.org/tools/install) with command: +For Ubuntu 20.04, +``` +sudo apt-get install vapigen +pip3 install --upgrade meson==0.64.1 markdown==3.4.1 markupsafe==2.1.1 jinja2==3.1.2 pygments==2.13.0 toml==0.10.2 typogrify==2.0.7 tomli==2.0.1 +``` + +For Ubuntu 24.04, +``` +sudo apt install pipx libgcrypt20-dev libglib2.0-dev libsecret-1-dev +pipx install meson==0.64.1 markdown==3.4.1 markupsafe==2.1.1 jinja2==3.1.2 pygments==2.13.0 toml==0.10.2 typogrify==2.0.7 tomli==2.0.1 +``` + +Install `libtinfo5` (required by [monero_c](https://github.com/MrCyjaneK/monero_c), should be dropped in the future): +``` +wget http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb -O libtinfo5.deb \ + && apt install ./libtinfo5.deb \ + && rm libtinfo5.deb +``` + +Install [Rust](https://www.rust-lang.org/tools/install) via [rustup.rs](https://rustup.rs), the required Rust toolchains, and `cargo-ndk 2.12.7` with command: ``` curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -source ~/.bashrc -rustup install 1.67.1 1.72.0 1.73.0 +source ~/.bashrc +rustup install 1.67.1 1.71.0 1.72.0 1.73.0 rustup default 1.67.1 cargo install cargo-ndk --version 2.12.7 --locked ``` @@ -77,6 +96,7 @@ sudo apt-get install clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev pip3 install --upgrade meson==0.64.1 markdown==3.4.1 markupsafe==2.1.1 jinja2==3.1.2 pygments==2.13.0 toml==0.10.2 typogrify==2.0.7 tomli==2.0.1 ``` +### Clone the repository and initialize submodules After installing the prerequisites listed above, download the code and init the submodules ``` git clone https://github.com/cypherstack/stack_wallet.git @@ -92,17 +112,15 @@ cd scripts/linux cd ../.. ``` -### Build coinlib -Coinlib's native secp256k1 library must be built prior to running Stack Wallet. It can be built from within the root `stack_wallet` folder on a... - - Linux host for Linux targets: `dart run coinlib:build_linux`, or +### Build secp256k1 +Coinlib requires a secp256k1 library to be built prior to running Stack Wallet. It can be built from within the root `stack_wallet` folder on a... + - Linux host for Linux targets: `dart run coinlib:build_linux` (requires [Docker](https://docs.docker.com/engine/install/ubuntu/) or [`podman`](https://podman.io/docs/installation)) - Linux host for Windows targets: `dart run coinlib:build_windows_crosscompile` - Windows host: `dart run coinlib:build_windows` - WSL2 host: `dart run coinlib:build_wsl` - macOS host: `dart run coinlib:build_macos` -To build coinlib on Linux, you will need `docker` (see [installation instructions](https://docs.docker.com/engine/install/ubuntu/)) or [`podman`](https://podman.io/docs/installation) (`sudo apt-get -y install podman`) - -For Windows targets, you can use a `secp256k1.dll` produced by any of the three middle options if the first attempt doesn't succeed. +or by using `scripts/linux/build_secp256k1.sh` or `scripts/windows/build_secp256k1.bat`. ### Run prebuild script @@ -117,7 +135,7 @@ or manually by creating the files referenced in that script with the specified c ### Build plugins #### Build script: `build_app.sh` -The `build_app.sh` script is use to build applications Stack Wallet. View the script's help message with `./build_app.sh -h` for more information on its usage. +The `build_app.sh` script is used to build the Stack Wallet and its family of applications. View the script's help message with `./build_app.sh -h` for more information on its usage. Options: @@ -144,7 +162,7 @@ cd scripts cd scripts ./build_app.sh -a stack_wallet -p linux ``` - + +TODO: configure compiler to prefer built over system libraries. Should already use them? --> #### Building plugins and configure for Windows +Install dependencies like MXE: ``` -cd scripts +cd scripts/windows ./deps.sh -./build_app.sh -a stack_wallet -p windows +``` + +and use `scripts/build_app.sh` to build plugins: +``` +cd .. +./build_app.sh -a stack_wallet -p windows -v 2.1.0 -b 210 ``` ### Running @@ -209,7 +233,7 @@ Download and install [Rust](https://www.rust-lang.org/tools/install). [Rustup]( ``` curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source ~/.bashrc -rustup install 1.67.1 1.72.0 1.73.0 +rustup install 1.67.1 1.71.0 1.72.0 1.73.0 rustup default 1.67.1 cargo install cargo-ndk --version 2.12.7 --locked cargo install cbindgen cargo-lipo @@ -219,7 +243,7 @@ rustup target add aarch64-apple-ios aarch64-apple-darwin Optionally download [Android Studio](https://developer.android.com/studio) as an IDE and activate its Dart and Flutter plugins. VS Code may work as an alternative, but this is not recommended. ### Flutter -Install [Flutter](https://docs.flutter.dev/get-started/install) 3.22.1 on your Mac host by following [these instructions](https://docs.flutter.dev/get-started/install/macos). Run `flutter doctor` in a terminal to confirm its installation. +Install [Flutter](https://docs.flutter.dev/get-started/install) 3.24.3 on your Mac host by following [these instructions](https://docs.flutter.dev/get-started/install/macos). Run `flutter doctor` in a terminal to confirm its installation. ### Build plugins and configure #### Building plugins for iOS @@ -272,16 +296,11 @@ Install the following libraries: sudo apt-get install libgtk2.0-dev ``` -You will also need to install MXE on the WSL2 Ubuntu 20.04 host and can do so by running `stack_wallet/scripts/windows/deps.sh`: -``` -./stack_wallet/scripts/windows/deps.sh -``` - The WSL2 host may optionally be navigated to the `stack_wallet` repository on the Windows host in order to build the plugins in-place and skip the next section in which you copy the `dll`s from WSL2 to Windows. Then build windows `dll` libraries by running the following script on the WSL2 Ubuntu 20.04 host: - `stack_wallet/scripts/windows/build_all.sh` -Copy the resulting `dll`s to their respective positions on the Windows host: +If the DLLs were built on the WSL filesystem instead of on Windows, copy the resulting `dll`s to their respective positions on the Windows host: - `stack_wallet/crypto_plugins/flutter_libepiccash/scripts/windows/build/libepic_cash_wallet.dll` - `stack_wallet/crypto_plugins/flutter_liblelantus/scripts/windows/build/libmobileliblelantus.dll` @@ -294,13 +313,13 @@ Copy the resulting `dll`s to their respective positions on the Windows host: Frostdart will be built by the Windows host later. ### Install Flutter on Windows host -Install Flutter 3.22.1 on your Windows host (not in WSL2) by [following their guide](https://docs.flutter.dev/get-started/install/windows/desktop?tab=download#install-the-flutter-sdk) or by cloning https://github.com/flutter/flutter, checking out the `3.22.1` tag, and adding its `flutter/bin` folder to your PATH as in +Install Flutter 3.24.3 on your Windows host (not in WSL2) by [following their guide](https://docs.flutter.dev/get-started/install/windows/desktop?tab=download#install-the-flutter-sdk) or by cloning https://github.com/flutter/flutter, checking out the `3.24.3` tag, and adding its `flutter/bin` folder to your PATH as in ```bat @echo off set "FLUTTER_DIR=%USERPROFILE%\development\flutter" git clone https://github.com/flutter/flutter.git "%FLUTTER_DIR%" cd /d "%FLUTTER_DIR%" -git checkout 3.22.1 +git checkout 3.24.3 setx PATH "%PATH%;%FLUTTER_DIR%\bin" echo Flutter setup completed. Please restart your command prompt. ``` @@ -310,7 +329,7 @@ Run `flutter doctor` in PowerShell to confirm its installation. ### Rust Install [Rust](https://www.rust-lang.org/tools/install) on the Windows host (not in WSL2). Download the installer from [rustup.rs](https://rustup.rs), make sure it works on the commandline (you may need to open a new terminal), and install the following versions: ``` -rustup install 1.67.1 1.72.0 1.73.0 +rustup install 1.67.1 1.71.0 1.72.0 1.73.0 rustup default 1.67.1 cargo install cargo-ndk --version 2.12.7 --locked ``` diff --git a/ios/MoneroWallet.framework/.gitignore b/ios/MoneroWallet.framework/.gitignore deleted file mode 100644 index 38de9b351..000000000 --- a/ios/MoneroWallet.framework/.gitignore +++ /dev/null @@ -1 +0,0 @@ -MoneroWallet \ No newline at end of file diff --git a/ios/MoneroWallet.framework/Info.plist b/ios/MoneroWallet.framework/Info.plist deleted file mode 100644 index de2be3211..000000000 --- a/ios/MoneroWallet.framework/Info.plist +++ /dev/null @@ -1,57 +0,0 @@ - - - - - BuildMachineOSBuild - 23E224 - CFBundleDevelopmentRegion - en - CFBundleExecutable - MoneroWallet - CFBundleIdentifier - com.cypherstack.MoneroWallet - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - MoneroWallet - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ??? - CFBundleSupportedPlatforms - - iPhoneOS - - CFBundleVersion - 1 - DTCompiler - com.apple.compilers.llvm.clang.1_0 - DTPlatformBuild - 21E210 - DTPlatformName - iphoneos - DTPlatformVersion - 17.4 - DTSDKBuild - 21E210 - DTSDKName - iphoneos17.4 - DTXcode - 1530 - DTXcodeBuild - 15E204a - MinimumOSVersion - 16.0 - UIDeviceFamily - - 1 - 2 - - UIRequiredDeviceCapabilities - - arm64 - - - diff --git a/ios/Podfile.lock b/ios/Podfile.lock index cff46cb7b..4ca2f124b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -3,12 +3,14 @@ PODS: - Flutter - MTBBarcodeScanner - SwiftProtobuf - - coinlib_flutter (0.3.2): + - coinlib_flutter (0.5.0): - Flutter - FlutterMacOS - connectivity_plus (0.0.1): - Flutter - ReachabilitySwift + - cs_monero_flutter_libs (0.0.1): + - Flutter - device_info_plus (0.0.1): - Flutter - devicelocale (0.0.1): @@ -50,8 +52,6 @@ PODS: - Flutter (1.0.0) - flutter_libepiccash (0.0.1): - Flutter - - flutter_libmonero (0.0.1): - - Flutter - flutter_libsparkmobile (0.0.1): - Flutter - flutter_local_notifications (0.0.1): @@ -67,15 +67,16 @@ PODS: - Flutter - lelantus (0.0.1): - Flutter - - local_auth (0.0.1): + - local_auth_darwin (0.0.1): - Flutter + - FlutterMacOS - MTBBarcodeScanner (5.0.11) - package_info_plus (0.4.5): - Flutter - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - - permission_handler_apple (9.1.1): + - permission_handler_apple (9.3.0): - Flutter - ReachabilitySwift (5.0.0) - SDWebImage (5.13.2): @@ -83,18 +84,21 @@ PODS: - SDWebImage/Core (5.13.2) - share_plus (0.0.1): - Flutter - - sqlite3 (3.46.0): - - sqlite3/common (= 3.46.0) - - sqlite3/common (3.46.0) - - sqlite3/fts5 (3.46.0): + - "sqlite3 (3.46.0+1)": + - "sqlite3/common (= 3.46.0+1)" + - "sqlite3/common (3.46.0+1)" + - "sqlite3/dbstatvtab (3.46.0+1)": - sqlite3/common - - sqlite3/perf-threadsafe (3.46.0): + - "sqlite3/fts5 (3.46.0+1)": - sqlite3/common - - sqlite3/rtree (3.46.0): + - "sqlite3/perf-threadsafe (3.46.0+1)": + - sqlite3/common + - "sqlite3/rtree (3.46.0+1)": - sqlite3/common - sqlite3_flutter_libs (0.0.1): - Flutter - - sqlite3 (~> 3.46.0) + - "sqlite3 (~> 3.46.0+1)" + - sqlite3/dbstatvtab - sqlite3/fts5 - sqlite3/perf-threadsafe - sqlite3/rtree @@ -106,19 +110,19 @@ PODS: - Flutter - url_launcher_ios (0.0.1): - Flutter - - wakelock (0.0.1): + - wakelock_plus (0.0.1): - Flutter DEPENDENCIES: - barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`) - coinlib_flutter (from `.symlinks/plugins/coinlib_flutter/darwin`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) + - cs_monero_flutter_libs (from `.symlinks/plugins/cs_monero_flutter_libs/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - devicelocale (from `.symlinks/plugins/devicelocale/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`) - Flutter (from `Flutter`) - flutter_libepiccash (from `.symlinks/plugins/flutter_libepiccash/ios`) - - flutter_libmonero (from `.symlinks/plugins/flutter_libmonero/ios`) - flutter_libsparkmobile (from `.symlinks/plugins/flutter_libsparkmobile/ios`) - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) @@ -127,7 +131,7 @@ DEPENDENCIES: - integration_test (from `.symlinks/plugins/integration_test/ios`) - isar_flutter_libs (from `.symlinks/plugins/isar_flutter_libs/ios`) - lelantus (from `.symlinks/plugins/lelantus/ios`) - - local_auth (from `.symlinks/plugins/local_auth/ios`) + - local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) @@ -136,7 +140,7 @@ DEPENDENCIES: - stack_wallet_backup (from `.symlinks/plugins/stack_wallet_backup/ios`) - tor_ffi_plugin (from `.symlinks/plugins/tor_ffi_plugin/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - - wakelock (from `.symlinks/plugins/wakelock/ios`) + - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`) SPEC REPOS: trunk: @@ -156,6 +160,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/coinlib_flutter/darwin" connectivity_plus: :path: ".symlinks/plugins/connectivity_plus/ios" + cs_monero_flutter_libs: + :path: ".symlinks/plugins/cs_monero_flutter_libs/ios" device_info_plus: :path: ".symlinks/plugins/device_info_plus/ios" devicelocale: @@ -166,8 +172,6 @@ EXTERNAL SOURCES: :path: Flutter flutter_libepiccash: :path: ".symlinks/plugins/flutter_libepiccash/ios" - flutter_libmonero: - :path: ".symlinks/plugins/flutter_libmonero/ios" flutter_libsparkmobile: :path: ".symlinks/plugins/flutter_libsparkmobile/ios" flutter_local_notifications: @@ -184,8 +188,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/isar_flutter_libs/ios" lelantus: :path: ".symlinks/plugins/lelantus/ios" - local_auth: - :path: ".symlinks/plugins/local_auth/ios" + local_auth_darwin: + :path: ".symlinks/plugins/local_auth_darwin/darwin" package_info_plus: :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: @@ -202,45 +206,45 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/tor_ffi_plugin/ios" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" - wakelock: - :path: ".symlinks/plugins/wakelock/ios" + wakelock_plus: + :path: ".symlinks/plugins/wakelock_plus/ios" SPEC CHECKSUMS: barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0 - coinlib_flutter: 6abec900d67762a6e7ccfd567a3cd3ae00bbee35 + coinlib_flutter: 9275e8255ef67d3da33beb6e117d09ced4f46eb5 connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a - device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea - devicelocale: b22617f40038496deffba44747101255cee005b0 + cs_monero_flutter_libs: 43cda3474c2bc907f2b2b5bb26fd89cb864fcfc6 + device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d + devicelocale: 35ba84dc7f45f527c3001535d8c8d104edd5d926 DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 - file_picker: ce3938a0df3cc1ef404671531facef740d03f920 + file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_libepiccash: 36241aa7d3126f6521529985ccb3dc5eaf7bb317 - flutter_libmonero: da68a616b73dd0374a8419c684fa6b6df2c44ffe flutter_libsparkmobile: 6373955cc3327a926d17059e7405dde2fb12f99f flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086 flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be frostdart: 4c72b69ccac2f13ede744107db046a125acce597 - integration_test: 13825b8a9334a850581300559b8839134b124670 - isar_flutter_libs: b69f437aeab9c521821c3f376198c4371fa21073 + integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573 + isar_flutter_libs: fdf730ca925d05687f36d7f1d355e482529ed097 lelantus: 417f0221260013dfc052cae9cf4b741b6479edba - local_auth: 1740f55d7af0a2e2a8684ce225fe79d8931e808c + local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3 MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb - package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7 - path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 - permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 + package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 SDWebImage: 72f86271a6f3139cc7e4a89220946489d4b9a866 - share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028 - sqlite3: 154b084339ede06960a5b3c8160066adc9176b7d - sqlite3_flutter_libs: 0d611efdf6d1c9297d5ab03dab21b75aeebdae31 + share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5 + sqlite3: 292c3e1bfe89f64e51ea7fc7dab9182a017c8630 + sqlite3_flutter_libs: c00457ebd31e59fa6bb830380ddba24d44fbcd3b stack_wallet_backup: 5b8563aba5d8ffbf2ce1944331ff7294a0ec7c03 SwiftProtobuf: 6ef3f0e422ef90d6605ca20b21a94f6c1324d6b3 SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780 tor_ffi_plugin: d80e291b649379c8176e1be739e49be007d4ef93 - url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4 - wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f + url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe + wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1 PODFILE CHECKSUM: 57c8aed26fba39d3ec9424816221f294a07c58eb diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 8117c8b24..ce5273b2e 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import UIKit import Flutter -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/ios/WowneroWallet.framework/.gitignore b/ios/WowneroWallet.framework/.gitignore deleted file mode 100644 index 98afbebec..000000000 --- a/ios/WowneroWallet.framework/.gitignore +++ /dev/null @@ -1 +0,0 @@ -WowneroWallet \ No newline at end of file diff --git a/ios/WowneroWallet.framework/Info.plist b/ios/WowneroWallet.framework/Info.plist deleted file mode 100644 index d1d435920..000000000 --- a/ios/WowneroWallet.framework/Info.plist +++ /dev/null @@ -1,57 +0,0 @@ - - - - - BuildMachineOSBuild - 23E224 - CFBundleDevelopmentRegion - en - CFBundleExecutable - WowneroWallet - CFBundleIdentifier - com.cypherstack.WowneroWallet - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - WowneroWallet - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ??? - CFBundleSupportedPlatforms - - iPhoneOS - - CFBundleVersion - 1 - DTCompiler - com.apple.compilers.llvm.clang.1_0 - DTPlatformBuild - 21E210 - DTPlatformName - iphoneos - DTPlatformVersion - 17.4 - DTSDKBuild - 21E210 - DTSDKName - iphoneos17.4 - DTXcode - 1530 - DTXcodeBuild - 15E204a - MinimumOSVersion - 16.0 - UIDeviceFamily - - 1 - 2 - - UIRequiredDeviceCapabilities - - arm64 - - - diff --git a/lib/app_config.dart b/lib/app_config.dart index 5f9a95829..62ce560c3 100644 --- a/lib/app_config.dart +++ b/lib/app_config.dart @@ -27,6 +27,8 @@ abstract class AppConfig { static List get coins => _supportedCoins; + static ({String from, String to}) get swapDefaults => _swapDefaults; + static bool get isSingleCoinApp => coins.length == 1; static CryptoCurrency? getCryptoCurrencyFor(String coinIdentifier) { diff --git a/lib/db/db_version_migration.dart b/lib/db/db_version_migration.dart index 1415803e4..fcf632669 100644 --- a/lib/db/db_version_migration.dart +++ b/lib/db/db_version_migration.dart @@ -77,6 +77,8 @@ class DbVersionMigrator with WalletDB { name: e.name, id: e.id, useSSL: e.useSSL, + torEnabled: e.torEnabled, + clearnetEnabled: e.clearnetEnabled, ), ) .toList(); @@ -88,6 +90,8 @@ class DbVersionMigrator with WalletDB { name: node.name, id: node.id, useSSL: node.useSSL, + torEnabled: node.torEnabled, + clearnetEnabled: node.clearnetEnabled, ), prefs: prefs, failovers: failovers, diff --git a/lib/db/hive/db.dart b/lib/db/hive/db.dart index 1e4c670e1..c71826fbc 100644 --- a/lib/db/hive/db.dart +++ b/lib/db/hive/db.dart @@ -10,7 +10,7 @@ import 'dart:isolate'; -import 'package:cw_core/wallet_info.dart' as xmr; +import 'package:compat/compat.dart' as lib_monero_compat; import 'package:hive/hive.dart' show Box; import 'package:hive/src/hive_impl.dart'; import 'package:mutex/mutex.dart'; @@ -71,7 +71,7 @@ class DB { Box? _boxTradesV2; Box? _boxTradeNotes; Box? _boxFavoriteWallets; - Box? _walletInfoSource; + Box? _walletInfoSource; Box? _boxPrefs; Box? _boxTradeLookup; Box? _boxDBInfo; @@ -85,7 +85,8 @@ class DB { final Map> _getSparkUsedCoinsTagsCacheBoxes = {}; // exposed for monero - Box get moneroWalletInfoBox => _walletInfoSource!; + Box get moneroWalletInfoBox => + _walletInfoSource!; // mutex for stack backup final mutex = Mutex(); @@ -147,8 +148,8 @@ class DB { _boxTradesV2 = await hive.openBox(boxNameTradesV2); _boxTradeNotes = await hive.openBox(boxNameTradeNotes); _boxTradeLookup = await hive.openBox(boxNameTradeLookup); - _walletInfoSource = - await hive.openBox(xmr.WalletInfo.boxName); + _walletInfoSource = await hive.openBox( + lib_monero_compat.WalletInfo.boxName); _boxFavoriteWallets = await hive.openBox(boxNameFavoriteWallets); await Future.wait([ diff --git a/lib/db/sqlite/firo_cache_writer.dart b/lib/db/sqlite/firo_cache_writer.dart index d42c48834..99d318444 100644 --- a/lib/db/sqlite/firo_cache_writer.dart +++ b/lib/db/sqlite/firo_cache_writer.dart @@ -19,8 +19,8 @@ FCResult _updateSparkUsedTagsWith( ) { // hash the tags here since this function is called in a background isolate final hashedTags = LibSpark.hashTags( - base64Tags: tags.map((e) => e[0] as String), - ); + base64Tags: tags.map((e) => e[0] as String).toSet(), + ).toList(); if (hashedTags.isEmpty) { // nothing to add, return early diff --git a/lib/electrumx_rpc/client_manager.dart b/lib/electrumx_rpc/client_manager.dart index 662c218ea..aea7e34e6 100644 --- a/lib/electrumx_rpc/client_manager.dart +++ b/lib/electrumx_rpc/client_manager.dart @@ -1,6 +1,10 @@ import 'dart:async'; import 'package:electrum_adapter/electrum_adapter.dart'; + +import '../utilities/logger.dart'; +import '../utilities/prefs.dart'; +import '../utilities/tor_plain_net_option_enum.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; class ClientManager { @@ -8,6 +12,7 @@ class ClientManager { static final ClientManager sharedInstance = ClientManager._(); final Map _map = {}; + final Map _mapNet = {}; final Map _heights = {}; final Map> _subscriptions = {}; final Map> _heightCompleters = {}; @@ -22,28 +27,53 @@ class ClientManager { ElectrumClient? getClient({ required CryptoCurrency cryptoCurrency, - }) => - _map[_keyHelper(cryptoCurrency)]; + required TorPlainNetworkOption netType, + }) { + final _key = _keyHelper(cryptoCurrency); - void addClient( + if (netType == _mapNet[_key]) { + return _map[_key]; + } else { + return null; + } + } + + Future addClient( ElectrumClient client, { required CryptoCurrency cryptoCurrency, - }) { + required TorPlainNetworkOption netType, + }) async { final key = _keyHelper(cryptoCurrency); if (_map[key] != null) { - throw Exception("ElectrumX Client for $key already exists."); + if (_mapNet[key] == netType) { + throw Exception( + "ElectrumX Client for $key and $netType already exists.", + ); + } + + await remove(cryptoCurrency: cryptoCurrency); + + _map[key] = client; + _mapNet[key] = netType; } else { _map[key] = client; + _mapNet[key] = netType; } _heightCompleters[key] = Completer(); - _subscriptions[key] = client.subscribeHeaders().listen((event) { - _heights[key] = event.height; + _subscriptions[key] = client.subscribeHeaders().listen( + (event) { + _heights[key] = event.height; - if (!_heightCompleters[key]!.isCompleted) { - _heightCompleters[key]!.complete(event.height); - } - }); + if (!_heightCompleters[key]!.isCompleted) { + _heightCompleters[key]!.complete(event.height); + } + }, + onError: (Object err, StackTrace s) => Logging.instance.log( + "ClientManager listen: $err\n$s", + level: LogLevel.Error, + ), + ); } Future getChainHeightFor(CryptoCurrency cryptoCurrency) async { @@ -60,10 +90,24 @@ class ClientManager { ); } + if (Prefs.instance.useTor) { + if (_mapNet[key]! == TorPlainNetworkOption.clear) { + throw Exception( + "Non-TOR only client for $key found.", + ); + } + } else { + if (_mapNet[key]! == TorPlainNetworkOption.tor) { + throw Exception( + "TOR only client for $key found.", + ); + } + } + return _heights[key] ?? await _heightCompleters[key]!.future; } - Future remove({ + Future<(ElectrumClient?, TorPlainNetworkOption?)> remove({ required CryptoCurrency cryptoCurrency, }) async { final key = _keyHelper(cryptoCurrency); @@ -72,7 +116,7 @@ class ClientManager { _heights.remove(key); _heightCompleters.remove(key); - return _map.remove(key); + return (_map.remove(key), _mapNet.remove(key)); } Future closeAll() async { @@ -91,6 +135,7 @@ class ClientManager { _heightCompleters.clear(); _heights.clear(); _subscriptions.clear(); + _mapNet.clear(); _map.clear(); } } diff --git a/lib/electrumx_rpc/electrumx_client.dart b/lib/electrumx_rpc/electrumx_client.dart index c31a8eac5..dff2b68fd 100644 --- a/lib/electrumx_rpc/electrumx_client.dart +++ b/lib/electrumx_rpc/electrumx_client.dart @@ -29,6 +29,7 @@ import '../utilities/amount/amount.dart'; import '../utilities/extensions/impl/string.dart'; import '../utilities/logger.dart'; import '../utilities/prefs.dart'; +import '../utilities/tor_plain_net_option_enum.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; import '../wallets/crypto_currency/interfaces/electrumx_currency_interface.dart'; import 'client_manager.dart'; @@ -42,6 +43,10 @@ typedef SparkMempoolData = ({ class WifiOnlyException implements Exception {} +class TorOnlyException implements Exception {} + +class ClearnetOnlyException implements Exception {} + class ElectrumXNode { ElectrumXNode({ required this.address, @@ -49,12 +54,16 @@ class ElectrumXNode { required this.name, required this.id, required this.useSSL, + required this.torEnabled, + required this.clearnetEnabled, }); final String address; final int port; final String name; final String id; final bool useSSL; + final bool torEnabled; + final bool clearnetEnabled; factory ElectrumXNode.from(ElectrumXNode node) { return ElectrumXNode( @@ -63,6 +72,8 @@ class ElectrumXNode { name: node.name, id: node.id, useSSL: node.useSSL, + torEnabled: node.torEnabled, + clearnetEnabled: node.clearnetEnabled, ); } @@ -74,6 +85,7 @@ class ElectrumXNode { class ElectrumXClient { final CryptoCurrency cryptoCurrency; + final TorPlainNetworkOption netType; String get host => _host; late String _host; @@ -90,6 +102,7 @@ class ElectrumXClient { ElectrumClient? getElectrumAdapter() => ClientManager.sharedInstance.getClient( cryptoCurrency: cryptoCurrency, + netType: netType, ); late Prefs _prefs; @@ -119,6 +132,7 @@ class ElectrumXClient { required int port, required bool useSSL, required Prefs prefs, + required this.netType, required List failovers, required this.cryptoCurrency, this.connectionTimeoutForSpecialCaseJsonRPCClients = @@ -168,6 +182,7 @@ class ElectrumXClient { _electrumAdapterChannel = null; await (await ClientManager.sharedInstance .remove(cryptoCurrency: cryptoCurrency)) + .$1 ?.close(); // Also close any chain height services that are currently open. @@ -193,6 +208,10 @@ class ElectrumXClient { failovers: failovers, globalEventBusForTesting: globalEventBusForTesting, cryptoCurrency: cryptoCurrency, + netType: TorPlainNetworkOption.fromNodeData( + node.torEnabled, + node.clearnetEnabled, + ), ); } @@ -236,6 +255,18 @@ class ElectrumXClient { // Get the proxy info from the TorService. proxyInfo = _torService.getProxyInfo(); } + + if (netType == TorPlainNetworkOption.clear) { + _electrumAdapterChannel = null; + await ClientManager.sharedInstance + .remove(cryptoCurrency: cryptoCurrency); + } + } else { + if (netType == TorPlainNetworkOption.tor) { + _electrumAdapterChannel = null; + await ClientManager.sharedInstance + .remove(cryptoCurrency: cryptoCurrency); + } } // If the current ElectrumAdapterClient is closed, create a new one. @@ -288,9 +319,10 @@ class ElectrumXClient { ); } - ClientManager.sharedInstance.addClient( + await ClientManager.sharedInstance.addClient( newClient, cryptoCurrency: cryptoCurrency, + netType: netType, ); } @@ -352,6 +384,10 @@ class ElectrumXClient { return response; } on WifiOnlyException { rethrow; + } on ClearnetOnlyException { + rethrow; + } on TorOnlyException { + rethrow; } on SocketException { // likely timed out so then retry if (retries > 0) { @@ -442,6 +478,10 @@ class ElectrumXClient { return response; } on WifiOnlyException { rethrow; + } on ClearnetOnlyException { + rethrow; + } on TorOnlyException { + rethrow; } on SocketException { // likely timed out so then retry if (retries > 0) { @@ -488,9 +528,17 @@ class ElectrumXClient { return await request( requestID: requestID, command: 'server.ping', - requestTimeout: const Duration(seconds: 2), + requestTimeout: const Duration(seconds: 3), retries: retryCount, - ).timeout(const Duration(seconds: 2)) as bool; + ).timeout( + const Duration(seconds: 3), + onTimeout: () { + Logging.instance.log( + "ElectrumxClient.ping timed out with retryCount=$retryCount, host=$_host", + level: LogLevel.Debug, + ); + }, + ) as bool; } catch (e) { rethrow; } diff --git a/lib/exceptions/wallet/node_tor_mismatch_config_exception.dart b/lib/exceptions/wallet/node_tor_mismatch_config_exception.dart new file mode 100644 index 000000000..d262ac532 --- /dev/null +++ b/lib/exceptions/wallet/node_tor_mismatch_config_exception.dart @@ -0,0 +1,8 @@ +class NodeTorMismatchConfigException implements Exception { + final String message; + + NodeTorMismatchConfigException({required this.message}); + + @override + String toString() => message; +} diff --git a/lib/main.dart b/lib/main.dart index 09ad6f068..e2cae6f72 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -13,15 +13,11 @@ import 'dart:io'; import 'dart:math'; import 'package:coinlib_flutter/coinlib_flutter.dart'; -import 'package:cw_core/node.dart'; -import 'package:cw_core/pathForWallet.dart'; -import 'package:cw_core/unspent_coins_info.dart'; -import 'package:cw_core/wallet_info.dart'; -import 'package:cw_core/wallet_type.dart'; +import 'package:compat/compat.dart' as lib_monero_compat; +import 'package:cs_monero/cs_monero.dart' as lib_monero; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_libmonero/monero/monero.dart'; -import 'package:flutter_libmonero/wownero/wownero.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -94,12 +90,6 @@ void main(List args) async { StackFileSystem.setDesktopOverrideDir(args.last); } - // Tell flutter_libmonero how to get access to the application dir - FS.setApplicationRootDirectoryFunction( - StackFileSystem.applicationRootDirectory, - ); - // TODO set any other external libs file paths (bad external lib design workaround) - final loadCoinlibFuture = loadCoinlib(); GoogleFonts.config.allowRuntimeFetching = false; @@ -172,15 +162,14 @@ void main(List args) async { // node model adapter DB.instance.hive.registerAdapter(NodeModelAdapter()); - DB.instance.hive.registerAdapter(NodeAdapter()); - - if (!DB.instance.hive.isAdapterRegistered(WalletInfoAdapter().typeId)) { - DB.instance.hive.registerAdapter(WalletInfoAdapter()); + if (!DB.instance.hive + .isAdapterRegistered(lib_monero_compat.WalletInfoAdapter().typeId)) { + DB.instance.hive.registerAdapter(lib_monero_compat.WalletInfoAdapter()); } - DB.instance.hive.registerAdapter(WalletTypeAdapter()); + DB.instance.hive.registerAdapter(lib_monero_compat.WalletTypeAdapter()); - DB.instance.hive.registerAdapter(UnspentCoinsInfoAdapter()); + lib_monero.Logging.useLogger = kDebugMode; DB.instance.hive.init( (await StackFileSystem.applicationHiveDirectory()).path, @@ -237,12 +226,9 @@ void main(List args) async { } } - monero.onStartup(); - wownero.onStartup(); - // SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, // overlays: [SystemUiOverlay.bottom]); - await NotificationApi.init(); + unawaited(NotificationApi.init()); await loadCoinlibFuture; @@ -378,7 +364,8 @@ class _MaterialAppWithThemeState extends ConsumerState // TODO: this should probably run unawaited. Keep commented out for now as proper community nodes ui hasn't been implemented yet // unawaited(_nodeService.updateCommunityNodes()); - if (AppConfig.hasFeature(AppFeature.swap)) { + if (AppConfig.hasFeature(AppFeature.swap) && + ref.read(prefsChangeNotifierProvider).enableExchange) { await ExchangeDataLoadingService.instance.initDB(); // run without awaiting if (ref.read(prefsChangeNotifierProvider).externalCalls && diff --git a/lib/models/isar/exchange_cache/currency.g.dart b/lib/models/isar/exchange_cache/currency.g.dart index 8f81bfdc0..89d93a11d 100644 --- a/lib/models/isar/exchange_cache/currency.g.dart +++ b/lib/models/isar/exchange_cache/currency.g.dart @@ -7,7 +7,7 @@ part of 'currency.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetCurrencyCollection on Isar { IsarCollection get currencies => this.collection(); @@ -135,7 +135,7 @@ const CurrencySchema = CollectionSchema( getId: _currencyGetId, getLinks: _currencyGetLinks, attach: _currencyAttach, - version: '3.0.5', + version: '3.1.8', ); int _currencyEstimateSize( diff --git a/lib/models/isar/exchange_cache/pair.g.dart b/lib/models/isar/exchange_cache/pair.g.dart index eae9ee5b5..0f9b6435d 100644 --- a/lib/models/isar/exchange_cache/pair.g.dart +++ b/lib/models/isar/exchange_cache/pair.g.dart @@ -7,7 +7,7 @@ part of 'pair.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetPairCollection on Isar { IsarCollection get pairs => this.collection(); @@ -92,7 +92,7 @@ const PairSchema = CollectionSchema( getId: _pairGetId, getLinks: _pairGetLinks, attach: _pairAttach, - version: '3.0.5', + version: '3.1.8', ); int _pairEstimateSize( diff --git a/lib/models/isar/models/address_label.g.dart b/lib/models/isar/models/address_label.g.dart index 2a6b41e70..a87af5963 100644 --- a/lib/models/isar/models/address_label.g.dart +++ b/lib/models/isar/models/address_label.g.dart @@ -7,7 +7,7 @@ part of 'address_label.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetAddressLabelCollection on Isar { IsarCollection get addressLabels => this.collection(); @@ -81,7 +81,7 @@ const AddressLabelSchema = CollectionSchema( getId: _addressLabelGetId, getLinks: _addressLabelGetLinks, attach: _addressLabelAttach, - version: '3.0.5', + version: '3.1.8', ); int _addressLabelEstimateSize( diff --git a/lib/models/isar/models/block_explorer.g.dart b/lib/models/isar/models/block_explorer.g.dart index f524392d5..0c4be65bc 100644 --- a/lib/models/isar/models/block_explorer.g.dart +++ b/lib/models/isar/models/block_explorer.g.dart @@ -7,7 +7,7 @@ part of 'block_explorer.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetTransactionBlockExplorerCollection on Isar { IsarCollection get transactionBlockExplorers => @@ -54,7 +54,7 @@ const TransactionBlockExplorerSchema = CollectionSchema( getId: _transactionBlockExplorerGetId, getLinks: _transactionBlockExplorerGetLinks, attach: _transactionBlockExplorerAttach, - version: '3.0.5', + version: '3.1.8', ); int _transactionBlockExplorerEstimateSize( diff --git a/lib/models/isar/models/blockchain_data/address.dart b/lib/models/isar/models/blockchain_data/address.dart index bcc7d22c5..584636050 100644 --- a/lib/models/isar/models/blockchain_data/address.dart +++ b/lib/models/isar/models/blockchain_data/address.dart @@ -11,6 +11,7 @@ import 'dart:convert'; import 'package:isar/isar.dart'; + import '../../../../exceptions/address/address_exception.dart'; import 'crypto_currency_address.dart'; import 'transaction.dart'; @@ -27,6 +28,7 @@ class Address extends CryptoCurrencyAddress { required this.derivationPath, required this.type, required this.subType, + this.zSafeFrost, this.otherData, }); @@ -55,6 +57,8 @@ class Address extends CryptoCurrencyAddress { final transactions = IsarLinks(); + late final bool? zSafeFrost; + int derivationChain() { if (subType == AddressSubType.receiving) { return 0; // 0 for receiving (external) @@ -80,6 +84,7 @@ class Address extends CryptoCurrencyAddress { AddressType? type, AddressSubType? subType, DerivationPath? derivationPath, + bool? zSafeFrost, String? otherData, }) { return Address( @@ -90,6 +95,7 @@ class Address extends CryptoCurrencyAddress { type: type ?? this.type, subType: subType ?? this.subType, derivationPath: derivationPath ?? this.derivationPath, + zSafeFrost: zSafeFrost ?? this.zSafeFrost, otherData: otherData ?? this.otherData, ); } @@ -105,6 +111,7 @@ class Address extends CryptoCurrencyAddress { "subType: ${subType.name}, " "transactionsLength: ${transactions.length} " "derivationPath: $derivationPath, " + "zSafeFrost: $zSafeFrost, " "otherData: $otherData, " "}"; @@ -117,6 +124,7 @@ class Address extends CryptoCurrencyAddress { "type": type.name, "subType": subType.name, "derivationPath": derivationPath?.value, + "zSafeFrost": zSafeFrost, "otherData": otherData, }; return jsonEncode(result); @@ -143,6 +151,7 @@ class Address extends CryptoCurrencyAddress { derivationPath: derivationPath, type: AddressType.values.byName(json["type"] as String), subType: AddressSubType.values.byName(json["subType"] as String), + zSafeFrost: json["zSafeFrost"] as bool?, otherData: json["otherData"] as String?, ); } @@ -165,7 +174,8 @@ enum AddressType { tezos, frostMS, p2tr, - solana; + solana, + cardanoShelley; String get readableName { switch (this) { @@ -201,6 +211,8 @@ enum AddressType { return "Solana"; case AddressType.p2tr: return "P2TR (taproot)"; + case AddressType.cardanoShelley: + return "Cardano Shelley"; } } } diff --git a/lib/models/isar/models/blockchain_data/address.g.dart b/lib/models/isar/models/blockchain_data/address.g.dart index 7e78cbee8..092198990 100644 --- a/lib/models/isar/models/blockchain_data/address.g.dart +++ b/lib/models/isar/models/blockchain_data/address.g.dart @@ -7,7 +7,7 @@ part of 'address.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetAddressCollection on Isar { IsarCollection
get addresses => this.collection(); @@ -59,6 +59,11 @@ const AddressSchema = CollectionSchema( id: 7, name: r'walletId', type: IsarType.string, + ), + r'zSafeFrost': PropertySchema( + id: 8, + name: r'zSafeFrost', + type: IsarType.bool, ) }, estimateSize: _addressEstimateSize, @@ -124,7 +129,7 @@ const AddressSchema = CollectionSchema( getId: _addressGetId, getLinks: _addressGetLinks, attach: _addressAttach, - version: '3.0.5', + version: '3.1.8', ); int _addressEstimateSize( @@ -172,6 +177,7 @@ void _addressSerialize( writer.writeByte(offsets[5], object.type.index); writer.writeString(offsets[6], object.value); writer.writeString(offsets[7], object.walletId); + writer.writeBool(offsets[8], object.zSafeFrost); } Address _addressDeserialize( @@ -195,6 +201,7 @@ Address _addressDeserialize( AddressType.p2pkh, value: reader.readString(offsets[6]), walletId: reader.readString(offsets[7]), + zSafeFrost: reader.readBoolOrNull(offsets[8]), ); object.id = id; return object; @@ -229,6 +236,8 @@ P _addressDeserializeProp

( return (reader.readString(offset)) as P; case 7: return (reader.readString(offset)) as P; + case 8: + return (reader.readBoolOrNull(offset)) as P; default: throw IsarError('Unknown property with id $propertyId'); } @@ -1474,6 +1483,32 @@ extension AddressQueryFilter )); }); } + + QueryBuilder zSafeFrostIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'zSafeFrost', + )); + }); + } + + QueryBuilder zSafeFrostIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'zSafeFrost', + )); + }); + } + + QueryBuilder zSafeFrostEqualTo( + bool? value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'zSafeFrost', + value: value, + )); + }); + } } extension AddressQueryObject @@ -1621,6 +1656,18 @@ extension AddressQuerySortBy on QueryBuilder { return query.addSortBy(r'walletId', Sort.desc); }); } + + QueryBuilder sortByZSafeFrost() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'zSafeFrost', Sort.asc); + }); + } + + QueryBuilder sortByZSafeFrostDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'zSafeFrost', Sort.desc); + }); + } } extension AddressQuerySortThenBy @@ -1708,6 +1755,18 @@ extension AddressQuerySortThenBy return query.addSortBy(r'walletId', Sort.desc); }); } + + QueryBuilder thenByZSafeFrost() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'zSafeFrost', Sort.asc); + }); + } + + QueryBuilder thenByZSafeFrostDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'zSafeFrost', Sort.desc); + }); + } } extension AddressQueryWhereDistinct @@ -1756,6 +1815,12 @@ extension AddressQueryWhereDistinct return query.addDistinctBy(r'walletId', caseSensitive: caseSensitive); }); } + + QueryBuilder distinctByZSafeFrost() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'zSafeFrost'); + }); + } } extension AddressQueryProperty @@ -1814,6 +1879,12 @@ extension AddressQueryProperty return query.addPropertyName(r'walletId'); }); } + + QueryBuilder zSafeFrostProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'zSafeFrost'); + }); + } } // ************************************************************************** @@ -1821,7 +1892,7 @@ extension AddressQueryProperty // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types const DerivationPathSchema = Schema( name: r'DerivationPath', diff --git a/lib/models/isar/models/blockchain_data/input.g.dart b/lib/models/isar/models/blockchain_data/input.g.dart index 608446fea..a08dc7637 100644 --- a/lib/models/isar/models/blockchain_data/input.g.dart +++ b/lib/models/isar/models/blockchain_data/input.g.dart @@ -7,7 +7,7 @@ part of 'input.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types const InputSchema = Schema( name: r'Input', diff --git a/lib/models/isar/models/blockchain_data/output.g.dart b/lib/models/isar/models/blockchain_data/output.g.dart index 5e3bbf02d..6a5812373 100644 --- a/lib/models/isar/models/blockchain_data/output.g.dart +++ b/lib/models/isar/models/blockchain_data/output.g.dart @@ -7,7 +7,7 @@ part of 'output.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types const OutputSchema = Schema( name: r'Output', diff --git a/lib/models/isar/models/blockchain_data/transaction.g.dart b/lib/models/isar/models/blockchain_data/transaction.g.dart index 965a64870..0d34d133d 100644 --- a/lib/models/isar/models/blockchain_data/transaction.g.dart +++ b/lib/models/isar/models/blockchain_data/transaction.g.dart @@ -7,7 +7,7 @@ part of 'transaction.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetTransactionCollection on Isar { IsarCollection get transactions => this.collection(); @@ -171,7 +171,7 @@ const TransactionSchema = CollectionSchema( getId: _transactionGetId, getLinks: _transactionGetLinks, attach: _transactionAttach, - version: '3.0.5', + version: '3.1.8', ); int _transactionEstimateSize( diff --git a/lib/models/isar/models/blockchain_data/utxo.dart b/lib/models/isar/models/blockchain_data/utxo.dart index 7bbf50896..87558c541 100644 --- a/lib/models/isar/models/blockchain_data/utxo.dart +++ b/lib/models/isar/models/blockchain_data/utxo.dart @@ -8,6 +8,7 @@ * */ +import 'dart:convert'; import 'dart:math'; import 'package:isar/isar.dart'; @@ -79,9 +80,28 @@ class UTXO { return max(0, currentChainHeight - (blockHeight! - 1)); } - bool isConfirmed(int currentChainHeight, int minimumConfirms) { + bool isConfirmed( + int currentChainHeight, + int minimumConfirms, + int minimumCoinbaseConfirms, + ) { final confirmations = getConfirmations(currentChainHeight); - return confirmations >= minimumConfirms; + return confirmations >= + (isCoinbase ? minimumCoinbaseConfirms : minimumConfirms); + } + + @ignore + String? get keyImage { + if (otherData == null) { + return null; + } + + try { + final map = jsonDecode(otherData!) as Map; + return map["keyImage"] as String; + } catch (_) { + return null; + } } UTXO copyWith({ diff --git a/lib/models/isar/models/blockchain_data/utxo.g.dart b/lib/models/isar/models/blockchain_data/utxo.g.dart index b12e9b470..0d2a0fcd7 100644 --- a/lib/models/isar/models/blockchain_data/utxo.g.dart +++ b/lib/models/isar/models/blockchain_data/utxo.g.dart @@ -7,7 +7,7 @@ part of 'utxo.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetUTXOCollection on Isar { IsarCollection get utxos => this.collection(); @@ -149,7 +149,7 @@ const UTXOSchema = CollectionSchema( getId: _uTXOGetId, getLinks: _uTXOGetLinks, attach: _uTXOAttach, - version: '3.0.5', + version: '3.1.8', ); int _uTXOEstimateSize( diff --git a/lib/models/isar/models/blockchain_data/v2/input_v2.g.dart b/lib/models/isar/models/blockchain_data/v2/input_v2.g.dart index c1a3b781b..fb0e9dd61 100644 --- a/lib/models/isar/models/blockchain_data/v2/input_v2.g.dart +++ b/lib/models/isar/models/blockchain_data/v2/input_v2.g.dart @@ -7,7 +7,7 @@ part of 'input_v2.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types const OutpointV2Schema = Schema( name: r'OutpointV2', @@ -330,7 +330,7 @@ extension OutpointV2QueryObject on QueryBuilder {} // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types const InputV2Schema = Schema( name: r'InputV2', diff --git a/lib/models/isar/models/blockchain_data/v2/output_v2.g.dart b/lib/models/isar/models/blockchain_data/v2/output_v2.g.dart index e26075d44..539ad6dbd 100644 --- a/lib/models/isar/models/blockchain_data/v2/output_v2.g.dart +++ b/lib/models/isar/models/blockchain_data/v2/output_v2.g.dart @@ -7,7 +7,7 @@ part of 'output_v2.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types const OutputV2Schema = Schema( name: r'OutputV2', diff --git a/lib/models/isar/models/blockchain_data/v2/transaction_v2.dart b/lib/models/isar/models/blockchain_data/v2/transaction_v2.dart index 4f4c123c7..3d8903391 100644 --- a/lib/models/isar/models/blockchain_data/v2/transaction_v2.dart +++ b/lib/models/isar/models/blockchain_data/v2/transaction_v2.dart @@ -87,7 +87,10 @@ class TransactionV2 { ); } + @ignore int? get size => _getFromOtherData(key: TxV2OdKeys.size) as int?; + + @ignore int? get vSize => _getFromOtherData(key: TxV2OdKeys.vSize) as int?; bool get isEpiccashTransaction => @@ -109,9 +112,14 @@ class TransactionV2 { return max(0, currentChainHeight - (height! - 1)); } - bool isConfirmed(int currentChainHeight, int minimumConfirms) { + bool isConfirmed( + int currentChainHeight, + int minimumConfirms, + int minimumCoinbaseConfirms, + ) { final confirmations = getConfirmations(currentChainHeight); - return confirmations >= minimumConfirms; + return confirmations >= + (isCoinbase() ? minimumCoinbaseConfirms : minimumConfirms); } Amount getFee({required int fractionDigits}) { @@ -121,6 +129,10 @@ class TransactionV2 { return fee; } + if (isCoinbase()) { + return Amount.zeroWith(fractionDigits: fractionDigits); + } + final inSum = inputs.map((e) => e.value).reduce((value, element) => value += element); final outSum = outputs @@ -131,6 +143,14 @@ class TransactionV2 { } Amount getAmountReceivedInThisWallet({required int fractionDigits}) { + if (_isMonero()) { + if (type == TransactionType.incoming) { + return _getMoneroAmount()!; + } else { + return Amount.zeroWith(fractionDigits: fractionDigits); + } + } + final outSum = outputs .where((e) => e.walletOwns) .fold(BigInt.zero, (p, e) => p + e.value); @@ -148,6 +168,14 @@ class TransactionV2 { } Amount getAmountSentFromThisWallet({required int fractionDigits}) { + if (_isMonero()) { + if (type == TransactionType.outgoing) { + return _getMoneroAmount()!; + } else { + return Amount.zeroWith(fractionDigits: fractionDigits); + } + } + final inSum = inputs .where((e) => e.walletOwns) .fold(BigInt.zero, (p, e) => p + e.value); @@ -188,18 +216,40 @@ class TransactionV2 { } } + Amount? _getMoneroAmount() { + try { + return Amount.fromSerializedJsonString( + _getFromOtherData(key: TxV2OdKeys.moneroAmount) as String, + ); + } catch (_) { + return null; + } + } + + bool _isMonero() { + final value = _getFromOtherData(key: TxV2OdKeys.isMoneroTransaction); + return value is bool ? value : false; + } + String statusLabel({ required int currentChainHeight, required int minConfirms, + required int minCoinbaseConfirms, }) { + String prettyConfirms() => "(" + "${getConfirmations(currentChainHeight)}" + "/" + "${(isCoinbase() ? minCoinbaseConfirms : minConfirms)}" + ")"; + if (subType == TransactionSubType.cashFusion || subType == TransactionSubType.mint || (subType == TransactionSubType.sparkMint && type == TransactionType.sentToSelf)) { - if (isConfirmed(currentChainHeight, minConfirms)) { + if (isConfirmed(currentChainHeight, minConfirms, minCoinbaseConfirms)) { return "Anonymized"; } else { - return "Anonymizing"; + return "Anonymizing ${prettyConfirms()}"; } } @@ -211,7 +261,7 @@ class TransactionV2 { if (isCancelled) { return "Cancelled"; } else if (type == TransactionType.incoming) { - if (isConfirmed(currentChainHeight, minConfirms)) { + if (isConfirmed(currentChainHeight, minConfirms, minCoinbaseConfirms)) { return "Received"; } else { if (numberOfMessages == 1) { @@ -219,11 +269,11 @@ class TransactionV2 { } else if ((numberOfMessages ?? 0) > 1) { return "Receiving (waiting for confirmations)"; // TODO test if the sender still has to open again after the receiver has 2 messages present, ie. sender->receiver->sender->node (yes) vs. sender->receiver->node (no) } else { - return "Receiving"; + return "Receiving ${prettyConfirms()}"; } } } else if (type == TransactionType.outgoing) { - if (isConfirmed(currentChainHeight, minConfirms)) { + if (isConfirmed(currentChainHeight, minConfirms, minCoinbaseConfirms)) { return "Sent (confirmed)"; } else { if (numberOfMessages == 1) { @@ -231,7 +281,7 @@ class TransactionV2 { } else if ((numberOfMessages ?? 0) > 1) { return "Sending (waiting for confirmations)"; } else { - return "Sending"; + return "Sending ${prettyConfirms()}"; } } } @@ -241,19 +291,23 @@ class TransactionV2 { // if (_transaction.isMinting) { // return "Minting"; // } else - if (isConfirmed(currentChainHeight, minConfirms)) { + if (isConfirmed(currentChainHeight, minConfirms, minCoinbaseConfirms)) { return "Received"; } else { - return "Receiving"; + return "Receiving ${prettyConfirms()}"; } } else if (type == TransactionType.outgoing) { - if (isConfirmed(currentChainHeight, minConfirms)) { + if (isConfirmed(currentChainHeight, minConfirms, minCoinbaseConfirms)) { return "Sent"; } else { - return "Sending"; + return "Sending ${prettyConfirms()}"; } } else if (type == TransactionType.sentToSelf) { - return "Sent to self"; + if (isConfirmed(currentChainHeight, minConfirms, minCoinbaseConfirms)) { + return "Sent to self"; + } else { + return "Sent to self ${prettyConfirms()}"; + } } else { return type.name; } @@ -267,6 +321,9 @@ class TransactionV2 { return map[key]; } + bool isCoinbase() => + type == TransactionType.incoming && inputs.any((e) => e.coinbase != null); + @override String toString() { return 'TransactionV2(\n' @@ -297,4 +354,7 @@ abstract final class TxV2OdKeys { static const contractAddress = "contractAddress"; static const nonce = "nonce"; static const overrideFee = "overrideFee"; + static const moneroAmount = "moneroAmount"; + static const moneroAccountIndex = "moneroAccountIndex"; + static const isMoneroTransaction = "isMoneroTransaction"; } diff --git a/lib/models/isar/models/blockchain_data/v2/transaction_v2.g.dart b/lib/models/isar/models/blockchain_data/v2/transaction_v2.g.dart index 83006da89..68d8a18c5 100644 --- a/lib/models/isar/models/blockchain_data/v2/transaction_v2.g.dart +++ b/lib/models/isar/models/blockchain_data/v2/transaction_v2.g.dart @@ -7,7 +7,7 @@ part of 'transaction_v2.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetTransactionV2Collection on Isar { IsarCollection get transactionV2s => this.collection(); @@ -177,7 +177,7 @@ const TransactionV2Schema = CollectionSchema( getId: _transactionV2GetId, getLinks: _transactionV2GetLinks, attach: _transactionV2Attach, - version: '3.0.5', + version: '3.1.8', ); int _transactionV2EstimateSize( diff --git a/lib/models/isar/models/contact_entry.g.dart b/lib/models/isar/models/contact_entry.g.dart index dc1eb2b61..282112f55 100644 --- a/lib/models/isar/models/contact_entry.g.dart +++ b/lib/models/isar/models/contact_entry.g.dart @@ -7,7 +7,7 @@ part of 'contact_entry.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetContactEntryCollection on Isar { IsarCollection get contactEntrys => this.collection(); @@ -69,7 +69,7 @@ const ContactEntrySchema = CollectionSchema( getId: _contactEntryGetId, getLinks: _contactEntryGetLinks, attach: _contactEntryAttach, - version: '3.0.5', + version: '3.1.8', ); int _contactEntryEstimateSize( @@ -1142,7 +1142,7 @@ extension ContactEntryQueryProperty // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types const ContactAddressEntrySchema = Schema( name: r'ContactAddressEntry', diff --git a/lib/models/isar/models/encrypted_string_value.g.dart b/lib/models/isar/models/encrypted_string_value.g.dart index fdfe5527a..5b3490dcc 100644 --- a/lib/models/isar/models/encrypted_string_value.g.dart +++ b/lib/models/isar/models/encrypted_string_value.g.dart @@ -7,7 +7,7 @@ part of 'encrypted_string_value.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetEncryptedStringValueCollection on Isar { IsarCollection get encryptedStringValues => @@ -54,7 +54,7 @@ const EncryptedStringValueSchema = CollectionSchema( getId: _encryptedStringValueGetId, getLinks: _encryptedStringValueGetLinks, attach: _encryptedStringValueAttach, - version: '3.0.5', + version: '3.1.8', ); int _encryptedStringValueEstimateSize( diff --git a/lib/models/isar/models/ethereum/eth_contract.g.dart b/lib/models/isar/models/ethereum/eth_contract.g.dart index bc9548e8d..fcb1af11d 100644 --- a/lib/models/isar/models/ethereum/eth_contract.g.dart +++ b/lib/models/isar/models/ethereum/eth_contract.g.dart @@ -7,7 +7,7 @@ part of 'eth_contract.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetEthContractCollection on Isar { IsarCollection get ethContracts => this.collection(); @@ -74,7 +74,7 @@ const EthContractSchema = CollectionSchema( getId: _ethContractGetId, getLinks: _ethContractGetLinks, attach: _ethContractAttach, - version: '3.0.5', + version: '3.1.8', ); int _ethContractEstimateSize( diff --git a/lib/models/isar/models/firo_specific/lelantus_coin.g.dart b/lib/models/isar/models/firo_specific/lelantus_coin.g.dart index 4b9214889..1c0f4d405 100644 --- a/lib/models/isar/models/firo_specific/lelantus_coin.g.dart +++ b/lib/models/isar/models/firo_specific/lelantus_coin.g.dart @@ -7,7 +7,7 @@ part of 'lelantus_coin.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetLelantusCoinCollection on Isar { IsarCollection get lelantusCoins => this.collection(); @@ -101,7 +101,7 @@ const LelantusCoinSchema = CollectionSchema( getId: _lelantusCoinGetId, getLinks: _lelantusCoinGetLinks, attach: _lelantusCoinAttach, - version: '3.0.5', + version: '3.1.8', ); int _lelantusCoinEstimateSize( diff --git a/lib/models/isar/models/log.g.dart b/lib/models/isar/models/log.g.dart index 8a4cf22b1..ebad873f2 100644 --- a/lib/models/isar/models/log.g.dart +++ b/lib/models/isar/models/log.g.dart @@ -7,7 +7,7 @@ part of 'log.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetLogCollection on Isar { IsarCollection get logs => this.collection(); @@ -59,7 +59,7 @@ const LogSchema = CollectionSchema( getId: _logGetId, getLinks: _logGetLinks, attach: _logAttach, - version: '3.0.5', + version: '3.1.8', ); int _logEstimateSize( diff --git a/lib/models/isar/models/sent_to_address.dart b/lib/models/isar/models/sent_to_address.dart new file mode 100644 index 000000000..f18b18a08 --- /dev/null +++ b/lib/models/isar/models/sent_to_address.dart @@ -0,0 +1,35 @@ +/* + * This file is part of Stack Wallet. + * + * Copyright (c) 2023 Cypher Stack + * All Rights Reserved. + * The code is distributed under GPLv3 license, see LICENSE file for details. + * Generated by Cypher Stack on 2023-05-26 + * + */ + +import 'package:isar/isar.dart'; + +part 'sent_to_address.g.dart'; + +@Collection() +class SentToAddress { + SentToAddress({ + required this.walletId, + required this.txid, + required this.value, + this.label = "", + }); + + Id id = Isar.autoIncrement; + + @Index() + late final String walletId; + + @Index(unique: true, composite: [CompositeIndex("walletId")]) + late final String txid; + + late final String value; + + late final String label; +} diff --git a/lib/models/isar/models/sent_to_address.g.dart b/lib/models/isar/models/sent_to_address.g.dart new file mode 100644 index 000000000..88153da9d --- /dev/null +++ b/lib/models/isar/models/sent_to_address.g.dart @@ -0,0 +1,1248 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'sent_to_address.dart'; + +// ************************************************************************** +// IsarCollectionGenerator +// ************************************************************************** + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types + +extension GetSentToAddressCollection on Isar { + IsarCollection get sentToAddress => this.collection(); +} + +const SentToAddressSchema = CollectionSchema( + name: r'SentToAddress', + id: 4845779153260162867, + properties: { + r'label': PropertySchema( + id: 0, + name: r'label', + type: IsarType.string, + ), + r'txid': PropertySchema( + id: 1, + name: r'txid', + type: IsarType.string, + ), + r'value': PropertySchema( + id: 2, + name: r'value', + type: IsarType.string, + ), + r'walletId': PropertySchema( + id: 3, + name: r'walletId', + type: IsarType.string, + ) + }, + estimateSize: _sentToAddressEstimateSize, + serialize: _sentToAddressSerialize, + deserialize: _sentToAddressDeserialize, + deserializeProp: _sentToAddressDeserializeProp, + idName: r'id', + indexes: { + r'walletId': IndexSchema( + id: -1783113319798776304, + name: r'walletId', + unique: false, + replace: false, + properties: [ + IndexPropertySchema( + name: r'walletId', + type: IndexType.hash, + caseSensitive: true, + ) + ], + ), + r'txid_walletId': IndexSchema( + id: -2771771174176035985, + name: r'txid_walletId', + unique: true, + replace: false, + properties: [ + IndexPropertySchema( + name: r'txid', + type: IndexType.hash, + caseSensitive: true, + ), + IndexPropertySchema( + name: r'walletId', + type: IndexType.hash, + caseSensitive: true, + ) + ], + ) + }, + links: {}, + embeddedSchemas: {}, + getId: _sentToAddressGetId, + getLinks: _sentToAddressGetLinks, + attach: _sentToAddressAttach, + version: '3.1.8', +); + +int _sentToAddressEstimateSize( + SentToAddress object, + List offsets, + Map> allOffsets, +) { + var bytesCount = offsets.last; + bytesCount += 3 + object.label.length * 3; + bytesCount += 3 + object.txid.length * 3; + bytesCount += 3 + object.value.length * 3; + bytesCount += 3 + object.walletId.length * 3; + return bytesCount; +} + +void _sentToAddressSerialize( + SentToAddress object, + IsarWriter writer, + List offsets, + Map> allOffsets, +) { + writer.writeString(offsets[0], object.label); + writer.writeString(offsets[1], object.txid); + writer.writeString(offsets[2], object.value); + writer.writeString(offsets[3], object.walletId); +} + +SentToAddress _sentToAddressDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = SentToAddress( + label: reader.readStringOrNull(offsets[0]) ?? "", + txid: reader.readString(offsets[1]), + value: reader.readString(offsets[2]), + walletId: reader.readString(offsets[3]), + ); + object.id = id; + return object; +} + +P _sentToAddressDeserializeProp

( + IsarReader reader, + int propertyId, + int offset, + Map> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readStringOrNull(offset) ?? "") as P; + case 1: + return (reader.readString(offset)) as P; + case 2: + return (reader.readString(offset)) as P; + case 3: + return (reader.readString(offset)) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +Id _sentToAddressGetId(SentToAddress object) { + return object.id; +} + +List> _sentToAddressGetLinks(SentToAddress object) { + return []; +} + +void _sentToAddressAttach( + IsarCollection col, Id id, SentToAddress object) { + object.id = id; +} + +extension SentToAddressByIndex on IsarCollection { + Future getByTxidWalletId(String txid, String walletId) { + return getByIndex(r'txid_walletId', [txid, walletId]); + } + + SentToAddress? getByTxidWalletIdSync(String txid, String walletId) { + return getByIndexSync(r'txid_walletId', [txid, walletId]); + } + + Future deleteByTxidWalletId(String txid, String walletId) { + return deleteByIndex(r'txid_walletId', [txid, walletId]); + } + + bool deleteByTxidWalletIdSync(String txid, String walletId) { + return deleteByIndexSync(r'txid_walletId', [txid, walletId]); + } + + Future> getAllByTxidWalletId( + List txidValues, List walletIdValues) { + final len = txidValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([txidValues[i], walletIdValues[i]]); + } + + return getAllByIndex(r'txid_walletId', values); + } + + List getAllByTxidWalletIdSync( + List txidValues, List walletIdValues) { + final len = txidValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([txidValues[i], walletIdValues[i]]); + } + + return getAllByIndexSync(r'txid_walletId', values); + } + + Future deleteAllByTxidWalletId( + List txidValues, List walletIdValues) { + final len = txidValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([txidValues[i], walletIdValues[i]]); + } + + return deleteAllByIndex(r'txid_walletId', values); + } + + int deleteAllByTxidWalletIdSync( + List txidValues, List walletIdValues) { + final len = txidValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([txidValues[i], walletIdValues[i]]); + } + + return deleteAllByIndexSync(r'txid_walletId', values); + } + + Future putByTxidWalletId(SentToAddress object) { + return putByIndex(r'txid_walletId', object); + } + + Id putByTxidWalletIdSync(SentToAddress object, {bool saveLinks = true}) { + return putByIndexSync(r'txid_walletId', object, saveLinks: saveLinks); + } + + Future> putAllByTxidWalletId(List objects) { + return putAllByIndex(r'txid_walletId', objects); + } + + List putAllByTxidWalletIdSync(List objects, + {bool saveLinks = true}) { + return putAllByIndexSync(r'txid_walletId', objects, saveLinks: saveLinks); + } +} + +extension SentToAddressQueryWhereSort + on QueryBuilder { + QueryBuilder anyId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension SentToAddressQueryWhere + on QueryBuilder { + QueryBuilder idEqualTo( + Id id) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: id, + upper: id, + )); + }); + } + + QueryBuilder idNotEqualTo( + Id id) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ) + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ); + } else { + return query + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ) + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ); + } + }); + } + + QueryBuilder idGreaterThan( + Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: include), + ); + }); + } + + QueryBuilder idLessThan( + Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: include), + ); + }); + } + + QueryBuilder idBetween( + Id lowerId, + Id upperId, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder walletIdEqualTo( + String walletId) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'walletId', + value: [walletId], + )); + }); + } + + QueryBuilder + walletIdNotEqualTo(String walletId) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [], + upper: [walletId], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [walletId], + includeLower: false, + upper: [], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [walletId], + includeLower: false, + upper: [], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [], + upper: [walletId], + includeUpper: false, + )); + } + }); + } + + QueryBuilder + txidEqualToAnyWalletId(String txid) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'txid_walletId', + value: [txid], + )); + }); + } + + QueryBuilder + txidNotEqualToAnyWalletId(String txid) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'txid_walletId', + lower: [], + upper: [txid], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'txid_walletId', + lower: [txid], + includeLower: false, + upper: [], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'txid_walletId', + lower: [txid], + includeLower: false, + upper: [], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'txid_walletId', + lower: [], + upper: [txid], + includeUpper: false, + )); + } + }); + } + + QueryBuilder + txidWalletIdEqualTo(String txid, String walletId) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'txid_walletId', + value: [txid, walletId], + )); + }); + } + + QueryBuilder + txidEqualToWalletIdNotEqualTo(String txid, String walletId) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'txid_walletId', + lower: [txid], + upper: [txid, walletId], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'txid_walletId', + lower: [txid, walletId], + includeLower: false, + upper: [txid], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'txid_walletId', + lower: [txid, walletId], + includeLower: false, + upper: [txid], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'txid_walletId', + lower: [txid], + upper: [txid, walletId], + includeUpper: false, + )); + } + }); + } +} + +extension SentToAddressQueryFilter + on QueryBuilder { + QueryBuilder idEqualTo( + Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'id', + value: value, + )); + }); + } + + QueryBuilder + idGreaterThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idLessThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idBetween( + Id lower, + Id upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder + labelEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'label', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + labelGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'label', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + labelLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'label', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + labelBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'label', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + labelStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'label', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + labelEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'label', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + labelContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'label', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + labelMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'label', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + labelIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'label', + value: '', + )); + }); + } + + QueryBuilder + labelIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'label', + value: '', + )); + }); + } + + QueryBuilder txidEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + txidGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + txidLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder txidBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'txid', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + txidStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + txidEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + txidContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder txidMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'txid', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + txidIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'txid', + value: '', + )); + }); + } + + QueryBuilder + txidIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'txid', + value: '', + )); + }); + } + + QueryBuilder + valueEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'value', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'value', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'value', + value: '', + )); + }); + } + + QueryBuilder + valueIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'value', + value: '', + )); + }); + } + + QueryBuilder + walletIdEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'walletId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'walletId', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'walletId', + value: '', + )); + }); + } + + QueryBuilder + walletIdIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'walletId', + value: '', + )); + }); + } +} + +extension SentToAddressQueryObject + on QueryBuilder {} + +extension SentToAddressQueryLinks + on QueryBuilder {} + +extension SentToAddressQuerySortBy + on QueryBuilder { + QueryBuilder sortByLabel() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'label', Sort.asc); + }); + } + + QueryBuilder sortByLabelDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'label', Sort.desc); + }); + } + + QueryBuilder sortByTxid() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.asc); + }); + } + + QueryBuilder sortByTxidDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.desc); + }); + } + + QueryBuilder sortByValue() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.asc); + }); + } + + QueryBuilder sortByValueDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.desc); + }); + } + + QueryBuilder sortByWalletId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.asc); + }); + } + + QueryBuilder + sortByWalletIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.desc); + }); + } +} + +extension SentToAddressQuerySortThenBy + on QueryBuilder { + QueryBuilder thenById() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.asc); + }); + } + + QueryBuilder thenByIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.desc); + }); + } + + QueryBuilder thenByLabel() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'label', Sort.asc); + }); + } + + QueryBuilder thenByLabelDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'label', Sort.desc); + }); + } + + QueryBuilder thenByTxid() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.asc); + }); + } + + QueryBuilder thenByTxidDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.desc); + }); + } + + QueryBuilder thenByValue() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.asc); + }); + } + + QueryBuilder thenByValueDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.desc); + }); + } + + QueryBuilder thenByWalletId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.asc); + }); + } + + QueryBuilder + thenByWalletIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.desc); + }); + } +} + +extension SentToAddressQueryWhereDistinct + on QueryBuilder { + QueryBuilder distinctByLabel( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'label', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByTxid( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'txid', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByValue( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'value', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByWalletId( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'walletId', caseSensitive: caseSensitive); + }); + } +} + +extension SentToAddressQueryProperty + on QueryBuilder { + QueryBuilder idProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'id'); + }); + } + + QueryBuilder labelProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'label'); + }); + } + + QueryBuilder txidProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'txid'); + }); + } + + QueryBuilder valueProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'value'); + }); + } + + QueryBuilder walletIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'walletId'); + }); + } +} diff --git a/lib/models/isar/models/transaction_note.g.dart b/lib/models/isar/models/transaction_note.g.dart index 152070328..67af8cdca 100644 --- a/lib/models/isar/models/transaction_note.g.dart +++ b/lib/models/isar/models/transaction_note.g.dart @@ -7,7 +7,7 @@ part of 'transaction_note.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetTransactionNoteCollection on Isar { IsarCollection get transactionNotes => this.collection(); @@ -76,7 +76,7 @@ const TransactionNoteSchema = CollectionSchema( getId: _transactionNoteGetId, getLinks: _transactionNoteGetLinks, attach: _transactionNoteAttach, - version: '3.0.5', + version: '3.1.8', ); int _transactionNoteEstimateSize( diff --git a/lib/models/isar/ordinal.g.dart b/lib/models/isar/ordinal.g.dart index 89c967cb0..5c23e11bb 100644 --- a/lib/models/isar/ordinal.g.dart +++ b/lib/models/isar/ordinal.g.dart @@ -7,7 +7,7 @@ part of 'ordinal.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetOrdinalCollection on Isar { IsarCollection get ordinals => this.collection(); @@ -83,7 +83,7 @@ const OrdinalSchema = CollectionSchema( getId: _ordinalGetId, getLinks: _ordinalGetLinks, attach: _ordinalAttach, - version: '3.0.5', + version: '3.1.8', ); int _ordinalEstimateSize( diff --git a/lib/models/isar/stack_theme.g.dart b/lib/models/isar/stack_theme.g.dart index 03c543ff0..c77d979f5 100644 --- a/lib/models/isar/stack_theme.g.dart +++ b/lib/models/isar/stack_theme.g.dart @@ -7,7 +7,7 @@ part of 'stack_theme.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetStackThemeCollection on Isar { IsarCollection get stackThemes => this.collection(); @@ -860,7 +860,7 @@ const StackThemeSchema = CollectionSchema( getId: _stackThemeGetId, getLinks: _stackThemeGetLinks, attach: _stackThemeAttach, - version: '3.0.5', + version: '3.1.8', ); int _stackThemeEstimateSize( @@ -18012,7 +18012,7 @@ extension StackThemeQueryProperty // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types const ThemeAssetsSchema = Schema( name: r'ThemeAssets', @@ -25833,7 +25833,7 @@ extension ThemeAssetsQueryObject on QueryBuilder {} // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types const ThemeAssetsV2Schema = Schema( name: r'ThemeAssetsV2', @@ -29441,7 +29441,7 @@ extension ThemeAssetsV2QueryObject on QueryBuilder {} // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types const ThemeAssetsV3Schema = Schema( name: r'ThemeAssetsV3', diff --git a/lib/models/keys/view_only_wallet_data.dart b/lib/models/keys/view_only_wallet_data.dart new file mode 100644 index 000000000..92c23b082 --- /dev/null +++ b/lib/models/keys/view_only_wallet_data.dart @@ -0,0 +1,164 @@ +import 'dart:convert'; + +import '../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart'; +import 'key_data_interface.dart'; + +// do not remove or change the order of these enum values +enum ViewOnlyWalletType { + cryptonote, + addressOnly, + xPub; +} + +sealed class ViewOnlyWalletData with KeyDataInterface { + @override + final String walletId; + + ViewOnlyWalletType get type; + + ViewOnlyWalletData({ + required this.walletId, + }); + + static ViewOnlyWalletData fromJsonEncodedString( + String jsonEncodedString, { + required String walletId, + }) { + final map = jsonDecode(jsonEncodedString) as Map; + final json = Map.from(map); + final type = ViewOnlyWalletType.values[json["type"] as int]; + + switch (type) { + case ViewOnlyWalletType.cryptonote: + return CryptonoteViewOnlyWalletData.fromJsonEncodedString( + jsonEncodedString, + walletId: walletId, + ); + + case ViewOnlyWalletType.addressOnly: + return AddressViewOnlyWalletData.fromJsonEncodedString( + jsonEncodedString, + walletId: walletId, + ); + + case ViewOnlyWalletType.xPub: + return ExtendedKeysViewOnlyWalletData.fromJsonEncodedString( + jsonEncodedString, + walletId: walletId, + ); + } + } + + String toJsonEncodedString(); +} + +class CryptonoteViewOnlyWalletData extends ViewOnlyWalletData { + @override + final type = ViewOnlyWalletType.cryptonote; + + final String address; + final String privateViewKey; + + CryptonoteViewOnlyWalletData({ + required super.walletId, + required this.address, + required this.privateViewKey, + }); + + static CryptonoteViewOnlyWalletData fromJsonEncodedString( + String jsonEncodedString, { + required String walletId, + }) { + final map = jsonDecode(jsonEncodedString) as Map; + final json = Map.from(map); + + return CryptonoteViewOnlyWalletData( + walletId: walletId, + address: json["address"] as String, + privateViewKey: json["privateViewKey"] as String, + ); + } + + @override + String toJsonEncodedString() => jsonEncode({ + "type": type.index, + "address": address, + "privateViewKey": privateViewKey, + }); +} + +class AddressViewOnlyWalletData extends ViewOnlyWalletData { + @override + final type = ViewOnlyWalletType.addressOnly; + + final String address; + + AddressViewOnlyWalletData({ + required super.walletId, + required this.address, + }); + + static AddressViewOnlyWalletData fromJsonEncodedString( + String jsonEncodedString, { + required String walletId, + }) { + final map = jsonDecode(jsonEncodedString) as Map; + final json = Map.from(map); + + return AddressViewOnlyWalletData( + walletId: walletId, + address: json["address"] as String, + ); + } + + @override + String toJsonEncodedString() => jsonEncode({ + "type": type.index, + "address": address, + }); +} + +class ExtendedKeysViewOnlyWalletData extends ViewOnlyWalletData { + @override + final type = ViewOnlyWalletType.xPub; + + final List xPubs; + + ExtendedKeysViewOnlyWalletData({ + required super.walletId, + required List xPubs, + }) : xPubs = List.unmodifiable(xPubs); + + static ExtendedKeysViewOnlyWalletData fromJsonEncodedString( + String jsonEncodedString, { + required String walletId, + }) { + final map = jsonDecode(jsonEncodedString) as Map; + final json = Map.from(map); + + return ExtendedKeysViewOnlyWalletData( + walletId: walletId, + xPubs: List>.from((json["xPubs"] as List)) + .map( + (e) => XPub( + path: e["path"] as String, + encoded: e["encoded"] as String, + ), + ) + .toList(growable: false), + ); + } + + @override + String toJsonEncodedString() => jsonEncode({ + "type": type.index, + "xPubs": [ + ...xPubs.map( + (e) => { + "path": e.path, + "encoded": e.encoded, + }, + ), + ], + }); +} diff --git a/lib/models/node_model.dart b/lib/models/node_model.dart index a07c9a2dd..e4d3f66b6 100644 --- a/lib/models/node_model.dart +++ b/lib/models/node_model.dart @@ -9,6 +9,7 @@ */ import 'package:hive/hive.dart'; + import '../utilities/default_nodes.dart'; import '../utilities/flutter_secure_storage_interface.dart'; @@ -38,6 +39,10 @@ class NodeModel { final bool isDown; // @HiveField(10) final bool? trusted; + // @HiveField(11) + final bool torEnabled; + // @HiveField(12) + final bool clearnetEnabled; NodeModel({ required this.host, @@ -49,6 +54,8 @@ class NodeModel { required this.coinName, required this.isFailover, required this.isDown, + required this.torEnabled, + required this.clearnetEnabled, this.loginName, this.trusted, }); @@ -64,6 +71,8 @@ class NodeModel { bool? isFailover, bool? isDown, bool? trusted, + bool? torEnabled, + bool? clearnetEnabled, }) { return NodeModel( host: host ?? this.host, @@ -77,6 +86,8 @@ class NodeModel { isFailover: isFailover ?? this.isFailover, isDown: isDown ?? this.isDown, trusted: trusted ?? this.trusted, + torEnabled: torEnabled ?? this.torEnabled, + clearnetEnabled: clearnetEnabled ?? this.clearnetEnabled, ); } @@ -98,6 +109,8 @@ class NodeModel { map['isFailover'] = isFailover; map['isDown'] = isDown; map['trusted'] = trusted; + map['torEnabled'] = torEnabled; + map['clearEnabled'] = clearnetEnabled; return map; } diff --git a/lib/models/type_adaptors/node_model.g.dart b/lib/models/type_adaptors/node_model.g.dart index 597468c56..32490fd50 100644 --- a/lib/models/type_adaptors/node_model.g.dart +++ b/lib/models/type_adaptors/node_model.g.dart @@ -28,13 +28,15 @@ class NodeModelAdapter extends TypeAdapter { isFailover: fields[8] as bool, isDown: fields[9] as bool, trusted: fields[10] as bool?, + torEnabled: fields[11] as bool? ?? true, + clearnetEnabled: fields[12] as bool? ?? true, ); } @override void write(BinaryWriter writer, NodeModel obj) { writer - ..writeByte(11) + ..writeByte(13) ..writeByte(0) ..write(obj.id) ..writeByte(1) @@ -56,7 +58,11 @@ class NodeModelAdapter extends TypeAdapter { ..writeByte(9) ..write(obj.isDown) ..writeByte(10) - ..write(obj.trusted); + ..write(obj.trusted) + ..writeByte(11) + ..write(obj.torEnabled) + ..writeByte(12) + ..write(obj.clearnetEnabled); } @override diff --git a/lib/networking/http.dart b/lib/networking/http.dart index a4f25c225..ae2a3b97b 100644 --- a/lib/networking/http.dart +++ b/lib/networking/http.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:socks5_proxy/socks_client.dart'; + import '../utilities/logger.dart'; // WIP wrapper layer @@ -118,6 +119,10 @@ class HTTP { onDone: () => completer.complete( Uint8List.fromList(bytes), ), + onError: (Object err, StackTrace s) => Logging.instance.log( + "Http wrapper layer listen: $err\n$s", + level: LogLevel.Error, + ), ); return completer.future; } diff --git a/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart b/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart index deb38a648..03d289237 100644 --- a/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart +++ b/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart @@ -10,7 +10,6 @@ import 'dart:async'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; @@ -69,6 +68,7 @@ class _AddWalletViewState extends ConsumerState { ...AppConfig.coins.where((e) => e.network == CryptoCurrencyNetwork.main), ]; final List coinEntities = []; + final List coinTestnetEntities = []; final List tokenEntities = []; final bool isDesktop = Util.isDesktop; @@ -130,16 +130,11 @@ class _AddWalletViewState extends ConsumerState { void initState() { _searchFieldController = TextEditingController(); _searchFocusNode = FocusNode(); - // _coinsTestnet.remove(Coin.firoTestNet); - - if (Util.isDesktop && !kDebugMode) { - _coins.removeWhere((e) => e is BitcoinFrost); - } coinEntities.addAll(_coins.map((e) => CoinEntity(e))); if (ref.read(prefsChangeNotifierProvider).showTestNetCoins) { - coinEntities.addAll(_coinsTestnet.map((e) => CoinEntity(e))); + coinTestnetEntities.addAll(_coinsTestnet.map((e) => CoinEntity(e))); } if (AppConfig.coins.whereType().isNotEmpty) { @@ -286,6 +281,14 @@ class _AddWalletViewState extends ConsumerState { initialState: ExpandableState.expanded, animationDurationMultiplier: 0.5, ), + if (coinTestnetEntities.isNotEmpty) + ExpandingSubListItem( + title: "Testnet", + entities: + filter(_searchTerm, coinTestnetEntities), + initialState: ExpandableState.expanded, + animationDurationMultiplier: 0.5, + ), if (tokenEntities.isNotEmpty) ExpandingSubListItem( title: "Tokens", @@ -419,6 +422,13 @@ class _AddWalletViewState extends ConsumerState { entities: filter(_searchTerm, coinEntities), initialState: ExpandableState.expanded, ), + if (coinTestnetEntities.isNotEmpty) + ExpandingSubListItem( + title: "Testnet", + entities: + filter(_searchTerm, coinTestnetEntities), + initialState: ExpandableState.expanded, + ), if (tokenEntities.isNotEmpty) ExpandingSubListItem( title: "Tokens", diff --git a/lib/pages/add_wallet_views/frost_ms/restore/restore_frost_ms_wallet_view.dart b/lib/pages/add_wallet_views/frost_ms/restore/restore_frost_ms_wallet_view.dart index 59a798a41..75c5cbc7d 100644 --- a/lib/pages/add_wallet_views/frost_ms/restore/restore_frost_ms_wallet_view.dart +++ b/lib/pages/add_wallet_views/frost_ms/restore/restore_frost_ms_wallet_view.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:barcode_scan2/barcode_scan2.dart'; import 'package:flutter/material.dart'; @@ -32,6 +33,7 @@ import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/desktop/desktop_app_bar.dart'; import '../../../../widgets/desktop/desktop_scaffold.dart'; import '../../../../widgets/desktop/primary_button.dart'; +import '../../../../widgets/desktop/qr_code_scanner_dialog.dart'; import '../../../../widgets/frost_mascot.dart'; import '../../../../widgets/icon_widgets/clipboard_icon.dart'; import '../../../../widgets/icon_widgets/qrcode_icon.dart'; @@ -207,6 +209,54 @@ class _RestoreFrostMsWalletViewState super.dispose(); } + Future scanQr() async { + try { + if (Platform.isAndroid || Platform.isIOS) { + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed( + const Duration(milliseconds: 75), + ); + } + + final qrResult = await BarcodeScanner.scan(); + + configFieldController.text = qrResult.rawContent; + + setState(() { + _configEmpty = configFieldController.text.isEmpty; + }); + } else { + // Platform.isLinux, Platform.isWindows, or Platform.isMacOS. + await showDialog( + context: context, + builder: (context) { + return QrCodeScannerDialog( + onQrCodeDetected: (qrCodeData) { + try { + // TODO [prio=low]: Validate QR code data. + configFieldController.text = qrCodeData; + + setState(() { + _configEmpty = configFieldController.text.isEmpty; + }); + } catch (e, s) { + Logging.instance.log("Error processing QR code data: $e\n$s", + level: LogLevel.Error); + } + }, + ); + }, + ); + } + } on PlatformException catch (e, s) { + Logging.instance.log( + "Failed to get camera permissions while trying to scan qr code: $e\n$s", + level: LogLevel.Warning, + ); + } + } + @override Widget build(BuildContext context) { return ConditionalParent( @@ -351,31 +401,7 @@ class _RestoreFrostMsWalletViewState semanticsLabel: "Scan QR Button. Opens Camera For Scanning QR Code.", key: const Key("frConfigScanQrButtonKey"), - onTap: () async { - try { - if (FocusScope.of(context).hasFocus) { - FocusScope.of(context).unfocus(); - await Future.delayed( - const Duration(milliseconds: 75), - ); - } - - final qrResult = await BarcodeScanner.scan(); - - configFieldController.text = - qrResult.rawContent; - - setState(() { - _configEmpty = - configFieldController.text.isEmpty; - }); - } on PlatformException catch (e, s) { - Logging.instance.log( - "Failed to get camera permissions while trying to scan qr code: $e\n$s", - level: LogLevel.Warning, - ); - } - }, + onTap: scanQr, child: const QrCodeIcon(), ), ], diff --git a/lib/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart b/lib/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart index 29742f251..f17ed06ff 100644 --- a/lib/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart +++ b/lib/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart @@ -25,6 +25,7 @@ import '../../../utilities/name_generator.dart'; import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; import '../../../wallets/crypto_currency/crypto_currency.dart'; +import '../../../wallets/crypto_currency/interfaces/view_only_option_currency_interface.dart'; import '../../../wallets/crypto_currency/intermediate/frost_currency.dart'; import '../../../wallets/isar/models/wallet_info.dart'; import '../../../widgets/background.dart'; @@ -104,7 +105,9 @@ class _NameYourWalletViewState extends ConsumerState { case AddWalletType.New: unawaited( Navigator.of(context).pushNamed( - coin.hasMnemonicPassphraseSupport + coin.possibleMnemonicLengths.length > 1 || + coin.hasMnemonicPassphraseSupport || + coin is ViewOnlyOptionCurrencyInterface ? NewWalletOptionsView.routeName : NewWalletRecoveryPhraseWarningView.routeName, arguments: Tuple2( diff --git a/lib/pages/add_wallet_views/new_wallet_options/new_wallet_options_view.dart b/lib/pages/add_wallet_views/new_wallet_options/new_wallet_options_view.dart index 1008fd5df..bfe41c3fe 100644 --- a/lib/pages/add_wallet_views/new_wallet_options/new_wallet_options_view.dart +++ b/lib/pages/add_wallet_views/new_wallet_options/new_wallet_options_view.dart @@ -12,9 +12,11 @@ import '../../../utilities/constants.dart'; import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; import '../../../wallets/crypto_currency/crypto_currency.dart'; +import '../../../wallets/crypto_currency/interfaces/view_only_option_currency_interface.dart'; import '../../../widgets/background.dart'; import '../../../widgets/conditional_parent.dart'; import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; +import '../../../widgets/custom_buttons/checkbox_text_button.dart'; import '../../../widgets/desktop/desktop_app_bar.dart'; import '../../../widgets/desktop/desktop_scaffold.dart'; import '../../../widgets/desktop/primary_button.dart'; @@ -25,8 +27,12 @@ import '../new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_wa import '../restore_wallet_view/restore_options_view/sub_widgets/mobile_mnemonic_length_selector.dart'; import '../restore_wallet_view/sub_widgets/mnemonic_word_count_select_sheet.dart'; -final pNewWalletOptions = - StateProvider<({String mnemonicPassphrase, int mnemonicWordsCount})?>( +final pNewWalletOptions = StateProvider< + ({ + String mnemonicPassphrase, + int mnemonicWordsCount, + bool convertToViewOnly, + })?>( (ref) => null, ); @@ -59,6 +65,8 @@ class _NewWalletOptionsViewState extends ConsumerState { bool hidePassword = true; NewWalletOptions _selectedOptions = NewWalletOptions.Default; + bool _convertToViewOnly = false; + @override void initState() { passwordController = TextEditingController(); @@ -210,7 +218,7 @@ class _NewWalletOptionsViewState extends ConsumerState { if (_selectedOptions == NewWalletOptions.Advanced) Column( children: [ - if (Util.isDesktop) + if (Util.isDesktop && lengths.length > 1) DropdownButtonHideUnderline( child: DropdownButton2( value: ref @@ -265,7 +273,7 @@ class _NewWalletOptionsViewState extends ConsumerState { ), ), ), - if (!Util.isDesktop) + if (!Util.isDesktop && lengths.length > 1) MobileMnemonicLengthSelector( chooseMnemonicLength: () { showModalBottomSheet( @@ -284,91 +292,109 @@ class _NewWalletOptionsViewState extends ConsumerState { ); }, ), - const SizedBox( - height: 24, - ), - RoundedWhiteContainer( - child: Center( - child: Text( - "You may add a BIP39 passphrase. This is optional. " - "You will need BOTH your seed and your passphrase to recover the wallet.", - style: Util.isDesktop - ? STextStyles.desktopTextExtraSmall(context) - .copyWith( - color: Theme.of(context) - .extension()! - .textSubtitle1, - ) - : STextStyles.itemSubtitle(context), + if (widget.coin.hasMnemonicPassphraseSupport) + const SizedBox( + height: 24, + ), + if (widget.coin.hasMnemonicPassphraseSupport) + RoundedWhiteContainer( + child: Center( + child: Text( + "You may add a BIP39 passphrase. This is optional. " + "You will need BOTH your seed and your passphrase to recover the wallet.", + style: Util.isDesktop + ? STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle1, + ) + : STextStyles.itemSubtitle(context), + ), ), ), - ), - const SizedBox( - height: 8, - ), - ClipRRect( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, + if (widget.coin.hasMnemonicPassphraseSupport) + const SizedBox( + height: 8, ), - child: TextField( - key: const Key("mnemonicPassphraseFieldKey1"), - focusNode: passwordFocusNode, - controller: passwordController, - style: Util.isDesktop - ? STextStyles.desktopTextMedium(context).copyWith( - height: 2, - ) - : STextStyles.field(context), - obscureText: hidePassword, - enableSuggestions: false, - autocorrect: false, - decoration: standardInputDecoration( - "BIP39 passphrase", - passwordFocusNode, - context, - ).copyWith( - suffixIcon: UnconstrainedBox( - child: ConditionalParent( - condition: Util.isDesktop, - builder: (child) => SizedBox( - height: 70, - child: child, - ), - child: Row( - children: [ - SizedBox( - width: Util.isDesktop ? 24 : 16, - ), - GestureDetector( - key: const Key( - "mnemonicPassphraseFieldShowPasswordButtonKey", - ), - onTap: () async { - setState(() { - hidePassword = !hidePassword; - }); - }, - child: SvgPicture.asset( - hidePassword - ? Assets.svg.eye - : Assets.svg.eyeSlash, - color: Theme.of(context) - .extension()! - .textDark3, + if (widget.coin.hasMnemonicPassphraseSupport) + ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + key: const Key("mnemonicPassphraseFieldKey1"), + focusNode: passwordFocusNode, + controller: passwordController, + style: Util.isDesktop + ? STextStyles.desktopTextMedium(context).copyWith( + height: 2, + ) + : STextStyles.field(context), + obscureText: hidePassword, + enableSuggestions: false, + autocorrect: false, + decoration: standardInputDecoration( + "BIP39 passphrase", + passwordFocusNode, + context, + ).copyWith( + suffixIcon: UnconstrainedBox( + child: ConditionalParent( + condition: Util.isDesktop, + builder: (child) => SizedBox( + height: 70, + child: child, + ), + child: Row( + children: [ + SizedBox( width: Util.isDesktop ? 24 : 16, - height: Util.isDesktop ? 24 : 16, ), - ), - const SizedBox( - width: 12, - ), - ], + GestureDetector( + key: const Key( + "mnemonicPassphraseFieldShowPasswordButtonKey", + ), + onTap: () async { + setState(() { + hidePassword = !hidePassword; + }); + }, + child: SvgPicture.asset( + hidePassword + ? Assets.svg.eye + : Assets.svg.eyeSlash, + color: Theme.of(context) + .extension()! + .textDark3, + width: Util.isDesktop ? 24 : 16, + height: Util.isDesktop ? 24 : 16, + ), + ), + const SizedBox( + width: 12, + ), + ], + ), ), ), ), ), ), - ), + if (widget.coin is ViewOnlyOptionCurrencyInterface) + const SizedBox( + height: 24, + ), + if (widget.coin is ViewOnlyOptionCurrencyInterface) + CheckboxTextButton( + label: "Convert to view only wallet. " + "You will only be shown the seed phrase once. " + "Save it somewhere. " + "If you lose it you will lose access to any funds in this wallet.", + onChanged: (value) { + _convertToViewOnly = value; + }, + ), ], ), if (!Util.isDesktop) const Spacer(), @@ -383,6 +409,7 @@ class _NewWalletOptionsViewState extends ConsumerState { mnemonicWordsCount: ref.read(mnemonicWordCountStateProvider.state).state, mnemonicPassphrase: passwordController.text, + convertToViewOnly: _convertToViewOnly, ); } else { ref.read(pNewWalletOptions.notifier).state = null; diff --git a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart index 42836e117..73acccb9d 100644 --- a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart +++ b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart @@ -560,6 +560,8 @@ class _NewWalletRecoveryPhraseWarningViewState wordCount = info .coin.defaultSeedPhraseLength; + // TODO: Refactor these to generate each coin in their respective classes + // This code should not be in a random view page file if (coin is Monero || coin is Wownero) { // currently a special case due to the @@ -580,7 +582,12 @@ class _NewWalletRecoveryPhraseWarningViewState ) .state! .mnemonicPassphrase; - } else {} + } else { + // this may not be epiccash specific? + if (coin is Epiccash) { + mnemonicPassphrase = ""; + } + } wordCount = ref .read( diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart index 0b7fcc37b..384cacb0c 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart @@ -23,6 +23,8 @@ import '../../../../utilities/format.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../utilities/util.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; +import '../../../../wallets/crypto_currency/interfaces/view_only_option_currency_interface.dart'; +import '../../../../wallets/crypto_currency/intermediate/cryptonote_currency.dart'; import '../../../../widgets/conditional_parent.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/custom_buttons/checkbox_text_button.dart'; @@ -32,7 +34,9 @@ import '../../../../widgets/desktop/desktop_scaffold.dart'; import '../../../../widgets/expandable.dart'; import '../../../../widgets/rounded_white_container.dart'; import '../../../../widgets/stack_text_field.dart'; +import '../../../../widgets/toggle.dart'; import '../../create_or_restore_wallet_view/sub_widgets/coin_image.dart'; +import '../restore_view_only_wallet_view.dart'; import '../restore_wallet_view.dart'; import '../sub_widgets/mnemonic_word_count_select_sheet.dart'; import 'sub_widgets/mobile_mnemonic_length_selector.dart'; @@ -67,9 +71,8 @@ class _RestoreOptionsViewState extends ConsumerState { late final TextEditingController passwordController; final bool _nextEnabled = true; - DateTime _restoreFromDate = DateTime.fromMillisecondsSinceEpoch(0); + DateTime? _restoreFromDate; bool hidePassword = true; - bool _expandedAdavnced = false; bool get supportsMnemonicPassphrase => coin.hasMnemonicPassphraseSupport; @@ -99,27 +102,46 @@ class _RestoreOptionsViewState extends ConsumerState { super.dispose(); } + bool _nextLock = false; Future nextPressed() async { - if (!isDesktop) { - // hide keyboard if has focus - if (FocusScope.of(context).hasFocus) { - FocusScope.of(context).unfocus(); - await Future.delayed(const Duration(milliseconds: 75)); + if (_nextLock) return; + _nextLock = true; + try { + if (!isDesktop) { + // hide keyboard if has focus + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed(const Duration(milliseconds: 75)); + } } - } - if (mounted) { - await Navigator.of(context).pushNamed( - RestoreWalletView.routeName, - arguments: Tuple6( - walletName, - coin, - ref.read(mnemonicWordCountStateProvider.state).state, - _restoreFromDate, - passwordController.text, - enableLelantusScanning, - ), - ); + if (mounted) { + if (!_showViewOnlyOption) { + await Navigator.of(context).pushNamed( + RestoreWalletView.routeName, + arguments: Tuple6( + walletName, + coin, + ref.read(mnemonicWordCountStateProvider.state).state, + _restoreFromDate, + passwordController.text, + enableLelantusScanning, + ), + ); + } else { + await Navigator.of(context).pushNamed( + RestoreViewOnlyWalletView.routeName, + arguments: ( + walletName: walletName, + coin: coin, + restoreFromDate: _restoreFromDate, + enableLelantusScanning: enableLelantusScanning, + ), + ); + } + } + } finally { + _nextLock = false; } } @@ -164,17 +186,12 @@ class _RestoreOptionsViewState extends ConsumerState { ); } + bool _showViewOnlyOption = false; + @override Widget build(BuildContext context) { debugPrint("BUILD: $runtimeType with ${coin.identifier} $walletName"); - final lengths = coin.possibleMnemonicLengths; - - final isMoneroAnd25 = coin is Monero && - ref.watch(mnemonicWordCountStateProvider.state).state == 25; - final isWowneroAnd25 = coin is Wownero && - ref.watch(mnemonicWordCountStateProvider.state).state == 25; - return MasterScaffold( isDesktop: isDesktop, appBar: isDesktop @@ -227,288 +244,57 @@ class _RestoreOptionsViewState extends ConsumerState { SizedBox( height: isDesktop ? 40 : 24, ), - if (isMoneroAnd25 || coin is Epiccash || isWowneroAnd25) - Text( - "Choose start date", - style: isDesktop - ? STextStyles.desktopTextExtraSmall(context).copyWith( - color: Theme.of(context) - .extension()! - .textDark3, - ) - : STextStyles.smallMed12(context), - textAlign: TextAlign.left, - ), - if (isMoneroAnd25 || coin is Epiccash || isWowneroAnd25) + if (coin is ViewOnlyOptionCurrencyInterface) SizedBox( - height: isDesktop ? 16 : 8, - ), - if (isMoneroAnd25 || coin is Epiccash || isWowneroAnd25) - if (!isDesktop) - RestoreFromDatePicker( - onTap: chooseDate, - controller: _dateController, - ), - if (isMoneroAnd25 || coin is Epiccash || isWowneroAnd25) - if (isDesktop) - // TODO desktop date picker - RestoreFromDatePicker( - onTap: chooseDesktopDate, - controller: _dateController, - ), - if (isMoneroAnd25 || coin is Epiccash || isWowneroAnd25) - const SizedBox( - height: 8, - ), - if (isMoneroAnd25 || coin is Epiccash || isWowneroAnd25) - RoundedWhiteContainer( - child: Center( - child: Text( - "Choose the date you made the wallet (approximate is fine)", - style: isDesktop - ? STextStyles.desktopTextExtraSmall(context).copyWith( - color: Theme.of(context) - .extension()! - .textSubtitle1, - ) - : STextStyles.smallMed12(context).copyWith( - fontSize: 10, - ), - ), - ), - ), - if (isMoneroAnd25 || coin is Epiccash || isWowneroAnd25) - SizedBox( - height: isDesktop ? 24 : 16, - ), - Text( - "Choose recovery phrase length", - style: isDesktop - ? STextStyles.desktopTextExtraSmall(context).copyWith( - color: Theme.of(context) - .extension()! - .textDark3, - ) - : STextStyles.smallMed12(context), - textAlign: TextAlign.left, - ), - SizedBox( - height: isDesktop ? 16 : 8, - ), - if (isDesktop) - DropdownButtonHideUnderline( - child: DropdownButton2( - value: - ref.watch(mnemonicWordCountStateProvider.state).state, - items: [ - ...lengths.map( - (e) => DropdownMenuItem( - value: e, - child: Text( - "$e words", - style: STextStyles.desktopTextMedium(context), - ), - ), - ), - ], - onChanged: (value) { - if (value is int) { - ref.read(mnemonicWordCountStateProvider.state).state = - value; - } + height: isDesktop ? 56 : 48, + width: isDesktop ? 490 : null, + child: Toggle( + key: UniqueKey(), + onText: "Seed", + offText: "View Only", + onColor: + Theme.of(context).extension()!.popupBG, + offColor: Theme.of(context) + .extension()! + .textFieldDefaultBG, + isOn: _showViewOnlyOption, + onValueChanged: (value) { + setState(() { + _showViewOnlyOption = value; + }); }, - isExpanded: true, - iconStyleData: IconStyleData( - icon: SvgPicture.asset( - Assets.svg.chevronDown, - width: 12, - height: 6, - color: Theme.of(context) - .extension()! - .textFieldActiveSearchIconRight, - ), - ), - dropdownStyleData: DropdownStyleData( - offset: const Offset(0, -10), - elevation: 0, - decoration: BoxDecoration( - color: Theme.of(context) - .extension()! - .textFieldDefaultBG, - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - ), - ), - menuItemStyleData: const MenuItemStyleData( - padding: EdgeInsets.symmetric( - horizontal: 16, - vertical: 8, + decoration: BoxDecoration( + color: Colors.transparent, + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, ), ), ), ), - if (!isDesktop) - MobileMnemonicLengthSelector( - chooseMnemonicLength: chooseMnemonicLength, - ), - if (supportsMnemonicPassphrase) + if (coin is ViewOnlyOptionCurrencyInterface) SizedBox( - height: isDesktop ? 24 : 16, + height: isDesktop ? 40 : 24, ), - if (supportsMnemonicPassphrase) - Expandable( - onExpandChanged: (state) { - setState(() { - _expandedAdavnced = state == ExpandableState.expanded; - }); - }, - header: Container( - color: Colors.transparent, - child: Padding( - padding: const EdgeInsets.only( - top: 8.0, - bottom: 8.0, - right: 10, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Advanced", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of(context) - .extension()! - .textDark3, - ) - : STextStyles.smallMed12(context), - textAlign: TextAlign.left, - ), - SvgPicture.asset( - _expandedAdavnced - ? Assets.svg.chevronUp - : Assets.svg.chevronDown, - width: 12, - height: 6, - color: Theme.of(context) - .extension()! - .textFieldActiveSearchIconRight, - ), - ], - ), + _showViewOnlyOption + ? ViewOnlyRestoreOption( + coin: coin, + dateController: _dateController, + dateChooserFunction: + isDesktop ? chooseDesktopDate : chooseDate, + ) + : SeedRestoreOption( + coin: coin, + dateController: _dateController, + pwController: passwordController, + pwFocusNode: passwordFocusNode, + supportsMnemonicPassphrase: supportsMnemonicPassphrase, + dateChooserFunction: + isDesktop ? chooseDesktopDate : chooseDate, + chooseMnemonicLength: chooseMnemonicLength, + lelScanChanged: (value) { + enableLelantusScanning = value; + }, ), - ), - body: Container( - color: Colors.transparent, - child: Column( - children: [ - if (coin is Firo) - CheckboxTextButton( - label: "Scan for Lelantus transactions", - onChanged: (newValue) { - setState(() { - enableLelantusScanning = newValue ?? true; - }); - }, - ), - if (coin is Firo) - const SizedBox( - height: 8, - ), - ClipRRect( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - child: TextField( - key: const Key("mnemonicPassphraseFieldKey1"), - focusNode: passwordFocusNode, - controller: passwordController, - style: isDesktop - ? STextStyles.desktopTextMedium(context) - .copyWith( - height: 2, - ) - : STextStyles.field(context), - obscureText: hidePassword, - enableSuggestions: false, - autocorrect: false, - decoration: standardInputDecoration( - "BIP39 passphrase", - passwordFocusNode, - context, - ).copyWith( - suffixIcon: UnconstrainedBox( - child: ConditionalParent( - condition: isDesktop, - builder: (child) => SizedBox( - height: 70, - child: child, - ), - child: Row( - children: [ - SizedBox( - width: isDesktop ? 24 : 16, - ), - GestureDetector( - key: const Key( - "mnemonicPassphraseFieldShowPasswordButtonKey", - ), - onTap: () async { - setState(() { - hidePassword = !hidePassword; - }); - }, - child: SvgPicture.asset( - hidePassword - ? Assets.svg.eye - : Assets.svg.eyeSlash, - color: Theme.of(context) - .extension()! - .textDark3, - width: isDesktop ? 24 : 16, - height: isDesktop ? 24 : 16, - ), - ), - const SizedBox( - width: 12, - ), - ], - ), - ), - ), - ), - ), - ), - const SizedBox( - height: 8, - ), - RoundedWhiteContainer( - child: Center( - child: Text( - "If the recovery phrase you are about to restore " - "was created with an optional BIP39 passphrase " - "you can enter it here.", - style: isDesktop - ? STextStyles.desktopTextExtraSmall(context) - .copyWith( - color: Theme.of(context) - .extension()! - .textSubtitle1, - ) - : STextStyles.itemSubtitle(context), - ), - ), - ), - const SizedBox( - height: 16, - ), - ], - ), - ), - ), if (!isDesktop) const Spacer( flex: 3, @@ -532,3 +318,394 @@ class _RestoreOptionsViewState extends ConsumerState { ); } } + +class SeedRestoreOption extends ConsumerStatefulWidget { + const SeedRestoreOption({ + super.key, + required this.coin, + required this.dateController, + required this.pwController, + required this.pwFocusNode, + required this.supportsMnemonicPassphrase, + required this.dateChooserFunction, + required this.chooseMnemonicLength, + required this.lelScanChanged, + }); + + final CryptoCurrency coin; + final TextEditingController dateController; + final TextEditingController pwController; + final FocusNode pwFocusNode; + final bool supportsMnemonicPassphrase; + + final Future Function() dateChooserFunction; + final Future Function() chooseMnemonicLength; + final void Function(bool) lelScanChanged; + + @override + ConsumerState createState() => _SeedRestoreOptionState(); +} + +class _SeedRestoreOptionState extends ConsumerState { + bool _hidePassword = true; + bool _expandedAdvanced = false; + bool _enableLelantusScanning = false; + + @override + Widget build(BuildContext context) { + final lengths = widget.coin.possibleMnemonicLengths; + + final isMoneroAnd25 = widget.coin is Monero && + ref.watch(mnemonicWordCountStateProvider.state).state == 25; + final isWowneroAnd25 = widget.coin is Wownero && + ref.watch(mnemonicWordCountStateProvider.state).state == 25; + + return Column( + children: [ + if (isMoneroAnd25 || widget.coin is Epiccash || isWowneroAnd25) + Text( + "Choose start date", + style: Util.isDesktop + ? STextStyles.desktopTextExtraSmall(context).copyWith( + color: + Theme.of(context).extension()!.textDark3, + ) + : STextStyles.smallMed12(context), + textAlign: TextAlign.left, + ), + if (isMoneroAnd25 || widget.coin is Epiccash || isWowneroAnd25) + SizedBox( + height: Util.isDesktop ? 16 : 8, + ), + if (isMoneroAnd25 || widget.coin is Epiccash || isWowneroAnd25) + RestoreFromDatePicker( + onTap: widget.dateChooserFunction, + controller: widget.dateController, + ), + if (isMoneroAnd25 || widget.coin is Epiccash || isWowneroAnd25) + const SizedBox( + height: 8, + ), + if (isMoneroAnd25 || widget.coin is Epiccash || isWowneroAnd25) + RoundedWhiteContainer( + child: Center( + child: Text( + "Choose the date you made the wallet (approximate is fine)", + style: Util.isDesktop + ? STextStyles.desktopTextExtraSmall(context).copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle1, + ) + : STextStyles.smallMed12(context).copyWith( + fontSize: 10, + ), + ), + ), + ), + if (isMoneroAnd25 || widget.coin is Epiccash || isWowneroAnd25) + SizedBox( + height: Util.isDesktop ? 24 : 16, + ), + Text( + "Choose recovery phrase length", + style: Util.isDesktop + ? STextStyles.desktopTextExtraSmall(context).copyWith( + color: Theme.of(context).extension()!.textDark3, + ) + : STextStyles.smallMed12(context), + textAlign: TextAlign.left, + ), + SizedBox( + height: Util.isDesktop ? 16 : 8, + ), + if (Util.isDesktop) + DropdownButtonHideUnderline( + child: DropdownButton2( + value: ref.watch(mnemonicWordCountStateProvider.state).state, + items: [ + ...lengths.map( + (e) => DropdownMenuItem( + value: e, + child: Text( + "$e words", + style: STextStyles.desktopTextMedium(context), + ), + ), + ), + ], + onChanged: (value) { + if (value is int) { + ref.read(mnemonicWordCountStateProvider.state).state = value; + } + }, + isExpanded: true, + iconStyleData: IconStyleData( + icon: SvgPicture.asset( + Assets.svg.chevronDown, + width: 12, + height: 6, + color: Theme.of(context) + .extension()! + .textFieldActiveSearchIconRight, + ), + ), + dropdownStyleData: DropdownStyleData( + offset: const Offset(0, -10), + elevation: 0, + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .textFieldDefaultBG, + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + ), + menuItemStyleData: const MenuItemStyleData( + padding: EdgeInsets.symmetric( + horizontal: 16, + vertical: 8, + ), + ), + ), + ), + if (!Util.isDesktop) + MobileMnemonicLengthSelector( + chooseMnemonicLength: widget.chooseMnemonicLength, + ), + if (widget.supportsMnemonicPassphrase) + SizedBox( + height: Util.isDesktop ? 24 : 16, + ), + if (widget.supportsMnemonicPassphrase) + Expandable( + onExpandChanged: (state) { + setState(() { + _expandedAdvanced = state == ExpandableState.expanded; + }); + }, + header: Container( + color: Colors.transparent, + child: Padding( + padding: const EdgeInsets.only( + top: 8.0, + bottom: 8.0, + right: 10, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Advanced", + style: Util.isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension()! + .textDark3, + ) + : STextStyles.smallMed12(context), + textAlign: TextAlign.left, + ), + SvgPicture.asset( + _expandedAdvanced + ? Assets.svg.chevronUp + : Assets.svg.chevronDown, + width: 12, + height: 6, + color: Theme.of(context) + .extension()! + .textFieldActiveSearchIconRight, + ), + ], + ), + ), + ), + body: Container( + color: Colors.transparent, + child: Column( + children: [ + if (widget.coin is Firo) + CheckboxTextButton( + label: "Scan for Lelantus transactions", + onChanged: (newValue) { + setState(() { + _enableLelantusScanning = newValue ?? true; + }); + + widget.lelScanChanged(_enableLelantusScanning); + }, + ), + if (widget.coin is Firo) + const SizedBox( + height: 8, + ), + ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + key: const Key("mnemonicPassphraseFieldKey1"), + focusNode: widget.pwFocusNode, + controller: widget.pwController, + style: Util.isDesktop + ? STextStyles.desktopTextMedium(context).copyWith( + height: 2, + ) + : STextStyles.field(context), + obscureText: _hidePassword, + enableSuggestions: false, + autocorrect: false, + decoration: standardInputDecoration( + "BIP39 passphrase", + widget.pwFocusNode, + context, + ).copyWith( + suffixIcon: UnconstrainedBox( + child: ConditionalParent( + condition: Util.isDesktop, + builder: (child) => SizedBox( + height: 70, + child: child, + ), + child: Row( + children: [ + SizedBox( + width: Util.isDesktop ? 24 : 16, + ), + GestureDetector( + key: const Key( + "mnemonicPassphraseFieldShowPasswordButtonKey", + ), + onTap: () async { + setState(() { + _hidePassword = !_hidePassword; + }); + }, + child: SvgPicture.asset( + _hidePassword + ? Assets.svg.eye + : Assets.svg.eyeSlash, + color: Theme.of(context) + .extension()! + .textDark3, + width: Util.isDesktop ? 24 : 16, + height: Util.isDesktop ? 24 : 16, + ), + ), + const SizedBox( + width: 12, + ), + ], + ), + ), + ), + ), + ), + ), + const SizedBox( + height: 8, + ), + RoundedWhiteContainer( + child: Center( + child: Text( + "If the recovery phrase you are about to restore " + "was created with an optional BIP39 passphrase " + "you can enter it here.", + style: Util.isDesktop + ? STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle1, + ) + : STextStyles.itemSubtitle(context), + ), + ), + ), + const SizedBox( + height: 16, + ), + ], + ), + ), + ), + ], + ); + } +} + +class ViewOnlyRestoreOption extends StatefulWidget { + const ViewOnlyRestoreOption({ + super.key, + required this.coin, + required this.dateController, + required this.dateChooserFunction, + }); + + final CryptoCurrency coin; + final TextEditingController dateController; + + final Future Function() dateChooserFunction; + + @override + State createState() => _ViewOnlyRestoreOptionState(); +} + +class _ViewOnlyRestoreOptionState extends State { + @override + Widget build(BuildContext context) { + final showDateOption = widget.coin is CryptonoteCurrency; + return Column( + children: [ + if (showDateOption) + Text( + "Choose start date", + style: Util.isDesktop + ? STextStyles.desktopTextExtraSmall(context).copyWith( + color: + Theme.of(context).extension()!.textDark3, + ) + : STextStyles.smallMed12(context), + textAlign: TextAlign.left, + ), + if (showDateOption) + SizedBox( + height: Util.isDesktop ? 16 : 8, + ), + if (showDateOption) + RestoreFromDatePicker( + onTap: widget.dateChooserFunction, + controller: widget.dateController, + ), + if (showDateOption) + const SizedBox( + height: 8, + ), + if (showDateOption) + RoundedWhiteContainer( + child: Center( + child: Text( + "Choose the date you made the wallet (approximate is fine)", + style: Util.isDesktop + ? STextStyles.desktopTextExtraSmall(context).copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle1, + ) + : STextStyles.smallMed12(context).copyWith( + fontSize: 10, + ), + ), + ), + ), + if (showDateOption) + SizedBox( + height: Util.isDesktop ? 24 : 16, + ), + ], + ); + } +} diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_view_only_wallet_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_view_only_wallet_view.dart new file mode 100644 index 000000000..3a7131762 --- /dev/null +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_view_only_wallet_view.dart @@ -0,0 +1,592 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:cs_monero/src/deprecated/get_height_by_date.dart' + as cs_monero_deprecated; +import 'package:dropdown_button2/dropdown_button2.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; + +import '../../../models/keys/view_only_wallet_data.dart'; +import '../../../pages_desktop_specific/desktop_home_view.dart'; +import '../../../pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart'; +import '../../../providers/db/main_db_provider.dart'; +import '../../../providers/global/secure_store_provider.dart'; +import '../../../providers/providers.dart'; +import '../../../themes/stack_colors.dart'; +import '../../../utilities/assets.dart'; +import '../../../utilities/barcode_scanner_interface.dart'; +import '../../../utilities/clipboard_interface.dart'; +import '../../../utilities/constants.dart'; +import '../../../utilities/text_styles.dart'; +import '../../../utilities/util.dart'; +import '../../../wallets/crypto_currency/crypto_currency.dart'; +import '../../../wallets/crypto_currency/interfaces/electrumx_currency_interface.dart'; +import '../../../wallets/crypto_currency/intermediate/bip39_hd_currency.dart'; +import '../../../wallets/crypto_currency/intermediate/cryptonote_currency.dart'; +import '../../../wallets/isar/models/wallet_info.dart'; +import '../../../wallets/wallet/impl/epiccash_wallet.dart'; +import '../../../wallets/wallet/impl/monero_wallet.dart'; +import '../../../wallets/wallet/impl/wownero_wallet.dart'; +import '../../../wallets/wallet/wallet.dart'; +import '../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart'; +import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; +import '../../../widgets/desktop/desktop_app_bar.dart'; +import '../../../widgets/desktop/desktop_scaffold.dart'; +import '../../../widgets/desktop/primary_button.dart'; +import '../../../widgets/stack_text_field.dart'; +import '../../../widgets/toggle.dart'; +import '../../home_view/home_view.dart'; +import 'confirm_recovery_dialog.dart'; +import 'sub_widgets/restore_failed_dialog.dart'; +import 'sub_widgets/restore_succeeded_dialog.dart'; +import 'sub_widgets/restoring_dialog.dart'; + +class RestoreViewOnlyWalletView extends ConsumerStatefulWidget { + const RestoreViewOnlyWalletView({ + super.key, + required this.walletName, + required this.coin, + required this.restoreFromDate, + this.enableLelantusScanning = false, + this.barcodeScanner = const BarcodeScannerWrapper(), + this.clipboard = const ClipboardWrapper(), + }); + + static const routeName = "/restoreViewOnlyWallet"; + + final String walletName; + final CryptoCurrency coin; + final DateTime? restoreFromDate; + final bool enableLelantusScanning; + final BarcodeScannerInterface barcodeScanner; + final ClipboardInterface clipboard; + + @override + ConsumerState createState() => + _RestoreViewOnlyWalletViewState(); +} + +class _RestoreViewOnlyWalletViewState + extends ConsumerState { + late final TextEditingController addressController; + late final TextEditingController viewKeyController; + + late String _currentDropDownValue; + + bool _enableRestoreButton = false; + bool _addressOnly = false; + + bool _buttonLock = false; + + Future _requestRestore() async { + if (_buttonLock) return; + _buttonLock = true; + + try { + if (!Util.isDesktop) { + // wait for keyboard to disappear + FocusScope.of(context).unfocus(); + await Future.delayed( + const Duration(milliseconds: 100), + ); + } + + if (mounted) { + await showDialog( + context: context, + useSafeArea: false, + barrierDismissible: true, + builder: (context) { + return ConfirmRecoveryDialog( + onConfirm: _attemptRestore, + ); + }, + ); + } + } finally { + _buttonLock = false; + } + } + + Future _attemptRestore() async { + int height = 0; + final Map otherDataJson = { + WalletInfoKeys.isViewOnlyKey: true, + }; + + final ViewOnlyWalletType viewOnlyWalletType; + if (widget.coin is Bip39HDCurrency) { + if (widget.coin is Firo) { + otherDataJson.addAll( + { + WalletInfoKeys.lelantusCoinIsarRescanRequired: false, + WalletInfoKeys.enableLelantusScanning: + widget.enableLelantusScanning, + }, + ); + } + viewOnlyWalletType = _addressOnly + ? ViewOnlyWalletType.addressOnly + : ViewOnlyWalletType.xPub; + } else if (widget.coin is CryptonoteCurrency) { + if (widget.restoreFromDate != null) { + if (widget.coin is Monero) { + height = cs_monero_deprecated.getMoneroHeightByDate( + date: widget.restoreFromDate!, + ); + } + if (widget.coin is Wownero) { + height = cs_monero_deprecated.getWowneroHeightByDate( + date: widget.restoreFromDate!, + ); + } + if (height < 0) height = 0; + } + + viewOnlyWalletType = ViewOnlyWalletType.cryptonote; + } else { + throw Exception( + "Unsupported view only wallet currency type found: ${widget.coin.runtimeType}", + ); + } + otherDataJson[WalletInfoKeys.viewOnlyTypeIndexKey] = + viewOnlyWalletType.index; + + if (!Platform.isLinux && !Util.isDesktop) await WakelockPlus.enable(); + + try { + final info = WalletInfo.createNew( + coin: widget.coin, + name: widget.walletName, + restoreHeight: height, + otherDataJsonString: jsonEncode(otherDataJson), + ); + + bool isRestoring = true; + // show restoring in progress + + if (mounted) { + unawaited( + showDialog( + context: context, + useSafeArea: false, + barrierDismissible: false, + builder: (context) { + return RestoringDialog( + onCancel: () async { + isRestoring = false; + + await ref.read(pWallets).deleteWallet( + info, + ref.read(secureStoreProvider), + ); + }, + ); + }, + ), + ); + } + + final ViewOnlyWalletData viewOnlyData; + switch (viewOnlyWalletType) { + case ViewOnlyWalletType.cryptonote: + if (addressController.text.isEmpty || + viewKeyController.text.isEmpty) { + throw Exception("Missing address and/or private view key fields"); + } + viewOnlyData = CryptonoteViewOnlyWalletData( + walletId: info.walletId, + address: addressController.text, + privateViewKey: viewKeyController.text, + ); + break; + + case ViewOnlyWalletType.addressOnly: + if (addressController.text.isEmpty) { + throw Exception("Address is empty"); + } + viewOnlyData = AddressViewOnlyWalletData( + walletId: info.walletId, + address: addressController.text, + ); + break; + + case ViewOnlyWalletType.xPub: + viewOnlyData = ExtendedKeysViewOnlyWalletData( + walletId: info.walletId, + xPubs: [ + XPub( + path: _currentDropDownValue, + encoded: viewKeyController.text, + ), + ], + ); + break; + } + + var node = ref + .read(nodeServiceChangeNotifierProvider) + .getPrimaryNodeFor(currency: widget.coin); + + if (node == null) { + node = widget.coin.defaultNode; + await ref.read(nodeServiceChangeNotifierProvider).setPrimaryNodeFor( + coin: widget.coin, + node: node, + ); + } + + try { + final wallet = await Wallet.create( + walletInfo: info, + mainDB: ref.read(mainDBProvider), + secureStorageInterface: ref.read(secureStoreProvider), + nodeService: ref.read(nodeServiceChangeNotifierProvider), + prefs: ref.read(prefsChangeNotifierProvider), + viewOnlyData: viewOnlyData, + ); + + // TODO: extract interface with isRestore param + switch (wallet.runtimeType) { + case const (EpiccashWallet): + await (wallet as EpiccashWallet).init(isRestore: true); + break; + + case const (MoneroWallet): + await (wallet as MoneroWallet).init(isRestore: true); + break; + + case const (WowneroWallet): + await (wallet as WowneroWallet).init(isRestore: true); + break; + + default: + await wallet.init(); + } + + await wallet.recover(isRescan: false); + + // check if state is still active before continuing + if (mounted) { + // don't remove this setMnemonicVerified thing + await wallet.info.setMnemonicVerified( + isar: ref.read(mainDBProvider).isar, + ); + + ref.read(pWallets).addWallet(wallet); + + if (mounted) { + if (Util.isDesktop) { + Navigator.of(context).popUntil( + ModalRoute.withName( + DesktopHomeView.routeName, + ), + ); + } else { + unawaited( + Navigator.of(context).pushNamedAndRemoveUntil( + HomeView.routeName, + (route) => false, + ), + ); + } + + await showDialog( + context: context, + useSafeArea: false, + barrierDismissible: true, + builder: (context) { + return const RestoreSucceededDialog(); + }, + ); + } + } + } catch (e) { + // check if state is still active and restore wasn't cancelled + // before continuing + if (mounted && isRestoring) { + // pop waiting dialog + Navigator.pop(context); + + // show restoring wallet failed dialog + await showDialog( + context: context, + useSafeArea: false, + barrierDismissible: true, + builder: (context) { + return RestoreFailedDialog( + errorMessage: e.toString(), + walletId: info.walletId, + walletName: info.name, + ); + }, + ); + } + } + } finally { + if (!Platform.isLinux && !Util.isDesktop) await WakelockPlus.disable(); + } + } + + @override + void initState() { + super.initState(); + addressController = TextEditingController(); + viewKeyController = TextEditingController(); + + if (widget.coin is Bip39HDCurrency) { + _currentDropDownValue = (widget.coin as Bip39HDCurrency) + .supportedHardenedDerivationPaths + .last; + } + } + + @override + void dispose() { + addressController.dispose(); + viewKeyController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final isDesktop = Util.isDesktop; + + final isElectrumX = widget.coin is ElectrumXCurrencyInterface; + + return MasterScaffold( + isDesktop: isDesktop, + appBar: isDesktop + ? const DesktopAppBar( + isCompactHeight: false, + leading: AppBarBackButton(), + trailing: ExitToMyStackButton(), + ) + : AppBar( + leading: AppBarBackButton( + onPressed: () async { + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed( + const Duration(milliseconds: 50), + ); + } + if (context.mounted) { + Navigator.of(context).pop(); + } + }, + ), + ), + body: Container( + color: Theme.of(context).extension()!.background, + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight, + maxWidth: isDesktop ? 480 : double.infinity, + ), + child: IntrinsicHeight( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + if (isDesktop) + const Spacer( + flex: 10, + ), + if (!isDesktop) + Text( + widget.walletName, + style: STextStyles.itemSubtitle(context), + ), + SizedBox( + height: isDesktop ? 0 : 4, + ), + Text( + "Enter view only details", + style: isDesktop + ? STextStyles.desktopH2(context) + : STextStyles.pageTitleH1(context), + ), + if (isElectrumX) + SizedBox( + height: isDesktop ? 24 : 16, + ), + if (isElectrumX) + SizedBox( + height: isDesktop ? 56 : 48, + width: isDesktop ? 490 : null, + child: Toggle( + key: UniqueKey(), + onText: "Extended pub key", + offText: "Single address", + onColor: Theme.of(context) + .extension()! + .popupBG, + offColor: Theme.of(context) + .extension()! + .textFieldDefaultBG, + isOn: _addressOnly, + onValueChanged: (value) { + setState(() { + _addressOnly = value; + }); + }, + decoration: BoxDecoration( + color: Colors.transparent, + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + ), + ), + SizedBox( + height: isDesktop ? 24 : 16, + ), + if (!isElectrumX || _addressOnly) + FullTextField( + key: const Key("viewOnlyAddressRestoreFieldKey"), + label: "Address", + controller: addressController, + onChanged: (newValue) { + if (isElectrumX) { + viewKeyController.text = ""; + setState(() { + _enableRestoreButton = newValue.isNotEmpty; + }); + } else { + setState(() { + _enableRestoreButton = newValue.isNotEmpty && + viewKeyController.text.isNotEmpty; + }); + } + }, + ), + if (!isElectrumX) + SizedBox( + height: isDesktop ? 16 : 12, + ), + if (isElectrumX && !_addressOnly) + DropdownButtonHideUnderline( + child: DropdownButton2( + value: _currentDropDownValue, + items: [ + ...(widget.coin as Bip39HDCurrency) + .supportedHardenedDerivationPaths + .map( + (e) => DropdownMenuItem( + value: e, + child: Text( + e, + style: STextStyles.w500_14(context), + ), + ), + ), + ], + onChanged: (value) { + if (value is String) { + setState(() { + _currentDropDownValue = value; + }); + } + }, + isExpanded: true, + buttonStyleData: ButtonStyleData( + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .textFieldDefaultBG, + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + ), + iconStyleData: IconStyleData( + icon: Padding( + padding: const EdgeInsets.only(right: 10), + child: SvgPicture.asset( + Assets.svg.chevronDown, + width: 12, + height: 6, + color: Theme.of(context) + .extension()! + .textFieldActiveSearchIconRight, + ), + ), + ), + dropdownStyleData: DropdownStyleData( + offset: const Offset(0, -10), + elevation: 0, + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .textFieldDefaultBG, + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + ), + menuItemStyleData: const MenuItemStyleData( + padding: EdgeInsets.symmetric( + horizontal: 16, + vertical: 8, + ), + ), + ), + ), + if (isElectrumX && !_addressOnly) + SizedBox( + height: isDesktop ? 16 : 12, + ), + if (!isElectrumX || !_addressOnly) + FullTextField( + key: const Key("viewOnlyKeyRestoreFieldKey"), + label: + "${isElectrumX ? "Extended" : "Private View"} Key", + controller: viewKeyController, + onChanged: (value) { + if (isElectrumX) { + addressController.text = ""; + setState(() { + _enableRestoreButton = value.isNotEmpty; + }); + } else { + setState(() { + _enableRestoreButton = value.isNotEmpty && + addressController.text.isNotEmpty; + }); + } + }, + ), + if (!isDesktop) const Spacer(), + SizedBox( + height: isDesktop ? 24 : 16, + ), + PrimaryButton( + enabled: _enableRestoreButton, + onPressed: _requestRestore, + width: isDesktop ? 480 : null, + label: "Restore", + ), + if (isDesktop) + const Spacer( + flex: 15, + ), + ], + ), + ), + ), + ), + ); + }, + ), + ), + ); + } +} diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart index 4c26ff3fa..2fae2352c 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart @@ -16,13 +16,14 @@ import 'dart:math'; import 'package:bip39/bip39.dart' as bip39; import 'package:bip39/src/wordlists/english.dart' as bip39wordlist; +import 'package:cs_monero/cs_monero.dart' as lib_monero; +import 'package:cs_monero/src/deprecated/get_height_by_date.dart' + as cs_monero_deprecated; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_libmonero/monero/monero.dart' as libxmr; -import 'package:flutter_libmonero/wownero/wownero.dart' as libwow; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:wakelock/wakelock.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; import '../../../notifications/show_flush_bar.dart'; import '../../../pages_desktop_specific/desktop_home_view.dart'; @@ -86,7 +87,7 @@ class RestoreWalletView extends ConsumerStatefulWidget { final CryptoCurrency coin; final String mnemonicPassphrase; final int seedWordsLength; - final DateTime restoreFromDate; + final DateTime? restoreFromDate; final bool enableLelantusScanning; final BarcodeScannerInterface barcodeScanner; @@ -181,7 +182,7 @@ class _RestoreWalletViewState extends ConsumerState { if (widget.coin is Monero) { switch (widget.seedWordsLength) { case 25: - return libxmr.monero.getMoneroWordList("English").contains(word); + return lib_monero.getMoneroWordList("English").contains(word); case 16: return Monero.sixteenWordsWordList.contains(word); default: @@ -189,7 +190,7 @@ class _RestoreWalletViewState extends ConsumerState { } } if (widget.coin is Wownero) { - final wowneroWordList = libwow.wownero.getWowneroWordList( + final wowneroWordList = lib_monero.getWowneroWordList( "English", seedWordsLength: widget.seedWordsLength, ); @@ -219,29 +220,35 @@ class _RestoreWalletViewState extends ConsumerState { int height = 0; String? otherDataJsonString; - if (widget.coin is Monero) { - height = libxmr.monero.getHeigthByDate(date: widget.restoreFromDate); - } else if (widget.coin is Wownero) { - height = libwow.wownero.getHeightByDate(date: widget.restoreFromDate); + if (widget.restoreFromDate != null) { + if (widget.coin is Monero) { + height = cs_monero_deprecated.getMoneroHeightByDate( + date: widget.restoreFromDate!, + ); + } + if (widget.coin is Wownero) { + height = cs_monero_deprecated.getWowneroHeightByDate( + date: widget.restoreFromDate!, + ); + } + if (height < 0) { + height = 0; + } } - // todo: wait until this implemented - // else if (widget.coin is Wownero) { - // height = wownero.getHeightByDate(date: widget.restoreFromDate); - // } // TODO: make more robust estimate of date maybe using https://explorer.epic.tech/api-index if (widget.coin is Epiccash) { - final int secondsSinceEpoch = - widget.restoreFromDate.millisecondsSinceEpoch ~/ 1000; - const int epicCashFirstBlock = 1565370278; - const double overestimateSecondsPerBlock = 61; - final int chosenSeconds = secondsSinceEpoch - epicCashFirstBlock; - final int approximateHeight = - chosenSeconds ~/ overestimateSecondsPerBlock; - //todo: check if print needed - // debugPrint( - // "approximate height: $approximateHeight chosen_seconds: $chosenSeconds"); - height = approximateHeight; + if (widget.restoreFromDate != null) { + final int secondsSinceEpoch = + widget.restoreFromDate!.millisecondsSinceEpoch ~/ 1000; + const int epicCashFirstBlock = 1565370278; + const double overestimateSecondsPerBlock = 61; + final int chosenSeconds = secondsSinceEpoch - epicCashFirstBlock; + final int approximateHeight = + chosenSeconds ~/ overestimateSecondsPerBlock; + + height = approximateHeight; + } if (height < 0) { height = 0; } @@ -282,7 +289,7 @@ class _RestoreWalletViewState extends ConsumerState { ), ); } else { - if (!Platform.isLinux) await Wakelock.enable(); + if (!Platform.isLinux) await WakelockPlus.enable(); final info = WalletInfo.createNew( coin: widget.coin, @@ -426,12 +433,12 @@ class _RestoreWalletViewState extends ConsumerState { } if (!Platform.isLinux && !isDesktop) { - await Wakelock.disable(); + await WakelockPlus.disable(); } } } catch (e) { if (!Platform.isLinux && !isDesktop) { - await Wakelock.disable(); + await WakelockPlus.disable(); } // if (e is HiveError && @@ -463,7 +470,7 @@ class _RestoreWalletViewState extends ConsumerState { } if (!Platform.isLinux && !isDesktop) { - await Wakelock.disable(); + await WakelockPlus.disable(); } } } @@ -648,16 +655,18 @@ class _RestoreWalletViewState extends ConsumerState { const Duration(milliseconds: 100), ); - await showDialog( - context: context, - useSafeArea: false, - barrierDismissible: true, - builder: (context) { - return ConfirmRecoveryDialog( - onConfirm: attemptRestore, - ); - }, - ); + if (mounted) { + await showDialog( + context: context, + useSafeArea: false, + barrierDismissible: true, + builder: (context) { + return ConfirmRecoveryDialog( + onConfirm: attemptRestore, + ); + }, + ); + } } @override diff --git a/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart b/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart index 9155690cb..e95e33539 100644 --- a/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart +++ b/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart @@ -9,19 +9,18 @@ */ import 'dart:async'; +import 'dart:convert'; import 'dart:math'; +import 'package:cs_monero/src/deprecated/get_height_by_date.dart' + as cs_monero_deprecated; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:tuple/tuple.dart'; + +import '../../../models/keys/view_only_wallet_data.dart'; import '../../../notifications/show_flush_bar.dart'; -import '../add_token_view/edit_wallet_tokens_view.dart'; -import '../new_wallet_options/new_wallet_options_view.dart'; -import '../new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart'; -import '../select_wallet_for_token_view.dart'; -import 'sub_widgets/word_table.dart'; -import 'verify_mnemonic_passphrase_dialog.dart'; -import '../../home_view/home_view.dart'; import '../../../pages_desktop_specific/desktop_home_view.dart'; import '../../../pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart'; import '../../../providers/db/main_db_provider.dart'; @@ -30,15 +29,32 @@ import '../../../providers/providers.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/assets.dart'; import '../../../utilities/constants.dart'; +import '../../../utilities/logger.dart'; +import '../../../utilities/show_loading.dart'; import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; -import '../../../wallets/crypto_currency/coins/ethereum.dart'; +import '../../../wallets/crypto_currency/crypto_currency.dart'; +import '../../../wallets/crypto_currency/intermediate/bip39_hd_currency.dart'; +import '../../../wallets/isar/models/wallet_info.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; +import '../../../wallets/wallet/impl/epiccash_wallet.dart'; +import '../../../wallets/wallet/impl/monero_wallet.dart'; +import '../../../wallets/wallet/impl/wownero_wallet.dart'; +import '../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../../wallets/wallet/wallet.dart'; +import '../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart'; +import '../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../widgets/desktop/desktop_app_bar.dart'; import '../../../widgets/desktop/desktop_scaffold.dart'; -import 'package:tuple/tuple.dart'; +import '../../../widgets/stack_dialog.dart'; +import '../../home_view/home_view.dart'; +import '../add_token_view/edit_wallet_tokens_view.dart'; +import '../new_wallet_options/new_wallet_options_view.dart'; +import '../new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart'; +import '../select_wallet_for_token_view.dart'; +import 'sub_widgets/word_table.dart'; +import 'verify_mnemonic_passphrase_dialog.dart'; final createSpecialEthWalletRoutingFlag = StateProvider((ref) => false); @@ -63,46 +79,25 @@ class _VerifyRecoveryPhraseViewState extends ConsumerState // with WidgetsBindingObserver { - late Wallet _wallet; + late String _walletId; + late CryptoCurrency _coin; late List _mnemonic; late final bool isDesktop; @override void initState() { - _wallet = widget.wallet; + _walletId = widget.wallet.walletId; + _coin = widget.wallet.cryptoCurrency; _mnemonic = widget.mnemonic; isDesktop = Util.isDesktop; - // WidgetsBinding.instance?.addObserver(this); super.initState(); } @override dispose() { - // WidgetsBinding.instance?.removeObserver(this); super.dispose(); } - // @override - // void didChangeAppLifecycleState(AppLifecycleState state) { - // switch (state) { - // case AppLifecycleState.inactive: - // debugPrint( - // "VerifyRecoveryPhraseView ========================= Inactive"); - // break; - // case AppLifecycleState.paused: - // debugPrint("VerifyRecoveryPhraseView ========================= Paused"); - // break; - // case AppLifecycleState.resumed: - // debugPrint( - // "VerifyRecoveryPhraseView ========================= Resumed"); - // break; - // case AppLifecycleState.detached: - // debugPrint( - // "VerifyRecoveryPhraseView ========================= Detached"); - // break; - // } - // } - Future _verifyMnemonicPassphrase() async { final result = await showDialog( context: context, @@ -112,9 +107,161 @@ class _VerifyRecoveryPhraseViewState return result == "verified"; } + Future _convertToViewOnly() async { + int height = 0; + final Map otherDataJson = { + WalletInfoKeys.isViewOnlyKey: true, + }; + + final ViewOnlyWalletType viewOnlyWalletType; + if (widget.wallet is ExtendedKeysInterface) { + if (widget.wallet.cryptoCurrency is Firo) { + otherDataJson.addAll( + { + WalletInfoKeys.lelantusCoinIsarRescanRequired: false, + WalletInfoKeys.enableLelantusScanning: false, + }, + ); + } + viewOnlyWalletType = ViewOnlyWalletType.xPub; + } else if (widget.wallet is LibMoneroWallet) { + if (widget.wallet.cryptoCurrency is Monero) { + height = cs_monero_deprecated.getMoneroHeightByDate( + date: DateTime.now().subtract(const Duration(days: 7)), + ); + } + if (widget.wallet.cryptoCurrency is Wownero) { + height = cs_monero_deprecated.getWowneroHeightByDate( + date: DateTime.now().subtract(const Duration(days: 7)), + ); + } + if (height < 0) height = 0; + + viewOnlyWalletType = ViewOnlyWalletType.cryptonote; + } else { + throw Exception( + "Unsupported view only wallet type found: ${widget.wallet.runtimeType}", + ); + } + + otherDataJson[WalletInfoKeys.viewOnlyTypeIndexKey] = + viewOnlyWalletType.index; + + final voInfo = WalletInfo.createNew( + coin: _coin, + name: widget.wallet.info.name, + restoreHeight: height, + otherDataJsonString: jsonEncode(otherDataJson), + ); + + final ViewOnlyWalletData viewOnlyData; + if (widget.wallet is ExtendedKeysInterface) { + final extendedKeyInfo = + await (widget.wallet as ExtendedKeysInterface).getXPubs(); + final testPath = (_coin as Bip39HDCurrency).constructDerivePath( + derivePathType: (_coin as Bip39HDCurrency).defaultDerivePathType, + chain: 0, + index: 0, + ); + + XPub? xPub; + for (final pub in extendedKeyInfo.xpubs) { + if (testPath.startsWith(pub.path)) { + xPub = pub; + break; + } + } + + if (xPub == null) { + throw Exception("Default derivation path not matched in xPubs"); + } + + viewOnlyData = ExtendedKeysViewOnlyWalletData( + walletId: voInfo.walletId, + xPubs: [xPub], + ); + } else if (widget.wallet is LibMoneroWallet) { + final w = widget.wallet as LibMoneroWallet; + + final info = await w + .hackToCreateNewViewOnlyWalletDataFromNewlyCreatedWalletThisFunctionShouldNotBeCalledUnlessYouKnowWhatYouAreDoing(); + final address = info.$1; + final privateViewKey = info.$2; + + await w.exit(); + + viewOnlyData = CryptonoteViewOnlyWalletData( + walletId: voInfo.walletId, + address: address, + privateViewKey: privateViewKey, + ); + } else { + throw Exception( + "Unsupported view only wallet type found: ${widget.wallet.runtimeType}", + ); + } + + final voWallet = await Wallet.create( + walletInfo: voInfo, + mainDB: ref.read(mainDBProvider), + secureStorageInterface: ref.read(secureStoreProvider), + nodeService: ref.read(nodeServiceChangeNotifierProvider), + prefs: ref.read(prefsChangeNotifierProvider), + viewOnlyData: viewOnlyData, + ); + + try { + // TODO: extract interface with isRestore param + switch (voWallet.runtimeType) { + case const (EpiccashWallet): + await (voWallet as EpiccashWallet).init(isRestore: true); + break; + + case const (MoneroWallet): + await (voWallet as MoneroWallet).init(isRestore: true); + break; + + case const (WowneroWallet): + await (voWallet as WowneroWallet).init(isRestore: true); + break; + + default: + await voWallet.init(); + } + + await voWallet.recover(isRescan: false); + + // don't remove this setMnemonicVerified thing + await voWallet.info.setMnemonicVerified( + isar: ref.read(mainDBProvider).isar, + ); + + ref.read(pWallets).addWallet(voWallet); + + await voWallet.exit(); + + await ref.read(pWallets).deleteWallet( + widget.wallet.info, + ref.read(secureStoreProvider), + ); + } catch (e) { + await ref.read(pWallets).deleteWallet( + widget.wallet.info, + ref.read(secureStoreProvider), + ); + await ref.read(pWallets).deleteWallet( + voWallet.info, + ref.read(secureStoreProvider), + ); + + rethrow; + } + } + Future _continue(bool isMatch) async { if (isMatch) { - if (ref.read(pNewWalletOptions.state).state != null) { + if (ref.read(pNewWalletOptions) != null && + ref.read(pNewWalletOptions)!.mnemonicPassphrase.isNotEmpty) { final passphraseVerified = await _verifyMnemonicPassphrase(); if (!passphraseVerified) { @@ -122,11 +269,11 @@ class _VerifyRecoveryPhraseViewState } } - await ref.read(pWalletInfo(_wallet.walletId)).setMnemonicVerified( + await ref.read(pWalletInfo(widget.wallet.walletId)).setMnemonicVerified( isar: ref.read(mainDBProvider).isar, ); - ref.read(pWallets).addWallet(_wallet); + ref.read(pWallets).addWallet(widget.wallet); final isCreateSpecialEthWallet = ref.read(createSpecialEthWalletRoutingFlag); @@ -140,6 +287,51 @@ class _VerifyRecoveryPhraseViewState .state; } + if (mounted && + ref.read(pNewWalletOptions)?.convertToViewOnly == true && + widget.wallet is ViewOnlyOptionInterface) { + try { + Exception? ex; + await showLoading( + whileFuture: _convertToViewOnly(), + context: context, + message: "Converting to view only wallet", + rootNavigator: Util.isDesktop, + onException: (e) { + ex = e; + }, + ); + + if (ex != null) { + throw ex!; + } + } catch (e, s) { + Logging.instance.log( + "$e\n$s", + level: LogLevel.Fatal, + ); + + if (mounted) { + await showDialog( + context: context, + builder: (_) => StackOkDialog( + title: e.toString(), + desktopPopRootNavigator: Util.isDesktop, + ), + ); + } + + if (mounted) { + Navigator.of(context).popUntil( + ModalRoute.withName( + NewWalletRecoveryPhraseView.routeName, + ), + ); + } + return; + } + } + if (mounted) { if (isDesktop) { if (isCreateSpecialEthWallet) { @@ -154,7 +346,7 @@ class _VerifyRecoveryPhraseViewState DesktopHomeView.routeName, ), ); - if (widget.wallet.info.coin is Ethereum) { + if (_coin is Ethereum) { unawaited( Navigator.of(context).pushNamed( EditWalletTokensView.routeName, @@ -177,7 +369,7 @@ class _VerifyRecoveryPhraseViewState (route) => false, ), ); - if (widget.wallet.info.coin is Ethereum) { + if (_coin is Ethereum) { unawaited( Navigator.of(context).pushNamed( EditWalletTokensView.routeName, @@ -267,7 +459,7 @@ class _VerifyRecoveryPhraseViewState Future delete() async { await ref.read(pWallets).deleteWallet( - _wallet.info, + widget.wallet.info, ref.read(secureStoreProvider), ); } @@ -297,7 +489,7 @@ class _VerifyRecoveryPhraseViewState trailing: ExitToMyStackButton( onPressed: () async { await delete(); - if (mounted) { + if (context.mounted) { Navigator.of(context).popUntil( ModalRoute.withName(DesktopHomeView.routeName), ); diff --git a/lib/pages/address_book_views/subviews/new_contact_address_entry_form.dart b/lib/pages/address_book_views/subviews/new_contact_address_entry_form.dart index 2bad87bc6..d84cee567 100644 --- a/lib/pages/address_book_views/subviews/new_contact_address_entry_form.dart +++ b/lib/pages/address_book_views/subviews/new_contact_address_entry_form.dart @@ -66,6 +66,62 @@ class _NewContactAddressEntryFormState List coins = []; + void _onQrTapped() async { + try { + // ref + // .read(shouldShowLockscreenOnResumeStateProvider + // .state) + // .state = false; + final qrResult = await widget.barcodeScanner.scan(); + + // Future.delayed( + // const Duration(seconds: 2), + // () => ref + // .read( + // shouldShowLockscreenOnResumeStateProvider + // .state) + // .state = true, + // ); + + final paymentData = AddressUtils.parsePaymentUri( + qrResult.rawContent, + logging: Logging.instance, + ); + + if (paymentData != null) { + addressController.text = paymentData.address; + ref.read(addressEntryDataProvider(widget.id)).address = + addressController.text.isEmpty ? null : addressController.text; + + addressLabelController.text = + paymentData.label ?? addressLabelController.text; + ref.read(addressEntryDataProvider(widget.id)).addressLabel = + addressLabelController.text.isEmpty + ? null + : addressLabelController.text; + + // now check for non standard encoded basic address + } else if (ref.read(addressEntryDataProvider(widget.id)).coin != null) { + if (ref.read(addressEntryDataProvider(widget.id)).coin!.validateAddress( + qrResult.rawContent, + )) { + addressController.text = qrResult.rawContent; + ref.read(addressEntryDataProvider(widget.id)).address = + qrResult.rawContent; + } + } + } on PlatformException catch (e, s) { + // ref + // .read(shouldShowLockscreenOnResumeStateProvider + // .state) + // .state = true; + Logging.instance.log( + "Failed to get camera permissions to scan address qr code: $e\n$s", + level: LogLevel.Warning, + ); + } + } + @override void initState() { addressLabelController = TextEditingController() @@ -75,6 +131,14 @@ class _NewContactAddressEntryFormState addressLabelFocusNode = FocusNode(); addressFocusNode = FocusNode(); coins = [...AppConfig.coins]; + + if (AppConfig.isSingleCoinApp) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + ref.read(addressEntryDataProvider(widget.id)).coin = coins.first; + } + }); + } super.initState(); } @@ -109,7 +173,7 @@ class _NewContactAddressEntryFormState return Column( children: [ - if (isDesktop) + if (isDesktop && !AppConfig.isSingleCoinApp) DropdownButtonHideUnderline( child: DropdownButton2( hint: Text( @@ -188,7 +252,7 @@ class _NewContactAddressEntryFormState ], ), ), - if (!isDesktop) + if (!isDesktop && !AppConfig.isSingleCoinApp) TextField( autocorrect: Util.isDesktop ? false : true, enableSuggestions: Util.isDesktop ? false : true, @@ -280,9 +344,10 @@ class _NewContactAddressEntryFormState ), ), ), - const SizedBox( - height: 8, - ), + if (!AppConfig.isSingleCoinApp) + const SizedBox( + height: 8, + ), ClipRRect( borderRadius: BorderRadius.circular( Constants.size.circularBorderRadius, @@ -395,71 +460,7 @@ class _NewContactAddressEntryFormState null) TextFieldIconButton( key: const Key("addAddressBookEntryScanQrButtonKey"), - onTap: () async { - try { - // ref - // .read(shouldShowLockscreenOnResumeStateProvider - // .state) - // .state = false; - final qrResult = await widget.barcodeScanner.scan(); - - // Future.delayed( - // const Duration(seconds: 2), - // () => ref - // .read( - // shouldShowLockscreenOnResumeStateProvider - // .state) - // .state = true, - // ); - - final results = - AddressUtils.parseUri(qrResult.rawContent); - if (results.isNotEmpty) { - addressController.text = results["address"] ?? ""; - ref - .read(addressEntryDataProvider(widget.id)) - .address = - addressController.text.isEmpty - ? null - : addressController.text; - - addressLabelController.text = results["label"] ?? - addressLabelController.text; - ref - .read(addressEntryDataProvider(widget.id)) - .addressLabel = - addressLabelController.text.isEmpty - ? null - : addressLabelController.text; - - // now check for non standard encoded basic address - } else if (ref - .read(addressEntryDataProvider(widget.id)) - .coin != - null) { - if (ref - .read(addressEntryDataProvider(widget.id)) - .coin! - .validateAddress( - qrResult.rawContent, - )) { - addressController.text = qrResult.rawContent; - ref - .read(addressEntryDataProvider(widget.id)) - .address = qrResult.rawContent; - } - } - } on PlatformException catch (e, s) { - // ref - // .read(shouldShowLockscreenOnResumeStateProvider - // .state) - // .state = true; - Logging.instance.log( - "Failed to get camera permissions to scan address qr code: $e\n$s", - level: LogLevel.Warning, - ); - } - }, + onTap: _onQrTapped, child: const QrCodeIcon(), ), const SizedBox( diff --git a/lib/pages/buy_view/buy_form.dart b/lib/pages/buy_view/buy_form.dart index dfc395492..f47831547 100644 --- a/lib/pages/buy_view/buy_form.dart +++ b/lib/pages/buy_view/buy_form.dart @@ -154,6 +154,8 @@ class _BuyFormState extends ConsumerState { } } + String _receivingAddressValidationErrorString = ""; + void selectCrypto() async { if (ref.read(simplexProvider).supportedCryptos.isEmpty) { bool shouldPop = false; @@ -711,6 +713,60 @@ class _BuyFormState extends ConsumerState { } } + void _onQrTapped() async { + try { + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed( + const Duration(milliseconds: 75), + ); + } + + final qrResult = await scanner.scan(); + + Logging.instance.log( + "qrResult content: ${qrResult.rawContent}", + level: LogLevel.Info, + ); + + final paymentData = AddressUtils.parsePaymentUri( + qrResult.rawContent, + logging: Logging.instance, + ); + + Logging.instance.log( + "qrResult parsed: $paymentData", + level: LogLevel.Info, + ); + + if (paymentData != null) { + // auto fill address + _address = paymentData.address; + _receiveAddressController.text = _address!; + + setState(() { + _addressToggleFlag = _receiveAddressController.text.isNotEmpty; + }); + + // now check for non standard encoded basic address + } else { + _address = qrResult.rawContent; + _receiveAddressController.text = _address ?? ""; + + setState(() { + _addressToggleFlag = _receiveAddressController.text.isNotEmpty; + }); + } + } on PlatformException catch (e, s) { + // here we ignore the exception caused by not giving permission + // to use the camera to scan a qr code + Logging.instance.log( + "Failed to get camera permissions while trying to scan qr code in SendView: $e\n$s", + level: LogLevel.Warning, + ); + } + } + @override void initState() { _receiveAddressController = TextEditingController(); @@ -1218,6 +1274,15 @@ class _BuyFormState extends ConsumerState { _address = newValue; setState(() { _addressToggleFlag = newValue.isNotEmpty; + + // TODO [prio=low]: Validate address. + if (newValue.startsWith("bc1p")) { + // Display an error message or handle invalid address + _receivingAddressValidationErrorString = + "Taproot addresses are not allowed."; + } else { + _receivingAddressValidationErrorString = ""; + } }); }, focusNode: _receiveAddressFocusNode, @@ -1364,63 +1429,7 @@ class _BuyFormState extends ConsumerState { !isDesktop) TextFieldIconButton( key: const Key("buyViewScanQrButtonKey"), - onTap: () async { - try { - if (FocusScope.of(context).hasFocus) { - FocusScope.of(context).unfocus(); - await Future.delayed( - const Duration(milliseconds: 75), - ); - } - - final qrResult = await scanner.scan(); - - Logging.instance.log( - "qrResult content: ${qrResult.rawContent}", - level: LogLevel.Info, - ); - - final results = AddressUtils.parseUri( - qrResult.rawContent, - ); - - Logging.instance.log( - "qrResult parsed: $results", - level: LogLevel.Info, - ); - - if (results.isNotEmpty) { - // auto fill address - _address = results["address"] ?? ""; - _receiveAddressController.text = _address!; - - setState(() { - _addressToggleFlag = - _receiveAddressController - .text.isNotEmpty; - }); - - // now check for non standard encoded basic address - } else { - _address = qrResult.rawContent; - _receiveAddressController.text = - _address ?? ""; - - setState(() { - _addressToggleFlag = - _receiveAddressController - .text.isNotEmpty; - }); - } - } on PlatformException catch (e, s) { - // here we ignore the exception caused by not giving permission - // to use the camera to scan a qr code - Logging.instance.log( - "Failed to get camera permissions while trying to scan qr code in SendView: $e\n$s", - level: LogLevel.Warning, - ); - } - }, + onTap: _onQrTapped, child: const QrCodeIcon(), ), ], @@ -1430,6 +1439,14 @@ class _BuyFormState extends ConsumerState { ), ), ), + SizedBox( + height: isDesktop ? 10 : 4, + ), + if (_receivingAddressValidationErrorString.isNotEmpty) + Text( + _receivingAddressValidationErrorString, + style: STextStyles.errorSmall(context), + ), SizedBox( height: isDesktop ? 20 : 12, ), diff --git a/lib/pages/cashfusion/fusion_progress_view.dart b/lib/pages/cashfusion/fusion_progress_view.dart index e4d787903..022fa0861 100644 --- a/lib/pages/cashfusion/fusion_progress_view.dart +++ b/lib/pages/cashfusion/fusion_progress_view.dart @@ -12,7 +12,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:wakelock/wakelock.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; import '../../pages_desktop_specific/cashfusion/sub_widgets/fusion_progress.dart'; import '../../providers/cash_fusion/fusion_progress_ui_state_provider.dart'; @@ -85,7 +85,7 @@ class _FusionProgressViewState extends ConsumerState { message: "Stopping fusion", ); - await Wakelock.disable(); + await WakelockPlus.disable(); return true; } else { @@ -101,7 +101,7 @@ class _FusionProgressViewState extends ConsumerState { @override void dispose() { - Wakelock.disable(); + WakelockPlus.disable(); super.dispose(); } @@ -117,7 +117,7 @@ class _FusionProgressViewState extends ConsumerState { .watch(fusionProgressUIStateProvider(widget.walletId)) .fusionRoundsCompleted; - Wakelock.enable(); + WakelockPlus.enable(); return WillPopScope( onWillPop: () async { diff --git a/lib/pages/churning/churn_error_dialog.dart b/lib/pages/churning/churn_error_dialog.dart new file mode 100644 index 000000000..c9335faff --- /dev/null +++ b/lib/pages/churning/churn_error_dialog.dart @@ -0,0 +1,127 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../providers/churning/churning_service_provider.dart'; +import '../../utilities/text_styles.dart'; +import '../../utilities/util.dart'; +import '../../widgets/conditional_parent.dart'; +import '../../widgets/desktop/desktop_dialog.dart'; +import '../../widgets/desktop/primary_button.dart'; +import '../../widgets/desktop/secondary_button.dart'; +import '../../widgets/stack_dialog.dart'; + +class ChurnErrorDialog extends ConsumerWidget { + const ChurnErrorDialog({ + super.key, + required this.error, + required this.walletId, + }); + + final String error; + final String walletId; + + static const errorTitle = "An error occurred"; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return ConditionalParent( + condition: Util.isDesktop, + builder: (child) => DesktopDialog( + maxHeight: double.infinity, + child: child, + ), + child: ConditionalParent( + condition: !Util.isDesktop, + builder: (child) => StackDialogBase( + child: child, + ), + child: Column( + children: [ + Util.isDesktop + ? Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only(left: 32, top: 32), + child: Text( + errorTitle, + style: STextStyles.desktopH2(context), + ), + ), + ], + ) + : Text( + errorTitle, + style: STextStyles.pageTitleH2(context), + ), + const SizedBox( + height: 20, + ), + Padding( + padding: Util.isDesktop + ? const EdgeInsets.all(32) + : const EdgeInsets.all(20), + child: Row( + children: [ + Flexible( + child: SelectableText( + error.startsWith("Exception:") + ? error.substring(10).trim() + : error, + ), + ), + ], + ), + ), + const SizedBox( + height: 20, + ), + Padding( + padding: Util.isDesktop + ? const EdgeInsets.all(32) + : const EdgeInsets.all(20), + child: Text( + "Stop churning or try and continue?", + style: Util.isDesktop + ? STextStyles.w600_14(context) + : STextStyles.w600_14(context), + ), + ), + Padding( + padding: EdgeInsets.only( + left: Util.isDesktop ? 32 : 20, + bottom: Util.isDesktop ? 32 : 20, + right: Util.isDesktop ? 32 : 20, + ), + child: Row( + children: [ + Expanded( + child: SecondaryButton( + label: "Stop", + onPressed: () { + ref.read(pChurningService(walletId)).stopChurning(); + Navigator.of(context).pop(); + }, + ), + ), + SizedBox( + width: Util.isDesktop ? 20 : 16, + ), + Expanded( + child: PrimaryButton( + label: "Continue", + onPressed: () { + ref.read(pChurningService(walletId)).unpause(); + Navigator.of(context).pop(); + }, + ), + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/churning/churning_progress_view.dart b/lib/pages/churning/churning_progress_view.dart new file mode 100644 index 000000000..55319738a --- /dev/null +++ b/lib/pages/churning/churning_progress_view.dart @@ -0,0 +1,264 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; + +import '../../providers/churning/churning_service_provider.dart'; +import '../../themes/stack_colors.dart'; +import '../../utilities/assets.dart'; +import '../../utilities/text_styles.dart'; +import '../../widgets/background.dart'; +import '../../widgets/churning/churn_progress_item.dart'; +import '../../widgets/custom_buttons/app_bar_icon_button.dart'; +import '../../widgets/desktop/primary_button.dart'; +import '../../widgets/desktop/secondary_button.dart'; +import '../../widgets/monero_chan_dance.dart'; +import '../../widgets/rounded_container.dart'; +import '../../widgets/stack_dialog.dart'; +import 'churn_error_dialog.dart'; + +class ChurningProgressView extends ConsumerStatefulWidget { + const ChurningProgressView({ + super.key, + required this.walletId, + }); + + static const routeName = "/churningProgressView"; + + final String walletId; + @override + ConsumerState createState() => + _ChurningProgressViewState(); +} + +class _ChurningProgressViewState extends ConsumerState { + Future _requestAndProcessCancel() async { + final shouldCancel = await showDialog( + context: context, + barrierDismissible: false, + builder: (_) => StackDialog( + title: "Cancel churning?", + leftButton: SecondaryButton( + label: "No", + buttonHeight: null, + onPressed: () { + Navigator.of(context).pop(false); + }, + ), + rightButton: PrimaryButton( + label: "Yes", + buttonHeight: null, + onPressed: () { + Navigator.of(context).pop(true); + }, + ), + ), + ); + + if (shouldCancel == true && mounted) { + ref.read(pChurningService(widget.walletId)).stopChurning(); + + await WakelockPlus.disable(); + + return true; + } else { + return false; + } + } + + @override + void initState() { + super.initState(); + + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) ref.read(pChurningService(widget.walletId)).churn(); + }); + } + + @override + void dispose() { + WakelockPlus.disable(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final bool _succeeded = ref.watch( + pChurningService(widget.walletId).select((s) => s.done), + ); + + final int _roundsCompleted = ref.watch( + pChurningService(widget.walletId).select((s) => s.roundsCompleted), + ); + + WakelockPlus.enable(); + + ref.listen( + pChurningService(widget.walletId).select((s) => s.lastSeenError), + (p, n) { + if (!ref.read(pChurningService(widget.walletId)).ignoreErrors && + n != null) { + if (context.mounted) { + showDialog( + context: context, + builder: (context) => ChurnErrorDialog( + error: n.toString(), + walletId: widget.walletId, + ), + ); + } + } + }, + ); + + return WillPopScope( + onWillPop: () async { + return await _requestAndProcessCancel(); + }, + child: Background( + child: SafeArea( + child: Scaffold( + backgroundColor: + Theme.of(context).extension()!.background, + appBar: AppBar( + automaticallyImplyLeading: false, + leading: AppBarBackButton( + onPressed: () async { + if (await _requestAndProcessCancel()) { + if (context.mounted) { + Navigator.of(context).pop(); + } + } + }, + ), + title: Text( + "Churning progress", + style: STextStyles.navBarTitle(context), + ), + titleSpacing: 0, + ), + body: LayoutBuilder( + builder: (builderContext, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight, + ), + child: IntrinsicHeight( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + if (_roundsCompleted == 0) + RoundedContainer( + color: Theme.of(context) + .extension()! + .snackBarBackError, + child: Text( + "Do not close this window. If you exit, " + "the process will be canceled.", + style: + STextStyles.smallMed14(context).copyWith( + color: Theme.of(context) + .extension()! + .snackBarTextError, + ), + textAlign: TextAlign.center, + ), + ), + if (_roundsCompleted > 0) + RoundedContainer( + color: Theme.of(context) + .extension()! + .snackBarBackInfo, + child: Text( + "Churning rounds completed: $_roundsCompleted", + style: STextStyles.w500_14(context).copyWith( + color: Theme.of(context) + .extension()! + .snackBarTextInfo, + ), + textAlign: TextAlign.center, + ), + ), + const SizedBox( + height: 20, + ), + const MoneroChanDance(), + const SizedBox( + height: 20, + ), + ProgressItem( + iconAsset: Assets.svg.alertCircle, + label: "Waiting for balance to unlock ${ref.watch( + pChurningService(widget.walletId) + .select((s) => s.confirmsInfo), + ) ?? ""}", + status: ref.watch( + pChurningService(widget.walletId) + .select((s) => s.waitingForUnlockedBalance), + ), + ), + const SizedBox( + height: 12, + ), + ProgressItem( + iconAsset: Assets.svg.churn, + label: "Creating churn transaction", + status: ref.watch( + pChurningService(widget.walletId) + .select((s) => s.makingChurnTransaction), + ), + ), + const SizedBox( + height: 12, + ), + ProgressItem( + iconAsset: Assets.svg.checkCircle, + label: "Complete", + status: ref.watch( + pChurningService(widget.walletId) + .select((s) => s.completedStatus), + ), + ), + const Spacer(), + const SizedBox( + height: 16, + ), + if (_succeeded) + PrimaryButton( + label: "Churn again", + onPressed: ref + .read(pChurningService(widget.walletId)) + .churn, + ), + if (_succeeded) + const SizedBox( + height: 16, + ), + SecondaryButton( + label: "Cancel", + onPressed: () async { + if (await _requestAndProcessCancel()) { + if (context.mounted) { + Navigator.of(context).pop(); + } + } + }, + ), + ], + ), + ), + ), + ), + ); + }, + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/churning/churning_rounds_selection_sheet.dart b/lib/pages/churning/churning_rounds_selection_sheet.dart new file mode 100644 index 000000000..de21964e8 --- /dev/null +++ b/lib/pages/churning/churning_rounds_selection_sheet.dart @@ -0,0 +1,159 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; + +import '../../themes/stack_colors.dart'; +import '../../utilities/constants.dart'; +import '../../utilities/extensions/extensions.dart'; +import '../../utilities/text_styles.dart'; + +enum ChurnOption { + continuous, + custom; +} + +class ChurnRoundCountSelectSheet extends HookWidget { + const ChurnRoundCountSelectSheet({ + super.key, + required this.currentOption, + }); + + final ChurnOption currentOption; + + @override + Widget build(BuildContext context) { + final option = useState(currentOption); + + return WillPopScope( + onWillPop: () async { + Navigator.of(context).pop(option.value); + return false; + }, + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).extension()!.popupBG, + borderRadius: const BorderRadius.vertical( + top: Radius.circular(20), + ), + ), + child: Padding( + padding: const EdgeInsets.only( + left: 24, + right: 24, + top: 10, + bottom: 0, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center( + child: Container( + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .textFieldDefaultBG, + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + width: 60, + height: 4, + ), + ), + const SizedBox( + height: 36, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Rounds of churn", + style: STextStyles.pageTitleH2(context), + textAlign: TextAlign.left, + ), + const SizedBox( + height: 20, + ), + for (int i = 0; i < ChurnOption.values.length; i++) + Column( + children: [ + GestureDetector( + onTap: () { + option.value = ChurnOption.values[i]; + Navigator.of(context).pop(option.value); + }, + child: Container( + color: Colors.transparent, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Column( + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + SizedBox( + width: 20, + height: 20, + child: Radio( + activeColor: Theme.of(context) + .extension()! + .radioButtonIconEnabled, + value: ChurnOption.values[i], + groupValue: option.value, + onChanged: (_) { + option.value = ChurnOption.values[i]; + Navigator.of(context).pop(option.value); + }, + ), + ), + // ], + // ), + const SizedBox( + width: 12, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + ChurnOption.values[i].name.capitalize(), + style: STextStyles.titleBold12(context), + textAlign: TextAlign.left, + ), + const SizedBox( + height: 2, + ), + Text( + ChurnOption.values[i] == + ChurnOption.continuous + ? "Keep churning until manually stopped" + : "Stop after a set number of churns", + style: STextStyles.itemSubtitle12(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark3, + ), + textAlign: TextAlign.left, + ), + ], + ), + ], + ), + ), + ), + const SizedBox( + height: 16, + ), + ], + ), + const SizedBox( + height: 16, + ), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/churning/churning_view.dart b/lib/pages/churning/churning_view.dart new file mode 100644 index 000000000..d59042e5e --- /dev/null +++ b/lib/pages/churning/churning_view.dart @@ -0,0 +1,292 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; + +import '../../providers/churning/churning_service_provider.dart'; +import '../../themes/stack_colors.dart'; +import '../../utilities/assets.dart'; +import '../../utilities/constants.dart'; +import '../../utilities/extensions/extensions.dart'; +import '../../utilities/text_styles.dart'; +import '../../widgets/background.dart'; +import '../../widgets/custom_buttons/app_bar_icon_button.dart'; +import '../../widgets/custom_buttons/checkbox_text_button.dart'; +import '../../widgets/desktop/primary_button.dart'; +import '../../widgets/rounded_container.dart'; +import '../../widgets/rounded_white_container.dart'; +import '../../widgets/stack_dialog.dart'; +import '../../widgets/stack_text_field.dart'; +import 'churning_progress_view.dart'; +import 'churning_rounds_selection_sheet.dart'; + +class ChurningView extends ConsumerStatefulWidget { + const ChurningView({ + super.key, + required this.walletId, + }); + + static const routeName = "/churnView"; + + final String walletId; + + @override + ConsumerState createState() => _ChurnViewState(); +} + +class _ChurnViewState extends ConsumerState { + late final TextEditingController churningRoundController; + late final FocusNode churningRoundFocusNode; + + bool _enableStartButton = false; + + ChurnOption _option = ChurnOption.continuous; + + Future _startChurn() async { + final churningService = ref.read(pChurningService(widget.walletId)); + + final int rounds = _option == ChurnOption.continuous + ? 0 + : int.parse(churningRoundController.text); + + churningService.rounds = rounds; + + await Navigator.of(context).pushNamed( + ChurningProgressView.routeName, + arguments: widget.walletId, + ); + } + + @override + void initState() { + churningRoundController = TextEditingController(); + + churningRoundFocusNode = FocusNode(); + + final rounds = ref.read(pChurningService(widget.walletId)).rounds; + + _option = rounds == 0 ? ChurnOption.continuous : ChurnOption.custom; + churningRoundController.text = rounds.toString(); + + _enableStartButton = churningRoundController.text.isNotEmpty; + + super.initState(); + } + + @override + void dispose() { + churningRoundController.dispose(); + churningRoundFocusNode.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Background( + child: SafeArea( + child: Scaffold( + backgroundColor: + Theme.of(context).extension()!.background, + appBar: AppBar( + automaticallyImplyLeading: false, + leading: const AppBarBackButton(), + title: Text( + "Churn", + style: STextStyles.navBarTitle(context), + ), + titleSpacing: 0, + actions: [ + AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + size: 36, + icon: SvgPicture.asset( + Assets.svg.circleQuestion, + width: 20, + height: 20, + color: Theme.of(context) + .extension()! + .topNavIconPrimary, + ), + onPressed: () async { + await showDialog( + context: context, + builder: (context) => const StackOkDialog( + title: "What is churning?", + message: "Churning in a Monero wallet involves" + " sending Monero to oneself in multiple" + " transactions, which can enhance privacy" + " by making it harder for observers to " + "link your transactions. This process" + " re-mixes the funds within the network," + " helping obscure transaction history. " + "Churning is optional and mainly beneficial" + " in scenarios where maximum privacy is" + " desired or if you received the Monero from" + " a source from which you'd like to disassociate.", + ), + ); + }, + ), + ), + ], + ), + body: LayoutBuilder( + builder: (builderContext, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight, + ), + child: IntrinsicHeight( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + RoundedWhiteContainer( + child: Text( + "Churning helps anonymize your coins by mixing them.", + style: STextStyles.w500_12(context).copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle1, + ), + ), + ), + const SizedBox( + height: 16, + ), + const SizedBox( + height: 16, + ), + Text( + "Configuration", + style: STextStyles.w500_14(context).copyWith( + color: Theme.of(context) + .extension()! + .textDark3, + ), + ), + const SizedBox( + height: 12, + ), + RoundedContainer( + onPressed: () async { + final option = + await showModalBottomSheet( + backgroundColor: Colors.transparent, + context: context, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(20), + ), + ), + builder: (_) { + return ChurnRoundCountSelectSheet( + currentOption: _option, + ); + }, + ); + if (option != null) { + setState(() { + _option = option; + }); + } + }, + color: Theme.of(context) + .extension()! + .textFieldActiveBG, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + _option.name.capitalize(), + style: STextStyles.w500_12(context), + ), + SvgPicture.asset( + Assets.svg.chevronDown, + width: 12, + color: Theme.of(context) + .extension()! + .textSubtitle1, + ), + ], + ), + ), + ), + if (_option == ChurnOption.custom) + const SizedBox( + height: 10, + ), + if (_option == ChurnOption.custom) + ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + autocorrect: false, + enableSuggestions: false, + controller: churningRoundController, + focusNode: churningRoundFocusNode, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], + keyboardType: TextInputType.number, + onChanged: (value) { + setState(() { + _enableStartButton = value.isNotEmpty; + }); + }, + style: STextStyles.field(context), + decoration: standardInputDecoration( + "Number of churns", + churningRoundFocusNode, + context, + ).copyWith( + labelText: "Enter number of churns..", + ), + ), + ), + const SizedBox( + height: 16, + ), + CheckboxTextButton( + label: "Pause on errors", + initialValue: !ref + .read(pChurningService(widget.walletId)) + .ignoreErrors, + onChanged: (value) { + ref + .read(pChurningService(widget.walletId)) + .ignoreErrors = !value; + }, + ), + const SizedBox( + height: 16, + ), + const Spacer(), + PrimaryButton( + label: "Start", + enabled: _enableStartButton, + onPressed: _startChurn, + ), + ], + ), + ), + ), + ), + ); + }, + ), + ), + ), + ); + } +} diff --git a/lib/pages/coin_control/coin_control_view.dart b/lib/pages/coin_control/coin_control_view.dart index 095046736..07703a149 100644 --- a/lib/pages/coin_control/coin_control_view.dart +++ b/lib/pages/coin_control/coin_control_view.dart @@ -350,6 +350,7 @@ class _CoinControlViewState extends ConsumerState { utxo.isConfirmed( currentHeight, minConfirms, + coin.minCoinbaseConfirms, )), initialSelectedState: isSelected, onSelectedChanged: (value) { @@ -414,6 +415,7 @@ class _CoinControlViewState extends ConsumerState { utxo.isConfirmed( currentHeight, minConfirms, + coin.minCoinbaseConfirms, )), initialSelectedState: isSelected, onSelectedChanged: (value) { @@ -558,6 +560,7 @@ class _CoinControlViewState extends ConsumerState { utxo.isConfirmed( currentHeight, minConfirms, + coin.minCoinbaseConfirms, )), initialSelectedState: isSelected, onSelectedChanged: (value) { diff --git a/lib/pages/coin_control/utxo_card.dart b/lib/pages/coin_control/utxo_card.dart index c0a8395aa..0881c7eb6 100644 --- a/lib/pages/coin_control/utxo_card.dart +++ b/lib/pages/coin_control/utxo_card.dart @@ -117,6 +117,11 @@ class _UtxoCardState extends ConsumerState { .getWallet(widget.walletId) .cryptoCurrency .minConfirms, + ref + .watch(pWallets) + .getWallet(widget.walletId) + .cryptoCurrency + .minCoinbaseConfirms, ) ? UTXOStatusIconStatus.confirmed : UTXOStatusIconStatus.unconfirmed, diff --git a/lib/pages/coin_control/utxo_details_view.dart b/lib/pages/coin_control/utxo_details_view.dart index 54a6f6e5a..ba71f7a01 100644 --- a/lib/pages/coin_control/utxo_details_view.dart +++ b/lib/pages/coin_control/utxo_details_view.dart @@ -13,9 +13,9 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; + import '../../db/isar/main_db.dart'; import '../../models/isar/models/isar_models.dart'; -import '../wallet_view/transaction_views/transaction_details_view.dart'; import '../../providers/global/wallets_provider.dart'; import '../../themes/stack_colors.dart'; import '../../utilities/amount/amount.dart'; @@ -33,6 +33,7 @@ import '../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../widgets/desktop/secondary_button.dart'; import '../../widgets/icon_widgets/utxo_status_icon.dart'; import '../../widgets/rounded_container.dart'; +import '../wallet_view/transaction_views/transaction_details_view.dart'; class UtxoDetailsView extends ConsumerStatefulWidget { const UtxoDetailsView({ @@ -97,6 +98,11 @@ class _UtxoDetailsViewState extends ConsumerState { final confirmed = utxo!.isConfirmed( currentHeight, ref.watch(pWallets).getWallet(widget.walletId).cryptoCurrency.minConfirms, + ref + .watch(pWallets) + .getWallet(widget.walletId) + .cryptoCurrency + .minCoinbaseConfirms, ); return ConditionalParent( diff --git a/lib/pages/exchange_view/exchange_step_views/step_2_view.dart b/lib/pages/exchange_view/exchange_step_views/step_2_view.dart index 461dc2935..028ce0ac4 100644 --- a/lib/pages/exchange_view/exchange_step_views/step_2_view.dart +++ b/lib/pages/exchange_view/exchange_step_views/step_2_view.dart @@ -70,6 +70,78 @@ class _Step2ViewState extends ConsumerState { bool enableNext = false; + void _onRefundQrTapped() async { + try { + final qrResult = await scanner.scan(); + + final paymentData = AddressUtils.parsePaymentUri( + qrResult.rawContent, + logging: Logging.instance, + ); + + if (paymentData != null) { + // auto fill address + _refundController.text = paymentData.address; + model.refundAddress = _refundController.text; + + setState(() { + enableNext = _toController.text.isNotEmpty && + _refundController.text.isNotEmpty; + }); + } else { + _refundController.text = qrResult.rawContent; + model.refundAddress = _refundController.text; + + setState(() { + enableNext = _toController.text.isNotEmpty && + _refundController.text.isNotEmpty; + }); + } + } on PlatformException catch (e, s) { + Logging.instance.log( + "Failed to get camera permissions while trying to scan qr code in SendView: $e\n$s", + level: LogLevel.Warning, + ); + } + } + + void _onToQrTapped() async { + try { + final qrResult = await scanner.scan(); + + final paymentData = AddressUtils.parsePaymentUri( + qrResult.rawContent, + logging: Logging.instance, + ); + + if (paymentData != null) { + // auto fill address + _toController.text = paymentData.address; + model.recipientAddress = _toController.text; + + setState(() { + enableNext = _toController.text.isNotEmpty && + (_refundController.text.isNotEmpty || + !ref.read(efExchangeProvider).supportsRefundAddress); + }); + } else { + _toController.text = qrResult.rawContent; + model.recipientAddress = _toController.text; + + setState(() { + enableNext = _toController.text.isNotEmpty && + (_refundController.text.isNotEmpty || + !!ref.read(efExchangeProvider).supportsRefundAddress); + }); + } + } on PlatformException catch (e, s) { + Logging.instance.log( + "Failed to get camera permissions while trying to scan qr code in SendView: $e\n$s", + level: LogLevel.Warning, + ); + } + } + @override void initState() { model = widget.model; @@ -137,7 +209,7 @@ class _Step2ViewState extends ConsumerState { FocusScope.of(context).unfocus(); await Future.delayed(const Duration(milliseconds: 75)); } - if (mounted) { + if (context.mounted) { Navigator.of(context).pop(); } }, @@ -405,50 +477,7 @@ class _Step2ViewState extends ConsumerState { key: const Key( "sendViewScanQrButtonKey", ), - onTap: () async { - try { - final qrResult = - await scanner.scan(); - - final results = - AddressUtils.parseUri( - qrResult.rawContent, - ); - if (results.isNotEmpty) { - // auto fill address - _toController.text = - results["address"] ?? ""; - model.recipientAddress = - _toController.text; - - setState(() { - enableNext = _toController - .text.isNotEmpty && - (_refundController.text - .isNotEmpty || - !supportsRefund); - }); - } else { - _toController.text = - qrResult.rawContent; - model.recipientAddress = - _toController.text; - - setState(() { - enableNext = _toController - .text.isNotEmpty && - (_refundController.text - .isNotEmpty || - !supportsRefund); - }); - } - } on PlatformException catch (e, s) { - Logging.instance.log( - "Failed to get camera permissions while trying to scan qr code in SendView: $e\n$s", - level: LogLevel.Warning, - ); - } - }, + onTap: _onToQrTapped, child: const QrCodeIcon(), ), ], @@ -685,51 +714,7 @@ class _Step2ViewState extends ConsumerState { key: const Key( "sendViewScanQrButtonKey", ), - onTap: () async { - try { - final qrResult = - await scanner.scan(); - - final results = - AddressUtils.parseUri( - qrResult.rawContent, - ); - if (results.isNotEmpty) { - // auto fill address - _refundController.text = - results["address"] ?? - ""; - model.refundAddress = - _refundController.text; - - setState(() { - enableNext = _toController - .text - .isNotEmpty && - _refundController - .text.isNotEmpty; - }); - } else { - _refundController.text = - qrResult.rawContent; - model.refundAddress = - _refundController.text; - - setState(() { - enableNext = _toController - .text - .isNotEmpty && - _refundController - .text.isNotEmpty; - }); - } - } on PlatformException catch (e, s) { - Logging.instance.log( - "Failed to get camera permissions while trying to scan qr code in SendView: $e\n$s", - level: LogLevel.Warning, - ); - } - }, + onTap: _onRefundQrTapped, child: const QrCodeIcon(), ), ], diff --git a/lib/pages/exchange_view/exchange_step_views/step_4_view.dart b/lib/pages/exchange_view/exchange_step_views/step_4_view.dart index 79655f8a6..69bd4ea01 100644 --- a/lib/pages/exchange_view/exchange_step_views/step_4_view.dart +++ b/lib/pages/exchange_view/exchange_step_views/step_4_view.dart @@ -28,6 +28,7 @@ import '../../../utilities/assets.dart'; import '../../../utilities/clipboard_interface.dart'; import '../../../utilities/constants.dart'; import '../../../utilities/enums/fee_rate_type_enum.dart'; +import '../../../utilities/logger.dart'; import '../../../utilities/text_styles.dart'; import '../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; @@ -315,7 +316,8 @@ class _Step4ViewState extends ConsumerState { ); } } - } catch (e) { + } catch (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Error); if (mounted && !wasCancelled) { // pop building dialog Navigator.of(context).pop(); diff --git a/lib/pages/exchange_view/send_from_view.dart b/lib/pages/exchange_view/send_from_view.dart index 36f9eb4c1..f499775b1 100644 --- a/lib/pages/exchange_view/send_from_view.dart +++ b/lib/pages/exchange_view/send_from_view.dart @@ -28,12 +28,14 @@ import '../../utilities/amount/amount_formatter.dart'; import '../../utilities/assets.dart'; import '../../utilities/constants.dart'; import '../../utilities/enums/fee_rate_type_enum.dart'; +import '../../utilities/logger.dart'; import '../../utilities/text_styles.dart'; import '../../utilities/util.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; import '../../wallets/isar/providers/wallet_info_provider.dart'; import '../../wallets/models/tx_data.dart'; import '../../wallets/wallet/impl/firo_wallet.dart'; +import '../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../widgets/background.dart'; import '../../widgets/conditional_parent.dart'; import '../../widgets/custom_buttons/app_bar_icon_button.dart'; @@ -271,6 +273,15 @@ class _SendFromCardState extends ConsumerState { ), ); + // Currently CwBasedInterface wallets (xmr/wow) shouldn't even have + // access to this screen but this is needed to get past an error that + // would occur only to lead to another error which is why xmr/wow wallets + // don't have access to this screen currently + if (wallet is LibMoneroWallet) { + await wallet.init(); + await wallet.open(); + } + final time = Future.delayed( const Duration( milliseconds: 2500, @@ -375,7 +386,8 @@ class _SendFromCardState extends ConsumerState { ); } } - } catch (e) { + } catch (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Error); if (mounted) { // pop building dialog Navigator.of(context).pop(); diff --git a/lib/pages/exchange_view/trade_details_view.dart b/lib/pages/exchange_view/trade_details_view.dart index 47bfb980b..0416056a7 100644 --- a/lib/pages/exchange_view/trade_details_view.dart +++ b/lib/pages/exchange_view/trade_details_view.dart @@ -190,6 +190,15 @@ class _TradeDetailsViewState extends ConsumerState { final isDesktop = Util.isDesktop; + final showSendFromStackButton = !hasTx && + !["xmr", "monero", "wow", "wownero"] + .contains(trade.payInCurrency.toLowerCase()) && + AppConfig.isStackCoin(trade.payInCurrency) && + (trade.status == "New" || + trade.status == "new" || + trade.status == "waiting" || + trade.status == "Waiting"); + return ConditionalParent( condition: !isDesktop, builder: (child) => Background( @@ -248,21 +257,11 @@ class _TradeDetailsViewState extends ConsumerState { children: children, ), ), - if (!hasTx && - AppConfig.isStackCoin(trade.payInCurrency) && - (trade.status == "New" || - trade.status == "new" || - trade.status == "waiting" || - trade.status == "Waiting")) + if (showSendFromStackButton) const SizedBox( height: 32, ), - if (!hasTx && - AppConfig.isStackCoin(trade.payInCurrency) && - (trade.status == "New" || - trade.status == "new" || - trade.status == "waiting" || - trade.status == "Waiting")) + if (showSendFromStackButton) SecondaryButton( label: "Send from ${AppConfig.prefix}", buttonHeight: ButtonHeight.l, @@ -1371,13 +1370,7 @@ class _TradeDetailsViewState extends ConsumerState { const SizedBox( height: 12, ), - if (!isDesktop && - !hasTx && - AppConfig.isStackCoin(trade.payInCurrency) && - (trade.status == "New" || - trade.status == "new" || - trade.status == "waiting" || - trade.status == "Waiting")) + if (!isDesktop && showSendFromStackButton) SecondaryButton( label: "Send from ${AppConfig.prefix}", onPressed: () { diff --git a/lib/pages/home_view/home_view.dart b/lib/pages/home_view/home_view.dart index 9b6f6eddb..7619a5d34 100644 --- a/lib/pages/home_view/home_view.dart +++ b/lib/pages/home_view/home_view.dart @@ -17,6 +17,7 @@ import 'package:flutter_svg/svg.dart'; import '../../app_config.dart'; import '../../providers/global/notifications_provider.dart'; +import '../../providers/global/prefs_provider.dart'; import '../../providers/ui/home_view_index_provider.dart'; import '../../providers/ui/unread_notifications_provider.dart'; import '../../services/event_bus/events/global/tor_connection_status_changed_event.dart'; @@ -172,6 +173,20 @@ class _HomeViewState extends ConsumerState { @override Widget build(BuildContext context) { debugPrint("BUILD: $runtimeType"); + + // dirty hack + ref.listen( + prefsChangeNotifierProvider.select((value) => value.enableExchange), + (prev, next) { + if (next == false && + mounted && + ref.read(homeViewPageIndexStateProvider) != 0) { + WidgetsBinding.instance.addPostFrameCallback( + (_) => ref.read(homeViewPageIndexStateProvider.state).state = 0, + ); + } + }); + return WillPopScope( onWillPop: _onWillPop, child: Background( @@ -345,7 +360,8 @@ class _HomeViewState extends ConsumerState { ), body: Column( children: [ - if (_children.length > 1) + if (_children.length > 1 && + ref.watch(prefsChangeNotifierProvider).enableExchange) Container( decoration: BoxDecoration( color: Theme.of(context) diff --git a/lib/pages/home_view/sub_widgets/home_view_button_bar.dart b/lib/pages/home_view/sub_widgets/home_view_button_bar.dart index 65ec6a7b8..9741603ac 100644 --- a/lib/pages/home_view/sub_widgets/home_view_button_bar.dart +++ b/lib/pages/home_view/sub_widgets/home_view_button_bar.dart @@ -44,8 +44,6 @@ class _HomeViewButtonBarState extends ConsumerState { @override Widget build(BuildContext context) { - //todo: check if print needed - // debugPrint("BUILD: HomeViewButtonBar"); final selectedIndex = ref.watch(homeViewPageIndexStateProvider.state).state; return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, diff --git a/lib/pages/paynym/dialogs/paynym_details_popup.dart b/lib/pages/paynym/dialogs/paynym_details_popup.dart index 693061482..b3e800a17 100644 --- a/lib/pages/paynym/dialogs/paynym_details_popup.dart +++ b/lib/pages/paynym/dialogs/paynym_details_popup.dart @@ -382,13 +382,15 @@ class _PaynymDetailsPopupState extends ConsumerState { ), child: Row( children: [ - Expanded( - child: PaynymFollowToggleButton( - walletId: widget.walletId, - paymentCodeStringToFollow: widget.accountLite.code, - style: PaynymFollowToggleButtonStyle.detailsPopup, - ), - ), + kDisableFollowing + ? const Spacer() + : Expanded( + child: PaynymFollowToggleButton( + walletId: widget.walletId, + paymentCodeStringToFollow: widget.accountLite.code, + style: PaynymFollowToggleButtonStyle.detailsPopup, + ), + ), const SizedBox( width: 12, ), diff --git a/lib/pages/paynym/paynym_claim_view.dart b/lib/pages/paynym/paynym_claim_view.dart index 9bf9cb3cd..8d61e139c 100644 --- a/lib/pages/paynym/paynym_claim_view.dart +++ b/lib/pages/paynym/paynym_claim_view.dart @@ -13,10 +13,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import '../../models/paynym/paynym_account.dart'; -import 'dialogs/claiming_paynym_dialog.dart'; -import 'paynym_home_view.dart'; -import '../wallet_view/wallet_view.dart'; + import '../../providers/global/paynym_api_provider.dart'; import '../../providers/global/wallets_provider.dart'; import '../../providers/wallet/my_paynym_account_state_provider.dart'; @@ -30,6 +27,9 @@ import '../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../widgets/desktop/desktop_app_bar.dart'; import '../../widgets/desktop/desktop_scaffold.dart'; import '../../widgets/desktop/primary_button.dart'; +import '../wallet_view/wallet_view.dart'; +import 'dialogs/claiming_paynym_dialog.dart'; +import 'paynym_home_view.dart'; class PaynymClaimView extends ConsumerStatefulWidget { const PaynymClaimView({ @@ -46,25 +46,25 @@ class PaynymClaimView extends ConsumerStatefulWidget { } class _PaynymClaimViewState extends ConsumerState { - Future _addSegwitCode(PaynymAccount myAccount) async { - final wallet = - ref.read(pWallets).getWallet(widget.walletId) as PaynymInterface; - - final token = await ref - .read(paynymAPIProvider) - .token(myAccount.nonSegwitPaymentCode.code); - final signature = await wallet.signStringWithNotificationKey(token.value!); - - final pCodeSegwit = await wallet.getPaymentCode(isSegwit: true); - final addResult = await ref.read(paynymAPIProvider).add( - token.value!, - signature, - myAccount.nymID, - pCodeSegwit.toString(), - ); - - return addResult.value ?? false; - } + // Future _addSegwitCode(PaynymAccount myAccount) async { + // final wallet = + // ref.read(pWallets).getWallet(widget.walletId) as PaynymInterface; + // + // final token = await ref + // .read(paynymAPIProvider) + // .token(myAccount.nonSegwitPaymentCode.code); + // final signature = await wallet.signStringWithNotificationKey(token.value!); + // + // final pCodeSegwit = await wallet.getPaymentCode(isSegwit: true); + // final addResult = await ref.read(paynymAPIProvider).add( + // token.value!, + // signature, + // myAccount.nymID, + // pCodeSegwit.toString(), + // ); + // + // return addResult.value ?? false; + // } @override Widget build(BuildContext context) { @@ -197,7 +197,7 @@ class _PaynymClaimViewState extends ConsumerState { if (shouldCancel) return; - // attempt to create new entry in paynym.is db + // attempt to create new entry in [PaynymIsApi.baseURL] db final created = await ref .read(paynymAPIProvider) .create(pCode.toString()); @@ -209,16 +209,16 @@ class _PaynymClaimViewState extends ConsumerState { // payment code already claimed debugPrint("pcode already claimed!!"); - final account = - await ref.read(paynymAPIProvider).nym(pCode.toString()); - if (!account.value!.segwit) { - for (int i = 0; i < 100; i++) { - final result = await _addSegwitCode(account.value!); - if (result == true) { - break; - } - } - } + // final account = + // await ref.read(paynymAPIProvider).nym(pCode.toString()); + // if (!account.value!.segwit) { + // for (int i = 0; i < 100; i++) { + // final result = await _addSegwitCode(account.value!); + // if (result == true) { + // break; + // } + // } + // } if (mounted) { if (isDesktop) { @@ -258,14 +258,14 @@ class _PaynymClaimViewState extends ConsumerState { if (claim.value?.claimed == pCode.toString()) { final account = await ref.read(paynymAPIProvider).nym(pCode.toString()); - if (!account.value!.segwit) { - for (int i = 0; i < 100; i++) { - final result = await _addSegwitCode(account.value!); - if (result == true) { - break; - } - } - } + // if (!account.value!.segwit) { + // for (int i = 0; i < 100; i++) { + // final result = await _addSegwitCode(account.value!); + // if (result == true) { + // break; + // } + // } + // } ref.read(myPaynymAccountStateProvider.state).state = account.value!; diff --git a/lib/pages/paynym/paynym_home_view.dart b/lib/pages/paynym/paynym_home_view.dart index 8c3c6419d..d27a8ae42 100644 --- a/lib/pages/paynym/paynym_home_view.dart +++ b/lib/pages/paynym/paynym_home_view.dart @@ -27,6 +27,7 @@ import '../../utilities/text_styles.dart'; import '../../utilities/util.dart'; import '../../widgets/conditional_parent.dart'; import '../../widgets/custom_buttons/app_bar_icon_button.dart'; +import '../../widgets/custom_buttons/paynym_follow_toggle_button.dart'; import '../../widgets/desktop/desktop_app_bar.dart'; import '../../widgets/desktop/desktop_scaffold.dart'; import '../../widgets/desktop/secondary_button.dart'; @@ -121,72 +122,75 @@ class _PaynymHomeViewState extends ConsumerState { ), ], ), - trailing: Padding( - padding: const EdgeInsets.only(right: 12), - child: SizedBox( - height: 56, - child: MouseRegion( - cursor: SystemMouseCursors.click, - onEnter: (_) => setState(() { - _followButtonHoverState = true; - }), - onExit: (_) => setState(() { - _followButtonHoverState = false; - }), - child: GestureDetector( - onTap: () { - showDialog( - context: context, - builder: (context) => AddNewPaynymFollowView( - walletId: widget.walletId, - ), - ); - }, - child: RoundedContainer( - padding: const EdgeInsets.symmetric(horizontal: 24.0), - color: _followButtonHoverState - ? Theme.of(context) - .extension()! - .highlight - : Colors.transparent, - radiusMultiplier: 100, - child: Row( - children: [ - SvgPicture.asset( - Assets.svg.plus, - width: 16, - height: 16, - color: Theme.of(context) - .extension()! - .textDark, - ), - const SizedBox( - width: 8, - ), - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - "Follow", - style: - STextStyles.desktopButtonSecondaryEnabled( - context, - ).copyWith( - fontSize: 16, + trailing: kDisableFollowing + ? null + : Padding( + padding: const EdgeInsets.only(right: 12), + child: SizedBox( + height: 56, + child: MouseRegion( + cursor: SystemMouseCursors.click, + onEnter: (_) => setState(() { + _followButtonHoverState = true; + }), + onExit: (_) => setState(() { + _followButtonHoverState = false; + }), + child: GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (context) => AddNewPaynymFollowView( + walletId: widget.walletId, + ), + ); + }, + child: RoundedContainer( + padding: + const EdgeInsets.symmetric(horizontal: 24.0), + color: _followButtonHoverState + ? Theme.of(context) + .extension()! + .highlight + : Colors.transparent, + radiusMultiplier: 100, + child: Row( + children: [ + SvgPicture.asset( + Assets.svg.plus, + width: 16, + height: 16, + color: Theme.of(context) + .extension()! + .textDark, ), - ), - const SizedBox( - height: 2, - ), - ], + const SizedBox( + width: 8, + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "Follow", + style: STextStyles + .desktopButtonSecondaryEnabled( + context, + ).copyWith( + fontSize: 16, + ), + ), + const SizedBox( + height: 2, + ), + ], + ), + ], + ), ), - ], + ), ), ), ), - ), - ), - ), ) : AppBar( leading: AppBarBackButton( @@ -201,28 +205,29 @@ class _PaynymHomeViewState extends ConsumerState { overflow: TextOverflow.ellipsis, ), actions: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 6), - child: AspectRatio( - aspectRatio: 1, - child: AppBarIconButton( - icon: SvgPicture.asset( - Assets.svg.circlePlusFilled, - width: 20, - height: 20, - color: Theme.of(context) - .extension()! - .accentColorDark, + if (!kDisableFollowing) + Padding( + padding: const EdgeInsets.symmetric(vertical: 6), + child: AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + icon: SvgPicture.asset( + Assets.svg.circlePlusFilled, + width: 20, + height: 20, + color: Theme.of(context) + .extension()! + .accentColorDark, + ), + onPressed: () { + Navigator.of(context).pushNamed( + AddNewPaynymFollowView.routeName, + arguments: widget.walletId, + ); + }, ), - onPressed: () { - Navigator.of(context).pushNamed( - AddNewPaynymFollowView.routeName, - arguments: widget.walletId, - ); - }, ), ), - ), Padding( padding: const EdgeInsets.symmetric(vertical: 6), child: AspectRatio( diff --git a/lib/pages/paynym/subwidgets/desktop_paynym_details.dart b/lib/pages/paynym/subwidgets/desktop_paynym_details.dart index 1c2b0cb81..6b08b84db 100644 --- a/lib/pages/paynym/subwidgets/desktop_paynym_details.dart +++ b/lib/pages/paynym/subwidgets/desktop_paynym_details.dart @@ -284,13 +284,17 @@ class _PaynymDetailsPopupState extends ConsumerState { const SizedBox( width: 20, ), - Expanded( - child: PaynymFollowToggleButton( - walletId: widget.walletId, - paymentCodeStringToFollow: widget.accountLite.code, - style: PaynymFollowToggleButtonStyle.detailsDesktop, - ), - ), + kDisableFollowing + ? const Spacer() + : Expanded( + child: PaynymFollowToggleButton( + walletId: widget.walletId, + paymentCodeStringToFollow: + widget.accountLite.code, + style: + PaynymFollowToggleButtonStyle.detailsDesktop, + ), + ), ], ), if (_showInsufficientFundsInfo) diff --git a/lib/pages/paynym/subwidgets/paynym_bot.dart b/lib/pages/paynym/subwidgets/paynym_bot.dart index 1a6d6e469..384e471e5 100644 --- a/lib/pages/paynym/subwidgets/paynym_bot.dart +++ b/lib/pages/paynym/subwidgets/paynym_bot.dart @@ -11,8 +11,10 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; + import '../../../networking/http.dart'; import '../../../services/tor_service.dart'; +import '../../../utilities/paynym_is_api.dart'; import '../../../utilities/prefs.dart'; class PayNymBot extends StatelessWidget { @@ -50,7 +52,8 @@ class PayNymBot extends StatelessWidget { Future _fetchImage() async { final HTTP client = HTTP(); - final Uri uri = Uri.parse("https://paynym.is/$paymentCodeString/avatar"); + final Uri uri = + Uri.parse("${PaynymIsApi.baseURL}/$paymentCodeString/avatar"); final response = await client.get( url: uri, diff --git a/lib/pages/paynym/subwidgets/paynym_card.dart b/lib/pages/paynym/subwidgets/paynym_card.dart index 32422a740..da71905f2 100644 --- a/lib/pages/paynym/subwidgets/paynym_card.dart +++ b/lib/pages/paynym/subwidgets/paynym_card.dart @@ -9,12 +9,13 @@ */ import 'package:flutter/material.dart'; -import 'paynym_bot.dart'; + import '../../../themes/stack_colors.dart'; import '../../../utilities/format.dart'; import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; import '../../../widgets/custom_buttons/paynym_follow_toggle_button.dart'; +import 'paynym_bot.dart'; class PaynymCard extends StatefulWidget { const PaynymCard({ @@ -84,10 +85,11 @@ class _PaynymCardState extends State { ], ), ), - PaynymFollowToggleButton( - walletId: widget.walletId, - paymentCodeStringToFollow: widget.paymentCodeString, - ), + if (!kDisableFollowing) + PaynymFollowToggleButton( + walletId: widget.walletId, + paymentCodeStringToFollow: widget.paymentCodeString, + ), ], ), ); diff --git a/lib/pages/pinpad_views/lock_screen_view.dart b/lib/pages/pinpad_views/lock_screen_view.dart index a2b61c404..5d630ba4f 100644 --- a/lib/pages/pinpad_views/lock_screen_view.dart +++ b/lib/pages/pinpad_views/lock_screen_view.dart @@ -12,9 +12,11 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mutex/mutex.dart'; import '../../notifications/show_flush_bar.dart'; // import 'package:stackwallet/providers/global/has_authenticated_start_state_provider.dart'; +import '../../providers/global/node_service_provider.dart'; import '../../providers/global/prefs_provider.dart'; import '../../providers/global/secure_store_provider.dart'; import '../../providers/global/wallets_provider.dart'; @@ -24,8 +26,10 @@ import '../../utilities/assets.dart'; import '../../utilities/biometrics.dart'; import '../../utilities/flutter_secure_storage_interface.dart'; import '../../utilities/show_loading.dart'; +import '../../utilities/show_node_tor_settings_mismatch.dart'; import '../../utilities/text_styles.dart'; -import '../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; +import '../../utilities/util.dart'; +import '../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../widgets/background.dart'; import '../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../widgets/custom_buttons/blue_text_button.dart'; @@ -70,6 +74,7 @@ class LockscreenView extends ConsumerStatefulWidget { class _LockscreenViewState extends ConsumerState { late final ShakeController _shakeController; + late final bool _autoPin; late int _attempts; bool _attemptLock = false; @@ -99,8 +104,22 @@ class _LockscreenViewState extends ConsumerState { final walletId = widget.routeOnSuccessArguments as String; final wallet = ref.read(pWallets).getWallet(walletId); + + final canContinue = await checkShowNodeTorSettingsMismatch( + context: context, + currency: wallet.cryptoCurrency, + prefs: ref.read(prefsChangeNotifierProvider), + nodeService: ref.read(nodeServiceChangeNotifierProvider), + allowCancel: false, + rootNavigator: Util.isDesktop, + ); + + if (!canContinue) { + return; + } + final Future loadFuture; - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { loadFuture = wallet.init().then((value) async => await (wallet).open()); } else { @@ -186,16 +205,18 @@ class _LockscreenViewState extends ConsumerState { biometrics = widget.biometrics; _attempts = 0; _timeout = Duration.zero; - + _autoPin = ref.read(prefsChangeNotifierProvider).autoPin; + if (_autoPin) { + _pinTextController.addListener(_onPinChangedAutologinCheck); + } _checkUseBiometrics(); - _pinTextController.addListener(_onPinChanged); super.initState(); } @override dispose() { // _shakeController.dispose(); - _pinTextController.removeListener(_onPinChanged); + _pinTextController.removeListener(_onPinChangedAutologinCheck); super.dispose(); } @@ -218,16 +239,120 @@ class _LockscreenViewState extends ConsumerState { final _pinTextController = TextEditingController(); - void _onPinChanged() async { - String enteredPin = _pinTextController.text; - final storedPin = await _secureStore.read(key: 'stack_pin'); - final autoPin = ref.read(prefsChangeNotifierProvider).autoPin; + final Mutex _autoPinCheckLock = Mutex(); + void _onPinChangedAutologinCheck() async { + if (mounted) { + await _autoPinCheckLock.acquire(); + } - if (enteredPin.length >= 4 && autoPin && enteredPin == storedPin) { + try { + if (_autoPin && _pinTextController.text.length >= 4) { + final storedPin = await _secureStore.read(key: 'stack_pin'); + if (_pinTextController.text == storedPin) { + await Future.delayed( + const Duration(milliseconds: 200), + ); + unawaited(_onUnlock()); + } + } + } finally { + _autoPinCheckLock.release(); + } + } + + void _onSubmitPin(String pin) async { + _attempts++; + + if (_attempts > maxAttemptsBeforeThrottling) { + _attemptLock = true; + switch (_attempts) { + case 4: + _timeout = const Duration(seconds: 30); + break; + + case 5: + _timeout = const Duration(seconds: 60); + break; + + case 6: + _timeout = const Duration(minutes: 5); + break; + + case 7: + _timeout = const Duration(minutes: 10); + break; + + case 8: + _timeout = const Duration(minutes: 20); + break; + + case 9: + _timeout = const Duration(minutes: 30); + break; + + default: + _timeout = const Duration(minutes: 60); + } + + _timer?.cancel(); + _timer = Timer(_timeout, () { + _attemptLock = false; + _attempts = 0; + }); + } + + if (_attemptLock) { + String prettyTime = ""; + if (_timeout.inSeconds >= 60) { + prettyTime += "${_timeout.inMinutes} minutes"; + } else { + prettyTime += "${_timeout.inSeconds} seconds"; + } + + unawaited( + showFloatingFlushBar( + type: FlushBarType.warning, + message: + "Incorrect PIN entered too many times. Please wait $prettyTime", + context: context, + iconAsset: Assets.svg.alertCircle, + ), + ); + + await Future.delayed( + const Duration(milliseconds: 100), + ); + + _pinTextController.text = ''; + + return; + } + + final storedPin = await _secureStore.read(key: 'stack_pin'); + + if (storedPin == pin) { await Future.delayed( const Duration(milliseconds: 200), ); unawaited(_onUnlock()); + } else { + unawaited(_shakeController.shake()); + if (mounted) { + unawaited( + showFloatingFlushBar( + type: FlushBarType.warning, + message: "Incorrect PIN. Please try again", + context: context, + iconAsset: Assets.svg.alertCircle, + ), + ); + } + + await Future.delayed( + const Duration(milliseconds: 100), + ); + + _pinTextController.text = ''; } } @@ -329,98 +454,9 @@ class _LockscreenViewState extends ConsumerState { isRandom: ref .read(prefsChangeNotifierProvider) .randomizePIN, - onSubmit: (String pin) async { - _attempts++; - - if (_attempts > maxAttemptsBeforeThrottling) { - _attemptLock = true; - switch (_attempts) { - case 4: - _timeout = const Duration(seconds: 30); - break; - - case 5: - _timeout = const Duration(seconds: 60); - break; - - case 6: - _timeout = const Duration(minutes: 5); - break; - - case 7: - _timeout = const Duration(minutes: 10); - break; - - case 8: - _timeout = const Duration(minutes: 20); - break; - - case 9: - _timeout = const Duration(minutes: 30); - break; - - default: - _timeout = const Duration(minutes: 60); - } - - _timer?.cancel(); - _timer = Timer(_timeout, () { - _attemptLock = false; - _attempts = 0; - }); - } - - if (_attemptLock) { - String prettyTime = ""; - if (_timeout.inSeconds >= 60) { - prettyTime += "${_timeout.inMinutes} minutes"; - } else { - prettyTime += "${_timeout.inSeconds} seconds"; - } - - unawaited( - showFloatingFlushBar( - type: FlushBarType.warning, - message: - "Incorrect PIN entered too many times. Please wait $prettyTime", - context: context, - iconAsset: Assets.svg.alertCircle, - ), - ); - - await Future.delayed( - const Duration(milliseconds: 100), - ); - - _pinTextController.text = ''; - - return; - } - - final storedPin = - await _secureStore.read(key: 'stack_pin'); - - if (storedPin == pin) { - await Future.delayed( - const Duration(milliseconds: 200), - ); - unawaited(_onUnlock()); - } else { - unawaited(_shakeController.shake()); - unawaited( - showFloatingFlushBar( - type: FlushBarType.warning, - message: "Incorrect PIN. Please try again", - context: context, - iconAsset: Assets.svg.alertCircle, - ), - ); - - await Future.delayed( - const Duration(milliseconds: 100), - ); - - _pinTextController.text = ''; + onSubmit: (pin) { + if (!_autoPinCheckLock.isLocked) { + _onSubmitPin(pin); } }, ), diff --git a/lib/pages/pinpad_views/pinpad_dialog.dart b/lib/pages/pinpad_views/pinpad_dialog.dart index 35d60aa32..68dbc144c 100644 --- a/lib/pages/pinpad_views/pinpad_dialog.dart +++ b/lib/pages/pinpad_views/pinpad_dialog.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mutex/mutex.dart'; import '../../notifications/show_flush_bar.dart'; import '../../providers/global/prefs_provider.dart'; @@ -38,6 +39,8 @@ class PinpadDialog extends ConsumerStatefulWidget { class _PinpadDialogState extends ConsumerState { late final ShakeController _shakeController; + late final bool _autoPin; + late int _attempts; bool _attemptLock = false; late Duration _timeout; @@ -63,16 +66,24 @@ class _PinpadDialogState extends ConsumerState { ); } - Future _onPinChanged() async { - final enteredPin = _pinTextController.text; - final storedPin = await _secureStore.read(key: 'stack_pin'); - final autoPin = ref.read(prefsChangeNotifierProvider).autoPin; + final Mutex _autoPinCheckLock = Mutex(); + void _onPinChangedAutologinCheck() async { + if (mounted) { + await _autoPinCheckLock.acquire(); + } - if (enteredPin.length >= 4 && autoPin && enteredPin == storedPin) { - await Future.delayed( - const Duration(milliseconds: 200), - ); - unawaited(_onUnlock()); + try { + if (_autoPin && _pinTextController.text.length >= 4) { + final storedPin = await _secureStore.read(key: 'stack_pin'); + if (_pinTextController.text == storedPin) { + await Future.delayed( + const Duration(milliseconds: 200), + ); + unawaited(_onUnlock()); + } + } + } finally { + _autoPinCheckLock.release(); } } @@ -215,16 +226,19 @@ class _PinpadDialogState extends ConsumerState { biometrics = widget.biometrics; _attempts = 0; _timeout = Duration.zero; + _autoPin = ref.read(prefsChangeNotifierProvider).autoPin; + if (_autoPin) { + _pinTextController.addListener(_onPinChangedAutologinCheck); + } _checkUseBiometrics(); - _pinTextController.addListener(_onPinChanged); super.initState(); } @override dispose() { // _shakeController.dispose(); - _pinTextController.removeListener(_onPinChanged); + _pinTextController.removeListener(_onPinChangedAutologinCheck); super.dispose(); } @@ -276,7 +290,11 @@ class _PinpadDialogState extends ConsumerState { submittedFieldDecoration: _pinPutDecoration, isRandom: ref.read(prefsChangeNotifierProvider).randomizePIN, - onSubmit: _onSubmit, + onSubmit: (pin) { + if (!_autoPinCheckLock.isLocked) { + _onSubmit(pin); + } + }, ), const SizedBox( height: 32, diff --git a/lib/pages/receive_view/addresses/address_card.dart b/lib/pages/receive_view/addresses/address_card.dart index db14fd979..59074d979 100644 --- a/lib/pages/receive_view/addresses/address_card.dart +++ b/lib/pages/receive_view/addresses/address_card.dart @@ -304,7 +304,7 @@ class _AddressCardState extends ConsumerState { key: _qrKey, child: QR( data: AddressUtils.buildUriString( - widget.coin, + widget.coin.uriScheme, address.value, {}, ), diff --git a/lib/pages/receive_view/addresses/address_details_view.dart b/lib/pages/receive_view/addresses/address_details_view.dart index eb102bfe7..25e18becb 100644 --- a/lib/pages/receive_view/addresses/address_details_view.dart +++ b/lib/pages/receive_view/addresses/address_details_view.dart @@ -8,6 +8,7 @@ * */ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; @@ -22,6 +23,7 @@ import '../../../utilities/address_utils.dart'; import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; +import '../../../wallets/wallet/intermediate/bip39_hd_wallet.dart'; import '../../../widgets/address_private_key.dart'; import '../../../widgets/background.dart'; import '../../../widgets/conditional_parent.dart'; @@ -96,7 +98,7 @@ class _AddressDetailsViewState extends ConsumerState { key: _qrKey, child: QR( data: AddressUtils.buildUriString( - ref.watch(pWalletCoin(widget.walletId)), + ref.watch(pWalletCoin(widget.walletId)).uriScheme, address.value, {}, ), @@ -146,6 +148,7 @@ class _AddressDetailsViewState extends ConsumerState { @override Widget build(BuildContext context) { final coin = ref.watch(pWalletCoin(widget.walletId)); + final wallet = ref.watch(pWallets).getWallet(widget.walletId); return ConditionalParent( condition: !isDesktop, builder: (child) => Background( @@ -288,7 +291,7 @@ class _AddressDetailsViewState extends ConsumerState { key: _qrKey, child: QR( data: AddressUtils.buildUriString( - coin, + coin.uriScheme, address.value, {}, ), @@ -371,13 +374,25 @@ class _AddressDetailsViewState extends ConsumerState { detail: address.subType.prettyName, button: Container(), ), - const _Div( - height: 12, - ), - AddressPrivateKey( - walletId: widget.walletId, - address: address, - ), + if (kDebugMode) + const _Div( + height: 12, + ), + if (kDebugMode) + DetailItem( + title: "frost secure (kDebugMode)", + detail: address.zSafeFrost.toString(), + button: Container(), + ), + if (wallet is Bip39HDWallet && !wallet.isViewOnly) + const _Div( + height: 12, + ), + if (wallet is Bip39HDWallet && !wallet.isViewOnly) + AddressPrivateKey( + walletId: widget.walletId, + address: address, + ), if (!isDesktop) const SizedBox( height: 20, diff --git a/lib/pages/receive_view/addresses/address_qr_popup.dart b/lib/pages/receive_view/addresses/address_qr_popup.dart index 5a8bc1592..b4446f543 100644 --- a/lib/pages/receive_view/addresses/address_qr_popup.dart +++ b/lib/pages/receive_view/addresses/address_qr_popup.dart @@ -142,7 +142,7 @@ class _AddressQrPopupState extends State { key: _qrKey, child: QR( data: AddressUtils.buildUriString( - widget.coin, + widget.coin.uriScheme, widget.addressString, {}, ), diff --git a/lib/pages/receive_view/addresses/wallet_addresses_view.dart b/lib/pages/receive_view/addresses/wallet_addresses_view.dart index f464d0e57..6c1441b43 100644 --- a/lib/pages/receive_view/addresses/wallet_addresses_view.dart +++ b/lib/pages/receive_view/addresses/wallet_addresses_view.dart @@ -67,6 +67,19 @@ class _WalletAddressesViewState extends ConsumerState { .and() .not() .typeEqualTo(AddressType.nonWallet) + .and() + .group( + (q) => q + .group( + (q2) => q2 + .typeEqualTo(AddressType.frostMS) + .and() + .zSafeFrostEqualTo(true), + ) + .or() + .not() + .typeEqualTo(AddressType.frostMS), + ) .sortByDerivationIndex() .idProperty() .findAll(); @@ -114,6 +127,19 @@ class _WalletAddressesViewState extends ConsumerState { .and() .not() .typeEqualTo(AddressType.nonWallet) + .and() + .group( + (q) => q + .group( + (q2) => q2 + .typeEqualTo(AddressType.frostMS) + .and() + .zSafeFrostEqualTo(true), + ) + .or() + .not() + .typeEqualTo(AddressType.frostMS), + ) .sortByDerivationIndex() .idProperty() .findAll(); diff --git a/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart b/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart index 7a7497d0e..dfe85d934 100644 --- a/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart +++ b/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart @@ -97,7 +97,7 @@ class _GenerateUriQrCodeViewState extends State { initialDirectory: dir.path, ); - if (path != null) { + if (path != null && mounted) { final file = File(path); if (file.existsSync()) { unawaited( @@ -109,13 +109,15 @@ class _GenerateUriQrCodeViewState extends State { ); } else { await file.writeAsBytes(pngBytes); - unawaited( - showFloatingFlushBar( - type: FlushBarType.success, - message: "$path saved!", - context: context, - ), - ); + if (mounted) { + unawaited( + showFloatingFlushBar( + type: FlushBarType.success, + message: "$path saved!", + context: context, + ), + ); + } } } } else { @@ -144,7 +146,18 @@ class _GenerateUriQrCodeViewState extends State { final amountString = amountController.text; final noteString = noteController.text; - if (amountString.isNotEmpty && Decimal.tryParse(amountString) == null) { + // try "." + Decimal? amount = Decimal.tryParse(amountString); + if (amount == null) { + // try single instance of "," + final first = amountString.indexOf(","); + final last = amountString.lastIndexOf(","); + if (first == last) { + amount = Decimal.tryParse(amountString.replaceFirst(",", ".")); + } + } + + if (amountString.isNotEmpty && amount == null) { showFloatingFlushBar( type: FlushBarType.warning, message: "Invalid amount", @@ -156,7 +169,7 @@ class _GenerateUriQrCodeViewState extends State { final Map queryParams = {}; if (amountString.isNotEmpty) { - queryParams["amount"] = amountString; + queryParams["amount"] = amount.toString(); } if (noteString.isNotEmpty) { queryParams["message"] = noteString; @@ -170,7 +183,7 @@ class _GenerateUriQrCodeViewState extends State { } final uriString = AddressUtils.buildUriString( - widget.coin, + widget.coin.uriScheme, receivingAddress, queryParams, ); @@ -263,7 +276,7 @@ class _GenerateUriQrCodeViewState extends State { } _uriString = AddressUtils.buildUriString( - widget.coin, + widget.coin.uriScheme, receivingAddress, {}, ); @@ -300,7 +313,7 @@ class _GenerateUriQrCodeViewState extends State { FocusScope.of(context).unfocus(); await Future.delayed(const Duration(milliseconds: 70)); } - if (mounted) { + if (context.mounted) { Navigator.of(context).pop(); } }, diff --git a/lib/pages/receive_view/receive_view.dart b/lib/pages/receive_view/receive_view.dart index 2beaab5f2..43a5c6c8d 100644 --- a/lib/pages/receive_view/receive_view.dart +++ b/lib/pages/receive_view/receive_view.dart @@ -18,6 +18,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:isar/isar.dart'; import '../../models/isar/models/isar_models.dart'; +import '../../models/keys/view_only_wallet_data.dart'; import '../../notifications/show_flush_bar.dart'; import '../../providers/db/main_db_provider.dart'; import '../../providers/providers.dart'; @@ -34,8 +35,10 @@ import '../../wallets/isar/providers/wallet_info_provider.dart'; import '../../wallets/wallet/impl/bitcoin_wallet.dart'; import '../../wallets/wallet/intermediate/bip39_hd_wallet.dart'; import '../../wallets/wallet/wallet_mixin_interfaces/bcash_interface.dart'; +import '../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart'; import '../../wallets/wallet/wallet_mixin_interfaces/multi_address_interface.dart'; import '../../wallets/wallet/wallet_mixin_interfaces/spark_interface.dart'; +import '../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; import '../../widgets/background.dart'; import '../../widgets/conditional_parent.dart'; import '../../widgets/custom_buttons/app_bar_icon_button.dart'; @@ -107,15 +110,39 @@ class _ReceiveViewState extends ConsumerState { final Address? address; if (wallet is Bip39HDWallet && wallet is! BCashInterface) { - final type = DerivePathType.values.firstWhere( - (e) => e.getAddressType() == _walletAddressTypes[_currentIndex], - ); + DerivePathType? type; + if (wallet.isViewOnly && wallet is ExtendedKeysInterface) { + final voData = await wallet.getViewOnlyWalletData() + as ExtendedKeysViewOnlyWalletData; + for (final t in wallet.cryptoCurrency.supportedDerivationPathTypes) { + final testPath = wallet.cryptoCurrency.constructDerivePath( + derivePathType: t, + chain: 0, + index: 0, + ); + if (testPath.startsWith(voData.xPubs.first.path)) { + type = t; + break; + } + } + } else { + type = DerivePathType.values.firstWhere( + (e) => e.getAddressType() == _walletAddressTypes[_currentIndex], + ); + } + address = await wallet.generateNextReceivingAddress( - derivePathType: type, + derivePathType: type!, ); - await ref.read(mainDBProvider).isar.writeTxn(() async { - await ref.read(mainDBProvider).isar.addresses.put(address!); + final isar = ref.read(mainDBProvider).isar; + await isar.writeTxn(() async { + await isar.addresses.put(address!); }); + final info = ref.read(pWalletInfo(walletId)); + await info.updateReceivingAddress( + newAddress: address.value, + isar: isar, + ); } else { await wallet.generateNewReceivingAddress(); address = null; @@ -183,10 +210,15 @@ class _ReceiveViewState extends ConsumerState { clipboard = widget.clipboard; final wallet = ref.read(pWallets).getWallet(walletId); _supportsSpark = wallet is SparkInterface; - _showMultiType = _supportsSpark || - (wallet is! BCashInterface && - wallet is Bip39HDWallet && - wallet.supportedAddressTypes.length > 1); + + if (wallet is ViewOnlyOptionInterface && wallet.isViewOnly) { + _showMultiType = false; + } else { + _showMultiType = _supportsSpark || + (wallet is! BCashInterface && + wallet is Bip39HDWallet && + wallet.supportedAddressTypes.length > 1); + } _walletAddressTypes.add(wallet.info.mainAddressType); @@ -259,6 +291,18 @@ class _ReceiveViewState extends ConsumerState { address = ref.watch(pWalletReceivingAddress(walletId)); } + final wallet = + ref.watch(pWallets.select((value) => value.getWallet(walletId))); + + final bool canGen; + if (wallet is ViewOnlyOptionInterface && + wallet.isViewOnly && + wallet.viewOnlyType == ViewOnlyWalletType.addressOnly) { + canGen = false; + } else { + canGen = (wallet is MultiAddressInterface || _supportsSpark); + } + return Background( child: Scaffold( backgroundColor: Theme.of(context).extension()!.background, @@ -547,17 +591,11 @@ class _ReceiveViewState extends ConsumerState { ); }, ), - if (ref.watch( - pWallets.select((value) => value.getWallet(walletId)), - ) is MultiAddressInterface || - _supportsSpark) + if (canGen) const SizedBox( height: 12, ), - if (ref.watch( - pWallets.select((value) => value.getWallet(walletId)), - ) is MultiAddressInterface || - _supportsSpark) + if (canGen) SecondaryButton( label: "Generate new address", onPressed: _supportsSpark && @@ -577,7 +615,7 @@ class _ReceiveViewState extends ConsumerState { children: [ QR( data: AddressUtils.buildUriString( - coin, + coin.uriScheme, address, {}, ), diff --git a/lib/pages/send_view/confirm_transaction_view.dart b/lib/pages/send_view/confirm_transaction_view.dart index 09237e080..5090645e3 100644 --- a/lib/pages/send_view/confirm_transaction_view.dart +++ b/lib/pages/send_view/confirm_transaction_view.dart @@ -16,11 +16,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_libepiccash/lib.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../models/isar/models/transaction_note.dart'; import '../../notifications/show_flush_bar.dart'; -import '../pinpad_views/lock_screen_view.dart'; -import 'sub_widgets/sending_transaction_dialog.dart'; -import '../wallet_view/wallet_view.dart'; import '../../pages_desktop_specific/coin_control/desktop_coin_control_use_dialog.dart'; import '../../pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_auth_send.dart'; import '../../providers/db/main_db_provider.dart'; @@ -53,6 +51,9 @@ import '../../widgets/rounded_white_container.dart'; import '../../widgets/stack_dialog.dart'; import '../../widgets/stack_text_field.dart'; import '../../widgets/textfield_icon_button.dart'; +import '../pinpad_views/lock_screen_view.dart'; +import '../wallet_view/wallet_view.dart'; +import 'sub_widgets/sending_transaction_dialog.dart'; class ConfirmTransactionView extends ConsumerStatefulWidget { const ConfirmTransactionView({ @@ -521,9 +522,11 @@ class _ConfirmTransactionViewState SelectableText( ref.watch(pAmountFormatter(coin)).format( amountWithoutChange, - ethContract: ref - .watch(pCurrentTokenWallet) - ?.tokenContract, + ethContract: widget.isTokenTx + ? ref + .watch(pCurrentTokenWallet)! + .tokenContract + : null, ), style: STextStyles.itemSubtitle12(context), textAlign: TextAlign.right, @@ -744,9 +747,11 @@ class _ConfirmTransactionViewState SelectableText( ref.watch(pAmountFormatter(coin)).format( amountWithoutChange, - ethContract: ref - .read(pCurrentTokenWallet) - ?.tokenContract, + ethContract: widget.isTokenTx + ? ref + .watch(pCurrentTokenWallet)! + .tokenContract + : null, ), style: STextStyles .desktopTextExtraExtraSmall( diff --git a/lib/pages/send_view/frost_ms/frost_send_view.dart b/lib/pages/send_view/frost_ms/frost_send_view.dart index a150ac726..3d90a896f 100644 --- a/lib/pages/send_view/frost_ms/frost_send_view.dart +++ b/lib/pages/send_view/frost_ms/frost_send_view.dart @@ -92,7 +92,6 @@ class _FrostSendViewState extends ConsumerState { final txData = await wallet.frostCreateSignConfig( txData: TxData(recipients: recipients), - changeAddress: (await wallet.getCurrentReceivingAddress())!.value, feePerWeight: customFeeRate, ); diff --git a/lib/pages/send_view/frost_ms/recipient.dart b/lib/pages/send_view/frost_ms/recipient.dart index 6d9d9b7c4..ce38153e7 100644 --- a/lib/pages/send_view/frost_ms/recipient.dart +++ b/lib/pages/send_view/frost_ms/recipient.dart @@ -120,6 +120,69 @@ class _RecipientState extends ConsumerState { } } + void _onQrTapped() async { + try { + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed( + const Duration( + milliseconds: 75, + ), + ); + } + + final qrResult = await ref.read(pBarcodeScanner).scan(); + + Logging.instance.log( + "qrResult content: ${qrResult.rawContent}", + level: LogLevel.Info, + ); + + final paymentData = AddressUtils.parsePaymentUri( + qrResult.rawContent, + logging: Logging.instance, + ); + + Logging.instance.log( + "qrResult parsed: $paymentData", + level: LogLevel.Info, + ); + + if (paymentData != null && + paymentData.coin?.uriScheme == widget.coin.uriScheme) { + // auto fill address + + addressController.text = paymentData.address.trim(); + + // autofill amount field + if (paymentData.amount != null) { + final Amount amount = Decimal.parse(paymentData.amount!).toAmount( + fractionDigits: widget.coin.fractionDigits, + ); + amountController.text = + ref.read(pAmountFormatter(widget.coin)).format( + amount, + withUnitName: false, + ); + } + } else { + addressController.text = qrResult.rawContent.trim(); + } + + setState(() { + _addressIsEmpty = addressController.text.isEmpty; + }); + + _updateRecipientData(); + } on PlatformException catch (e, s) { + Logging.instance.log( + "Failed to get camera permissions while " + "trying to scan qr code in SendView: $e\n$s", + level: LogLevel.Warning, + ); + } + } + @override void initState() { addressController = TextEditingController(); @@ -289,76 +352,7 @@ class _RecipientState extends ConsumerState { key: const Key( "sendViewScanQrButtonKey", ), - onTap: () async { - try { - if (FocusScope.of(context).hasFocus) { - FocusScope.of(context).unfocus(); - await Future.delayed( - const Duration( - milliseconds: 75, - ), - ); - } - - final qrResult = - await ref.read(pBarcodeScanner).scan(); - - Logging.instance.log( - "qrResult content: ${qrResult.rawContent}", - level: LogLevel.Info, - ); - - /// TODO: deal with address utils - final results = - AddressUtils.parseUri(qrResult.rawContent); - - Logging.instance.log( - "qrResult parsed: $results", - level: LogLevel.Info, - ); - - if (results.isNotEmpty && - results["scheme"] == - widget.coin.uriScheme) { - // auto fill address - - addressController.text = - (results["address"] ?? "").trim(); - - // autofill amount field - if (results["amount"] != null) { - final Amount amount = - Decimal.parse(results["amount"]!) - .toAmount( - fractionDigits: - widget.coin.fractionDigits, - ); - amountController.text = ref - .read(pAmountFormatter(widget.coin)) - .format( - amount, - withUnitName: false, - ); - } - } else { - addressController.text = - qrResult.rawContent.trim(); - } - - setState(() { - _addressIsEmpty = - addressController.text.isEmpty; - }); - - _updateRecipientData(); - } on PlatformException catch (e, s) { - Logging.instance.log( - "Failed to get camera permissions while " - "trying to scan qr code in SendView: $e\n$s", - level: LogLevel.Warning, - ); - } - }, + onTap: _onQrTapped, child: const QrCodeIcon(), ), ], diff --git a/lib/pages/send_view/frost_ms/send_steps/frost_send_step_1b.dart b/lib/pages/send_view/frost_ms/send_steps/frost_send_step_1b.dart index 058b980c5..b7c95ea2c 100644 --- a/lib/pages/send_view/frost_ms/send_steps/frost_send_step_1b.dart +++ b/lib/pages/send_view/frost_ms/send_steps/frost_send_step_1b.dart @@ -67,6 +67,7 @@ class _FrostSendStep1bState extends ConsumerState { final data = Frost.extractDataFromSignConfig( signConfig: config, coin: wallet.cryptoCurrency, + serializedKeys: (await wallet.getSerializedKeys())!, ); final utxos = await ref diff --git a/lib/pages/send_view/send_view.dart b/lib/pages/send_view/send_view.dart index 60c6799a3..1dd9b68ef 100644 --- a/lib/pages/send_view/send_view.dart +++ b/lib/pages/send_view/send_view.dart @@ -11,7 +11,7 @@ import 'dart:async'; import 'dart:io'; -import 'package:cw_core/monero_transaction_priority.dart'; +import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -163,25 +163,27 @@ class _SendViewState extends ConsumerState { level: LogLevel.Info, ); - final results = AddressUtils.parseUri(qrResult.rawContent); + final paymentData = AddressUtils.parsePaymentUri( + qrResult.rawContent, + logging: Logging.instance, + ); - Logging.instance.log("qrResult parsed: $results", level: LogLevel.Info); - - if (results.isNotEmpty && results["scheme"] == coin.uriScheme) { + if (paymentData != null && + paymentData.coin?.uriScheme == coin.uriScheme) { // auto fill address - _address = (results["address"] ?? "").trim(); + _address = paymentData.address.trim(); sendToController.text = _address!; // autofill notes field - if (results["message"] != null) { - noteController.text = results["message"]!; - } else if (results["label"] != null) { - noteController.text = results["label"]!; + if (paymentData.message != null) { + noteController.text = paymentData.message!; + } else if (paymentData.label != null) { + noteController.text = paymentData.label!; } // autofill amount field - if (results["amount"] != null) { - final Amount amount = Decimal.parse(results["amount"]!).toAmount( + if (paymentData.amount != null) { + final Amount amount = Decimal.parse(paymentData.amount!).toAmount( fractionDigits: coin.fractionDigits, ); cryptoAmountController.text = ref.read(pAmountFormatter(coin)).format( @@ -197,12 +199,8 @@ class _SendViewState extends ConsumerState { }); // now check for non standard encoded basic address - } else if (ref - .read(pWallets) - .getWallet(walletId) - .cryptoCurrency - .validateAddress(qrResult.rawContent)) { - _address = qrResult.rawContent.trim(); + } else { + _address = qrResult.rawContent.split("\n").first.trim(); sendToController.text = _address ?? ""; _setValidAddressProviders(_address); @@ -472,22 +470,22 @@ class _SendViewState extends ConsumerState { Amount fee; if (coin is Monero) { - MoneroTransactionPriority specialMoneroId; + lib_monero.TransactionPriority specialMoneroId; switch (ref.read(feeRateTypeStateProvider.state).state) { case FeeRateType.fast: - specialMoneroId = MoneroTransactionPriority.fast; + specialMoneroId = lib_monero.TransactionPriority.high; break; case FeeRateType.average: - specialMoneroId = MoneroTransactionPriority.regular; + specialMoneroId = lib_monero.TransactionPriority.medium; break; case FeeRateType.slow: - specialMoneroId = MoneroTransactionPriority.slow; + specialMoneroId = lib_monero.TransactionPriority.normal; break; default: throw ArgumentError("custom fee not available for monero"); } - fee = await wallet.estimateFeeFor(amount, specialMoneroId.raw!); + fee = await wallet.estimateFeeFor(amount, specialMoneroId.value); cachedFees[amount] = ref.read(pAmountFormatter(coin)).format( fee, withUnitName: true, @@ -829,7 +827,8 @@ class _SendViewState extends ConsumerState { ), ); } - } catch (e) { + } catch (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Error); if (mounted) { // pop building dialog Navigator.of(context).pop(); @@ -882,6 +881,48 @@ class _SendViewState extends ConsumerState { } } + String _getSendAllTitle(bool showCoinControl, Set selectedUTXOs) { + if (showCoinControl && selectedUTXOs.isNotEmpty) { + return "Send all selected"; + } + + return "Send all ${coin.ticker}"; + } + + Amount _selectedUtxosAmount(Set utxos) => Amount( + rawValue: + utxos.map((e) => BigInt.from(e.value)).reduce((v, e) => v += e), + fractionDigits: ref.read(pWalletCoin(walletId)).fractionDigits, + ); + + Future _sendAllTapped(bool showCoinControl) async { + final Amount amount; + + if (showCoinControl && selectedUTXOs.isNotEmpty) { + amount = _selectedUtxosAmount(selectedUTXOs); + } else if (isFiro) { + switch (ref.read(publicPrivateBalanceStateProvider.state).state) { + case FiroType.public: + amount = ref.read(pWalletBalance(walletId)).spendable; + break; + case FiroType.lelantus: + amount = ref.read(pWalletBalanceSecondary(walletId)).spendable; + break; + case FiroType.spark: + amount = ref.read(pWalletBalanceTertiary(walletId)).spendable; + break; + } + } else { + amount = ref.read(pWalletBalance(walletId)).spendable; + } + + cryptoAmountController.text = ref.read(pAmountFormatter(coin)).format( + amount, + withUnitName: false, + ); + _cryptoAmountChanged(); + } + bool get isPaynymSend => widget.accountLite != null; bool isCustomFee = false; @@ -942,6 +983,9 @@ class _SendViewState extends ConsumerState { if (isPaynymSend) { sendToController.text = widget.accountLite!.nymName; noteController.text = "PayNym send"; + WidgetsBinding.instance.addPostFrameCallback( + (_) => _setValidAddressProviders(sendToController.text), + ); } // if (coin is! Epiccash) { @@ -1067,7 +1111,7 @@ class _SendViewState extends ConsumerState { _address = _address!.substring(0, _address!.indexOf("\n")); } - sendToController.text = formatAddress(_address!); + sendToController.text = AddressUtils().formatAddress(_address!); } }); } @@ -1398,7 +1442,8 @@ class _SendViewState extends ConsumerState { if (coin is Epiccash) { // strip http:// and https:// if content contains @ - content = formatAddress( + content = AddressUtils() + .formatAddress( content, ); } @@ -1769,59 +1814,9 @@ class _SendViewState extends ConsumerState { ), if (coin is! Ethereum && coin is! Tezos) CustomTextButton( - text: "Send all ${coin.ticker}", - onTap: () async { - if (isFiro) { - final Amount amount; - switch (ref - .read( - publicPrivateBalanceStateProvider - .state, - ) - .state) { - case FiroType.public: - amount = ref - .read(pWalletBalance(walletId)) - .spendable; - break; - case FiroType.lelantus: - amount = ref - .read( - pWalletBalanceSecondary( - walletId, - ), - ) - .spendable; - break; - case FiroType.spark: - amount = ref - .read( - pWalletBalanceTertiary( - walletId, - ), - ) - .spendable; - break; - } - - cryptoAmountController.text = ref - .read(pAmountFormatter(coin)) - .format( - amount, - withUnitName: false, - ); - } else { - cryptoAmountController.text = ref - .read(pAmountFormatter(coin)) - .format( - ref - .read(pWalletBalance(walletId)) - .spendable, - withUnitName: false, - ); - } - _cryptoAmountChanged(); - }, + text: _getSendAllTitle( + showCoinControl, selectedUTXOs), + onTap: () => _sendAllTapped(showCoinControl), ), ], ), @@ -2417,22 +2412,3 @@ class _SendViewState extends ConsumerState { ); } } - -String formatAddress(String epicAddress) { - // strip http:// or https:// prefixes if the address contains an @ symbol (and is thus an epicbox address) - if ((epicAddress.startsWith("http://") || - epicAddress.startsWith("https://")) && - epicAddress.contains("@")) { - epicAddress = epicAddress.replaceAll("http://", ""); - epicAddress = epicAddress.replaceAll("https://", ""); - } - // strip mailto: prefix - if (epicAddress.startsWith("mailto:")) { - epicAddress = epicAddress.replaceAll("mailto:", ""); - } - // strip / suffix if the address contains an @ symbol (and is thus an epicbox address) - if (epicAddress.endsWith("/") && epicAddress.contains("@")) { - epicAddress = epicAddress.substring(0, epicAddress.length - 1); - } - return epicAddress; -} diff --git a/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart b/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart index a70098c03..0ca0dba47 100644 --- a/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart +++ b/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart @@ -8,9 +8,10 @@ * */ -import 'package:cw_core/monero_transaction_priority.dart'; import 'package:flutter/material.dart'; +import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../models/paymint/fee_object_model.dart'; import '../../../providers/providers.dart'; import '../../../providers/ui/fee_rate_type_state_provider.dart'; @@ -90,7 +91,7 @@ class _TransactionFeeSelectionSheetState if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.fast.raw!, + lib_monero.TransactionPriority.high.value, ); ref.read(feeSheetSessionCacheProvider).fast[amount] = fee; } else if (coin is Firo) { @@ -127,7 +128,7 @@ class _TransactionFeeSelectionSheetState if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.regular.raw!, + lib_monero.TransactionPriority.medium.value, ); ref.read(feeSheetSessionCacheProvider).average[amount] = fee; } else if (coin is Firo) { @@ -163,7 +164,7 @@ class _TransactionFeeSelectionSheetState if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.slow.raw!, + lib_monero.TransactionPriority.normal.value, ); ref.read(feeSheetSessionCacheProvider).slow[amount] = fee; } else if (coin is Firo) { diff --git a/lib/pages/send_view/token_send_view.dart b/lib/pages/send_view/token_send_view.dart index 61ae1d1a9..4837b9773 100644 --- a/lib/pages/send_view/token_send_view.dart +++ b/lib/pages/send_view/token_send_view.dart @@ -15,12 +15,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; + import '../../models/isar/models/isar_models.dart'; import '../../models/send_view_auto_fill_data.dart'; -import '../address_book_views/address_book_view.dart'; -import 'confirm_transaction_view.dart'; -import 'sub_widgets/building_transaction_dialog.dart'; -import 'sub_widgets/transaction_fee_selection_sheet.dart'; import '../../providers/providers.dart'; import '../../providers/ui/fee_rate_type_state_provider.dart'; import '../../providers/ui/preview_tx_button_state_provider.dart'; @@ -30,6 +27,7 @@ import '../../utilities/address_utils.dart'; import '../../utilities/amount/amount.dart'; import '../../utilities/amount/amount_formatter.dart'; import '../../utilities/amount/amount_input_formatter.dart'; +import '../../utilities/amount/amount_unit.dart'; import '../../utilities/assets.dart'; import '../../utilities/barcode_scanner_interface.dart'; import '../../utilities/clipboard_interface.dart'; @@ -55,6 +53,11 @@ import '../../widgets/icon_widgets/x_icon.dart'; import '../../widgets/stack_dialog.dart'; import '../../widgets/stack_text_field.dart'; import '../../widgets/textfield_icon_button.dart'; +import '../address_book_views/address_book_view.dart'; +import '../token_view/token_view.dart'; +import 'confirm_transaction_view.dart'; +import 'sub_widgets/building_transaction_dialog.dart'; +import 'sub_widgets/transaction_fee_selection_sheet.dart'; class TokenSendView extends ConsumerStatefulWidget { const TokenSendView({ @@ -161,25 +164,30 @@ class _TokenSendViewState extends ConsumerState { level: LogLevel.Info, ); - final results = AddressUtils.parseUri(qrResult.rawContent); + final paymentData = AddressUtils.parsePaymentUri( + qrResult.rawContent, + logging: Logging.instance, + ); - Logging.instance.log("qrResult parsed: $results", level: LogLevel.Info); + Logging.instance + .log("qrResult parsed: $paymentData", level: LogLevel.Info); - if (results.isNotEmpty && results["scheme"] == coin.uriScheme) { + if (paymentData != null && + paymentData.coin?.uriScheme == coin.uriScheme) { // auto fill address - _address = (results["address"] ?? "").trim(); + _address = paymentData.address.trim(); sendToController.text = _address!; // autofill notes field - if (results["message"] != null) { - noteController.text = results["message"]!; - } else if (results["label"] != null) { - noteController.text = results["label"]!; + if (paymentData.message != null) { + noteController.text = paymentData.message!; + } else if (paymentData.label != null) { + noteController.text = paymentData.label!; } // autofill amount field - if (results["amount"] != null) { - final Amount amount = Decimal.parse(results["amount"]!).toAmount( + if (paymentData.amount != null) { + final Amount amount = Decimal.parse(paymentData.amount!).toAmount( fractionDigits: tokenContract.decimals, ); cryptoAmountController.text = ref.read(pAmountFormatter(coin)).format( @@ -196,12 +204,8 @@ class _TokenSendViewState extends ConsumerState { }); // now check for non standard encoded basic address - } else if (ref - .read(pWallets) - .getWallet(walletId) - .cryptoCurrency - .validateAddress(qrResult.rawContent)) { - _address = qrResult.rawContent.trim(); + } else { + _address = qrResult.rawContent.split("\n").first.trim(); sendToController.text = _address ?? ""; _updatePreviewButtonState(_address, _amountToSend); @@ -521,6 +525,7 @@ class _TokenSendViewState extends ConsumerState { walletId: walletId, isTokenTx: true, onSuccess: clearSendForm, + routeOnSuccessName: TokenView.routeName, ), settings: const RouteSettings( name: ConfirmTransactionView.routeName, @@ -529,7 +534,8 @@ class _TokenSendViewState extends ConsumerState { ), ); } - } catch (e) { + } catch (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Error); if (mounted) { // pop building dialog Navigator.of(context).pop(); @@ -1028,7 +1034,9 @@ class _TokenSendViewState extends ConsumerState { child: Padding( padding: const EdgeInsets.all(12), child: Text( - tokenContract.symbol, + ref + .watch(pAmountUnit(coin)) + .unitForContract(tokenContract), style: STextStyles.smallMed14(context) .copyWith( color: Theme.of(context) diff --git a/lib/pages/settings_views/global_settings_view/about_view.dart b/lib/pages/settings_views/global_settings_view/about_view.dart index f4c3eb7ec..b72c3222c 100644 --- a/lib/pages/settings_views/global_settings_view/about_view.dart +++ b/lib/pages/settings_views/global_settings_view/about_view.dart @@ -272,48 +272,48 @@ class AboutView extends ConsumerWidget { const SizedBox( height: 12, ), - if (AppConfig.coins.whereType().isNotEmpty) - FutureBuilder( - future: GitStatus.getMoneroCommitStatus(), - builder: ( - context, - AsyncSnapshot snapshot, - ) { - CommitStatus stateOfCommit = - CommitStatus.notLoaded; - - if (snapshot.connectionState == - ConnectionState.done && - snapshot.hasData) { - stateOfCommit = snapshot.data!; - } - return RoundedWhiteContainer( - child: Column( - crossAxisAlignment: - CrossAxisAlignment.stretch, - children: [ - Text( - "Monero Build Commit", - style: STextStyles.titleBold12(context), - ), - const SizedBox( - height: 4, - ), - SelectableText( - GitStatus.moneroCommit, - style: GitStatus.styleForStatus( - stateOfCommit, - context, - ), - ), - ], - ), - ); - }, - ), - const SizedBox( - height: 12, - ), + // if (AppConfig.coins.whereType().isNotEmpty) + // FutureBuilder( + // future: GitStatus.getMoneroCommitStatus(), + // builder: ( + // context, + // AsyncSnapshot snapshot, + // ) { + // CommitStatus stateOfCommit = + // CommitStatus.notLoaded; + // + // if (snapshot.connectionState == + // ConnectionState.done && + // snapshot.hasData) { + // stateOfCommit = snapshot.data!; + // } + // return RoundedWhiteContainer( + // child: Column( + // crossAxisAlignment: + // CrossAxisAlignment.stretch, + // children: [ + // Text( + // "Monero Build Commit", + // style: STextStyles.titleBold12(context), + // ), + // const SizedBox( + // height: 4, + // ), + // SelectableText( + // GitStatus.moneroCommit, + // style: GitStatus.styleForStatus( + // stateOfCommit, + // context, + // ), + // ), + // ], + // ), + // ); + // }, + // ), + // const SizedBox( + // height: 12, + // ), RoundedWhiteContainer( child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/pages/settings_views/global_settings_view/advanced_views/advanced_settings_view.dart b/lib/pages/settings_views/global_settings_view/advanced_views/advanced_settings_view.dart index e47c9e209..6f04cce80 100644 --- a/lib/pages/settings_views/global_settings_view/advanced_views/advanced_settings_view.dart +++ b/lib/pages/settings_views/global_settings_view/advanced_views/advanced_settings_view.dart @@ -183,6 +183,57 @@ class AdvancedSettingsView extends StatelessWidget { }, ), ), + // showExchange pref. + if (Constants.enableExchange) + const SizedBox( + height: 8, + ), + if (Constants.enableExchange) + RoundedWhiteContainer( + child: Consumer( + builder: (_, ref, __) { + return RawMaterialButton( + // splashColor: Theme.of(context).extension()!.highlight, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + onPressed: null, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Enable exchange features", + style: STextStyles.titleBold12(context), + textAlign: TextAlign.left, + ), + SizedBox( + height: 20, + width: 40, + child: DraggableSwitchButton( + isOn: ref.watch( + prefsChangeNotifierProvider.select( + (value) => value.enableExchange, + ), + ), + onValueChanged: (newValue) { + ref + .read(prefsChangeNotifierProvider) + .enableExchange = newValue; + }, + ), + ), + ], + ), + ), + ); + }, + ), + ), const SizedBox( height: 8, ), diff --git a/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart b/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart index 81297430e..92e5cc256 100644 --- a/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart +++ b/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart @@ -18,7 +18,7 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_libepiccash/git_versions.dart' as EPIC_VERSIONS; -import 'package:flutter_libmonero/git_versions.dart' as MONERO_VERSIONS; +// import 'package:flutter_libmonero/git_versions.dart' as MONERO_VERSIONS; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:lelantus/git_versions.dart' as FIRO_VERSIONS; @@ -313,8 +313,8 @@ class _DebugViewState extends ConsumerState { FIRO_VERSIONS.getPluginVersion(); final String epicCashCommit = EPIC_VERSIONS.getPluginVersion(); - final String moneroCommit = - MONERO_VERSIONS.getPluginVersion(); + // final String moneroCommit = + // MONERO_VERSIONS.getPluginVersion(); final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin(); final deviceInfo = @@ -347,7 +347,7 @@ class _DebugViewState extends ConsumerState { "appName": appName, "firoCommit": firoCommit, "epicCashCommit": epicCashCommit, - "moneroCommit": moneroCommit, + // "moneroCommit": moneroCommit, "deviceInfoMap": deviceInfoMap, "errorLogs": errorLogs, }; diff --git a/lib/pages/settings_views/global_settings_view/advanced_views/manage_coin_units/manage_coin_units_view.dart b/lib/pages/settings_views/global_settings_view/advanced_views/manage_coin_units/manage_coin_units_view.dart index cd2f7d231..28ebe18a9 100644 --- a/lib/pages/settings_views/global_settings_view/advanced_views/manage_coin_units/manage_coin_units_view.dart +++ b/lib/pages/settings_views/global_settings_view/advanced_views/manage_coin_units/manage_coin_units_view.dart @@ -3,9 +3,9 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'edit_coin_units_view.dart'; -import '../../../../../providers/global/prefs_provider.dart'; + import '../../../../../app_config.dart'; +import '../../../../../providers/global/prefs_provider.dart'; import '../../../../../themes/coin_icon_provider.dart'; import '../../../../../themes/stack_colors.dart'; import '../../../../../utilities/assets.dart'; @@ -18,6 +18,7 @@ import '../../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../../widgets/desktop/desktop_dialog.dart'; import '../../../../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../../../../widgets/rounded_white_container.dart'; +import 'edit_coin_units_view.dart'; class ManageCoinUnitsView extends ConsumerWidget { const ManageCoinUnitsView({super.key}); @@ -44,13 +45,9 @@ class ManageCoinUnitsView extends ConsumerWidget { prefsChangeNotifierProvider.select((value) => value.showTestNetCoins), ); - final _coins = AppConfig.coins - .where((e) => e is! Firo && e.network != CryptoCurrencyNetwork.test) - .toList(); - final coins = showTestNet - ? _coins - : _coins.where((e) => e.network != CryptoCurrencyNetwork.test).toList(); + ? AppConfig.coins + : AppConfig.coins.where((e) => !e.network.isTestNet).toList(); return ConditionalParent( condition: Util.isDesktop, 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 478440c99..ccf4bf2ba 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 @@ -18,18 +18,21 @@ import 'package:uuid/uuid.dart'; import '../../../../models/node_model.dart'; import '../../../../notifications/show_flush_bar.dart'; +import '../../../../providers/global/active_wallet_provider.dart'; import '../../../../providers/global/secure_store_provider.dart'; import '../../../../providers/providers.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; import '../../../../utilities/constants.dart'; +import '../../../../utilities/enums/sync_type_enum.dart'; import '../../../../utilities/flutter_secure_storage_interface.dart'; import '../../../../utilities/test_node_connection.dart'; import '../../../../utilities/text_styles.dart'; +import '../../../../utilities/tor_plain_net_option_enum.dart'; import '../../../../utilities/util.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../wallets/crypto_currency/intermediate/cryptonote_currency.dart'; -import '../../../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; +import '../../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../../../widgets/background.dart'; import '../../../../widgets/conditional_parent.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; @@ -222,13 +225,18 @@ class _AddEditNodeViewState extends ConsumerState { // strip unused path String address = formData.host!; - if (coin is CwBasedInterface) { + if (coin is LibMoneroWallet) { if (address.startsWith("http")) { final uri = Uri.parse(address); address = "${uri.scheme}://${uri.host}"; } } + final torEnabled = formData.netOption == TorPlainNetworkOption.tor || + formData.netOption == TorPlainNetworkOption.both; + final plainEnabled = formData.netOption == TorPlainNetworkOption.clear || + formData.netOption == TorPlainNetworkOption.both; + switch (viewType) { case AddEditNodeViewType.add: final NodeModel node = NodeModel( @@ -243,6 +251,8 @@ class _AddEditNodeViewState extends ConsumerState { isFailover: formData.isFailover!, trusted: formData.trusted!, isDown: false, + torEnabled: torEnabled, + clearnetEnabled: plainEnabled, ); await ref.read(nodeServiceChangeNotifierProvider).add( @@ -250,6 +260,7 @@ class _AddEditNodeViewState extends ConsumerState { formData.password, true, ); + await _notifyWalletsOfUpdatedNode(); if (mounted) { Navigator.of(context) .popUntil(ModalRoute.withName(widget.routeOnSuccessOrDelete)); @@ -268,6 +279,8 @@ class _AddEditNodeViewState extends ConsumerState { isFailover: formData.isFailover!, trusted: formData.trusted!, isDown: false, + torEnabled: torEnabled, + clearnetEnabled: plainEnabled, ); await ref.read(nodeServiceChangeNotifierProvider).add( @@ -275,6 +288,7 @@ class _AddEditNodeViewState extends ConsumerState { formData.password, true, ); + await _notifyWalletsOfUpdatedNode(); if (mounted) { Navigator.of(context) .popUntil(ModalRoute.withName(widget.routeOnSuccessOrDelete)); @@ -283,6 +297,39 @@ class _AddEditNodeViewState extends ConsumerState { } } + Future _notifyWalletsOfUpdatedNode() async { + final wallets = + ref.read(pWallets).wallets.where((e) => e.info.coin == widget.coin); + final prefs = ref.read(prefsChangeNotifierProvider); + + switch (prefs.syncType) { + case SyncingType.currentWalletOnly: + for (final wallet in wallets) { + if (ref.read(currentWalletIdProvider) == wallet.walletId) { + unawaited(wallet.updateNode().then((value) => wallet.refresh())); + } else { + unawaited(wallet.updateNode()); + } + } + break; + case SyncingType.selectedWalletsAtStartup: + final List walletIdsToSync = prefs.walletIdsSyncOnStartup; + for (final wallet in wallets) { + if (walletIdsToSync.contains(wallet.walletId)) { + unawaited(wallet.updateNode().then((value) => wallet.refresh())); + } else { + unawaited(wallet.updateNode()); + } + } + break; + case SyncingType.allWalletsOnStartup: + for (final wallet in wallets) { + unawaited(wallet.updateNode().then((value) => wallet.refresh())); + } + break; + } + } + @override void initState() { isDesktop = Util.isDesktop; @@ -422,7 +469,7 @@ class _AddEditNodeViewState extends ConsumerState { condition: isDesktop, builder: (child) => DesktopDialog( maxWidth: 580, - maxHeight: 500, + maxHeight: double.infinity, child: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -568,10 +615,11 @@ class NodeFormData { String? name, host, login, password; int? port; bool? useSSL, isFailover, trusted; + TorPlainNetworkOption? netOption; @override String toString() { - return "{ name: $name, host: $host, port: $port, useSSL: $useSSL, trusted: $trusted }"; + return "{ name: $name, host: $host, port: $port, useSSL: $useSSL, trusted: $trusted, netOption: $netOption }"; } } @@ -615,6 +663,7 @@ class _NodeFormState extends ConsumerState { bool _trusted = false; int? port; late bool enableSSLCheckbox; + late TorPlainNetworkOption netOption; late final bool enableAuthFields; @@ -672,6 +721,7 @@ class _NodeFormState extends ConsumerState { ref.read(nodeFormDataProvider).useSSL = _useSSL; ref.read(nodeFormDataProvider).isFailover = _isFailover; ref.read(nodeFormDataProvider).trusted = _trusted; + ref.read(nodeFormDataProvider).netOption = netOption; } @override @@ -704,6 +754,15 @@ class _NodeFormState extends ConsumerState { _useSSL = node.useSSL; _isFailover = node.isFailover; _trusted = node.trusted ?? false; + + if (node.torEnabled && !node.clearnetEnabled) { + netOption = TorPlainNetworkOption.tor; + } else if (node.clearnetEnabled && !node.torEnabled) { + netOption = TorPlainNetworkOption.clear; + } else { + netOption = TorPlainNetworkOption.both; + } + if (widget.coin is Epiccash) { enableSSLCheckbox = !node.host.startsWith("http"); } else { @@ -716,6 +775,7 @@ class _NodeFormState extends ConsumerState { }); } else { enableSSLCheckbox = true; + netOption = TorPlainNetworkOption.both; // default to port 3413 // _portController.text = "3413"; } @@ -837,7 +897,7 @@ class _NodeFormState extends ConsumerState { } else { enableSSLCheckbox = true; } - } else if (widget.coin is CwBasedInterface) { + } else if (widget.coin is LibMoneroWallet) { if (newValue.startsWith("https://")) { _useSSL = true; } else if (newValue.startsWith("http://")) { @@ -1052,7 +1112,7 @@ class _NodeFormState extends ConsumerState { ), ], ), - if (widget.coin is CwBasedInterface) + if (widget.coin is LibMoneroWallet) Row( children: [ GestureDetector( @@ -1168,7 +1228,145 @@ class _NodeFormState extends ConsumerState { ), ], ), + if (widget.coin is! Ethereum) + const SizedBox( + height: 16, + ), + if (widget.coin is! Ethereum) + Row( + children: [ + RadioTextButton( + label: "Only TOR traffic", + enabled: !widget.readOnly, + value: TorPlainNetworkOption.tor, + groupValue: netOption, + onChanged: (value) { + if (!widget.readOnly) { + setState( + () => netOption = TorPlainNetworkOption.tor, + ); + _updateState(); + } + }, + ), + ], + ), + if (widget.coin is! Ethereum) + const SizedBox( + height: 8, + ), + if (widget.coin is! Ethereum) + Row( + children: [ + RadioTextButton( + label: "Only non-TOR traffic", + enabled: !widget.readOnly, + value: TorPlainNetworkOption.clear, + groupValue: netOption, + onChanged: (value) { + if (!widget.readOnly) { + setState( + () => netOption = TorPlainNetworkOption.clear, + ); + _updateState(); + } + }, + ), + ], + ), + if (widget.coin is! Ethereum) + const SizedBox( + height: 8, + ), + if (widget.coin is! Ethereum) + Row( + children: [ + RadioTextButton( + label: "Allow both", + enabled: !widget.readOnly, + value: TorPlainNetworkOption.both, + groupValue: netOption, + onChanged: (value) { + if (!widget.readOnly) { + setState( + () => netOption = TorPlainNetworkOption.both, + ); + _updateState(); + } + }, + ), + ], + ), ], ); } } + +class RadioTextButton extends StatelessWidget { + const RadioTextButton({ + super.key, + required this.value, + required this.label, + required this.groupValue, + required this.onChanged, + this.enabled = true, + }); + + final T value; + final String label; + final T groupValue; + final bool enabled; + final void Function(T) onChanged; + + @override + Widget build(BuildContext context) { + return ConditionalParent( + condition: Util.isDesktop, + builder: (child) => MouseRegion( + cursor: SystemMouseCursors.click, + child: child, + ), + child: GestureDetector( + onTap: () { + if (value != groupValue) { + onChanged.call(value); + } + }, + child: Container( + color: Colors.transparent, + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 20, + height: 20, + child: Radio( + activeColor: Theme.of(context) + .extension()! + .radioButtonIconEnabled, + value: value, + groupValue: groupValue, + onChanged: !enabled + ? null + : (_) { + if (value != groupValue) { + onChanged.call(value); + } + }, + ), + ), + const SizedBox( + width: 14, + ), + Text( + label, + style: STextStyles.w500_14(context), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/settings_views/global_settings_view/manage_nodes_views/manage_nodes_view.dart b/lib/pages/settings_views/global_settings_view/manage_nodes_views/manage_nodes_view.dart index e44d352f4..a7756f662 100644 --- a/lib/pages/settings_views/global_settings_view/manage_nodes_views/manage_nodes_view.dart +++ b/lib/pages/settings_views/global_settings_view/manage_nodes_views/manage_nodes_view.dart @@ -62,7 +62,7 @@ class _ManageNodesViewState extends ConsumerState { final coins = showTestNet ? _coins - : _coins.where((e) => e.network != CryptoCurrencyNetwork.test).toList(); + : _coins.where((e) => !e.network.isTestNet).toList(); return Background( child: Scaffold( 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 da7f0cfcc..56c87e5c4 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 @@ -13,15 +13,19 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:tuple/tuple.dart'; + import '../../../../notifications/show_flush_bar.dart'; -import 'add_edit_node_view.dart'; +import '../../../../providers/global/active_wallet_provider.dart'; import '../../../../providers/global/secure_store_provider.dart'; import '../../../../providers/providers.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; +import '../../../../utilities/enums/sync_type_enum.dart'; import '../../../../utilities/flutter_secure_storage_interface.dart'; import '../../../../utilities/test_node_connection.dart'; import '../../../../utilities/text_styles.dart'; +import '../../../../utilities/tor_plain_net_option_enum.dart'; import '../../../../utilities/util.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../widgets/background.dart'; @@ -31,7 +35,7 @@ import '../../../../widgets/desktop/delete_button.dart'; import '../../../../widgets/desktop/desktop_dialog.dart'; import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/desktop/secondary_button.dart'; -import 'package:tuple/tuple.dart'; +import 'add_edit_node_view.dart'; class NodeDetailsView extends ConsumerStatefulWidget { const NodeDetailsView({ @@ -59,6 +63,39 @@ class _NodeDetailsViewState extends ConsumerState { bool _desktopReadOnly = true; + Future _notifyWalletsOfUpdatedNode() async { + final wallets = + ref.read(pWallets).wallets.where((e) => e.info.coin == widget.coin); + final prefs = ref.read(prefsChangeNotifierProvider); + + switch (prefs.syncType) { + case SyncingType.currentWalletOnly: + for (final wallet in wallets) { + if (ref.read(currentWalletIdProvider) == wallet.walletId) { + unawaited(wallet.updateNode().then((value) => wallet.refresh())); + } else { + unawaited(wallet.updateNode()); + } + } + break; + case SyncingType.selectedWalletsAtStartup: + final List walletIdsToSync = prefs.walletIdsSyncOnStartup; + for (final wallet in wallets) { + if (walletIdsToSync.contains(wallet.walletId)) { + unawaited(wallet.updateNode().then((value) => wallet.refresh())); + } else { + unawaited(wallet.updateNode()); + } + } + break; + case SyncingType.allWalletsOnStartup: + for (final wallet in wallets) { + unawaited(wallet.updateNode().then((value) => wallet.refresh())); + } + break; + } + } + @override initState() { secureStore = ref.read(secureStoreProvider); @@ -265,6 +302,16 @@ class _NodeDetailsViewState extends ConsumerState { .read(nodeServiceChangeNotifierProvider) .getNodeById(id: nodeId)!; + final TorPlainNetworkOption netOption; + if (ref.read(nodeFormDataProvider).netOption != null) { + netOption = ref.read(nodeFormDataProvider).netOption!; + } else { + netOption = TorPlainNetworkOption.fromNodeData( + node.torEnabled, + node.clearnetEnabled, + ); + } + final nodeFormData = NodeFormData() ..useSSL = node.useSSL ..trusted = node.trusted @@ -272,7 +319,8 @@ class _NodeDetailsViewState extends ConsumerState { ..host = node.host ..login = node.loginName ..port = node.port - ..isFailover = node.isFailover; + ..isFailover = node.isFailover + ..netOption = netOption; nodeFormData.password = await node.getPassword( ref.read(secureStoreProvider), ); @@ -338,6 +386,16 @@ class _NodeDetailsViewState extends ConsumerState { loginName: ref.read(nodeFormDataProvider).login, isFailover: ref.read(nodeFormDataProvider).isFailover, + torEnabled: + ref.read(nodeFormDataProvider).netOption == + TorPlainNetworkOption.tor || + ref.read(nodeFormDataProvider).netOption == + TorPlainNetworkOption.both, + clearnetEnabled: + ref.read(nodeFormDataProvider).netOption == + TorPlainNetworkOption.clear || + ref.read(nodeFormDataProvider).netOption == + TorPlainNetworkOption.both, ); await ref @@ -347,6 +405,7 @@ class _NodeDetailsViewState extends ConsumerState { ref.read(nodeFormDataProvider).password, true, ); + await _notifyWalletsOfUpdatedNode(); } }, ) diff --git a/lib/pages/settings_views/global_settings_view/security_views/change_pin_view/change_pin_view.dart b/lib/pages/settings_views/global_settings_view/security_views/change_pin_view/change_pin_view.dart index 4c4bfa66c..57fa710ad 100644 --- a/lib/pages/settings_views/global_settings_view/security_views/change_pin_view/change_pin_view.dart +++ b/lib/pages/settings_views/global_settings_view/security_views/change_pin_view/change_pin_view.dart @@ -61,12 +61,9 @@ class _ChangePinViewState extends ConsumerState { int pinCount = 1; - final TextEditingController _pinTextController = TextEditingController(); - @override void initState() { _secureStore = ref.read(secureStoreProvider); - _pinTextController.addListener(_onPinChanged); super.initState(); } @@ -77,23 +74,9 @@ class _ChangePinViewState extends ConsumerState { _pinPutController2.dispose(); _pinPutFocusNode1.dispose(); _pinPutFocusNode2.dispose(); - _pinTextController.removeListener(_onPinChanged); super.dispose(); } - void _onPinChanged() async { - String enteredPin = _pinTextController.text; - final storedPin = await _secureStore.read(key: 'stack_pin'); - final autoPin = ref.read(prefsChangeNotifierProvider).autoPin; - - if (enteredPin.length >= 4 && autoPin && enteredPin == storedPin) { - await _pageController.nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.linear, - ); - } - } - @override Widget build(BuildContext context) { return Background( diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart index 3dcdd45d5..d20faf66b 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart @@ -18,7 +18,7 @@ import 'package:isar/isar.dart'; import 'package:stack_wallet_backup/stack_wallet_backup.dart'; import 'package:tuple/tuple.dart'; import 'package:uuid/uuid.dart'; -import 'package:wakelock/wakelock.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; import '../../../../../app_config.dart'; import '../../../../../db/hive/db.dart'; @@ -27,6 +27,7 @@ import '../../../../../models/exchange/change_now/exchange_transaction.dart'; import '../../../../../models/exchange/response_objects/trade.dart'; import '../../../../../models/isar/models/contact_entry.dart'; import '../../../../../models/isar/models/transaction_note.dart'; +import '../../../../../models/keys/view_only_wallet_data.dart'; import '../../../../../models/node_model.dart'; import '../../../../../models/stack_restoring_ui_state.dart'; import '../../../../../models/trade_wallet_lookup.dart'; @@ -52,10 +53,13 @@ import '../../../../../wallets/isar/models/frost_wallet_info.dart'; import '../../../../../wallets/isar/models/wallet_info.dart'; import '../../../../../wallets/wallet/impl/bitcoin_frost_wallet.dart'; import '../../../../../wallets/wallet/impl/epiccash_wallet.dart'; +import '../../../../../wallets/wallet/impl/monero_wallet.dart'; +import '../../../../../wallets/wallet/impl/wownero_wallet.dart'; +import '../../../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../../../../wallets/wallet/wallet.dart'; -import '../../../../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/private_key_interface.dart'; +import '../../../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; class PreRestoreState { final Set walletIds; @@ -310,7 +314,10 @@ abstract class SWB { backupWallet['isFavorite'] = wallet.info.isFavourite; backupWallet['otherDataJsonString'] = wallet.info.otherDataJsonString; - if (wallet is MnemonicInterface) { + if (wallet is ViewOnlyOptionInterface && wallet.isViewOnly) { + backupWallet['viewOnlyWalletDataKey'] = + (await wallet.getViewOnlyWalletData()).toJsonEncodedString(); + } else if (wallet is MnemonicInterface) { backupWallet['mnemonic'] = await wallet.getMnemonic(); backupWallet['mnemonicPassphrase'] = await wallet.getMnemonicPassphrase(); @@ -417,7 +424,16 @@ abstract class SWB { String? mnemonic, mnemonicPassphrase, privateKey; - if (walletbackup['mnemonic'] == null) { + ViewOnlyWalletData? viewOnlyData; + if (info.isViewOnly) { + final viewOnlyDataEncoded = + walletbackup['viewOnlyWalletDataKey'] as String; + + viewOnlyData = ViewOnlyWalletData.fromJsonEncodedString( + viewOnlyDataEncoded, + walletId: info.walletId, + ); + } else if (walletbackup['mnemonic'] == null) { // probably private key based if (walletbackup['privateKey'] != null) { privateKey = walletbackup['privateKey'] as String; @@ -484,27 +500,44 @@ abstract class SWB { mnemonic: mnemonic, mnemonicPassphrase: mnemonicPassphrase, privateKey: privateKey, + viewOnlyData: viewOnlyData, ); - await wallet.init(); + switch (wallet.runtimeType) { + case const (EpiccashWallet): + await (wallet as EpiccashWallet).init(isRestore: true); + break; + + case const (MoneroWallet): + await (wallet as MoneroWallet).init(isRestore: true); + break; + + case const (WowneroWallet): + await (wallet as WowneroWallet).init(isRestore: true); + break; + + default: + await wallet.init(); + } int restoreHeight = walletbackup['restoreHeight'] as int? ?? 0; if (restoreHeight <= 0) { - restoreHeight = walletbackup['storedChainHeight'] as int? ?? 0; + if (wallet is EpiccashWallet || wallet is LibMoneroWallet) { + restoreHeight = 0; + } else { + restoreHeight = walletbackup['storedChainHeight'] as int? ?? 0; + } } - Future? restoringFuture; - - if (!(wallet is CwBasedInterface || wallet is EpiccashWallet)) { - if (wallet is BitcoinFrostWallet) { - restoringFuture = wallet.recover( - isRescan: false, - multisigConfig: multisigConfig!, - serializedKeys: serializedKeys!, - ); - } else { - restoringFuture = wallet.recover(isRescan: false); - } + final Future? restoringFuture; + if (wallet is BitcoinFrostWallet) { + restoringFuture = wallet.recover( + isRescan: false, + multisigConfig: multisigConfig!, + serializedKeys: serializedKeys!, + ); + } else { + restoringFuture = wallet.recover(isRescan: false); } uiState?.update( @@ -692,7 +725,7 @@ abstract class SWB { StackRestoringUIState? uiState, SecureStorageInterface secureStorageInterface, ) async { - if (!Platform.isLinux) await Wakelock.enable(); + if (!Platform.isLinux) await WakelockPlus.enable(); Logging.instance.log( "SWB creating temp backup", @@ -906,7 +939,7 @@ abstract class SWB { await status; } - if (!Platform.isLinux) await Wakelock.disable(); + if (!Platform.isLinux) await WakelockPlus.disable(); // check if cancel was requested and restore previous state if (_checkShouldCancel( preRestoreState, @@ -1249,6 +1282,8 @@ abstract class SWB { loginName: node['loginName'] as String?, isFailover: node['isFailover'] as bool, isDown: node['isDown'] as bool, + torEnabled: node['torEnabled'] as bool? ?? true, + clearnetEnabled: node['plainEnabled'] as bool? ?? true, ), node["password"] as String?, true, diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/swb_file_system.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/swb_file_system.dart index 7282a802a..001279117 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/swb_file_system.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/swb_file_system.dart @@ -82,17 +82,20 @@ class SWBFileSystem { } Future pickDir(BuildContext context) async { - final String? path; + final String? chosenPath; if (Platform.isIOS) { - path = startPath?.path; + chosenPath = startPath?.path; } else { - path = await FilePicker.platform.getDirectoryPath( + final String path = Platform.isWindows + ? startPath!.path.replaceAll("/", "\\") + : startPath!.path; + chosenPath = await FilePicker.platform.getDirectoryPath( dialogTitle: "Choose Backup location", - initialDirectory: startPath!.path, + initialDirectory: path, lockParentWindow: true, ); } - dirPath = path; + dirPath = chosenPath; } Future openFile(BuildContext context) async { diff --git a/lib/pages/settings_views/sub_widgets/view_only_wallet_data_widget.dart b/lib/pages/settings_views/sub_widgets/view_only_wallet_data_widget.dart new file mode 100644 index 000000000..265986087 --- /dev/null +++ b/lib/pages/settings_views/sub_widgets/view_only_wallet_data_widget.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; + +import '../../../models/keys/view_only_wallet_data.dart'; +import '../../../utilities/util.dart'; +import '../../../widgets/custom_buttons/simple_copy_button.dart'; +import '../../../widgets/detail_item.dart'; +import '../../wallet_view/transaction_views/transaction_details_view.dart'; + +class ViewOnlyWalletDataWidget extends StatelessWidget { + const ViewOnlyWalletDataWidget({ + super.key, + required this.data, + }); + + final ViewOnlyWalletData data; + + @override + Widget build(BuildContext context) { + return switch (data) { + final CryptonoteViewOnlyWalletData e => Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + DetailItem( + title: "Address", + detail: e.address, + button: Util.isDesktop + ? IconCopyButton( + data: e.address, + ) + : SimpleCopyButton( + data: e.address, + ), + ), + const SizedBox( + height: 16, + ), + DetailItem( + title: "Private view key", + detail: e.privateViewKey, + button: Util.isDesktop + ? IconCopyButton( + data: e.privateViewKey, + ) + : SimpleCopyButton( + data: e.privateViewKey, + ), + ), + ], + ), + final AddressViewOnlyWalletData e => Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + DetailItem( + title: "Address", + detail: e.address, + button: Util.isDesktop + ? IconCopyButton( + data: e.address, + ) + : SimpleCopyButton( + data: e.address, + ), + ), + ], + ), + final ExtendedKeysViewOnlyWalletData e => Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + ...e.xPubs.map( + (xPub) => DetailItem( + title: xPub.path, + detail: xPub.encoded, + button: Util.isDesktop + ? IconCopyButton( + data: xPub.encoded, + ) + : SimpleCopyButton( + data: xPub.encoded, + ), + ), + ), + ], + ), + }; + } +} diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart index 95538ed10..75b8ca87b 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart @@ -17,6 +17,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../app_config.dart'; import '../../../../models/keys/cw_key_data.dart'; import '../../../../models/keys/key_data_interface.dart'; +import '../../../../models/keys/view_only_wallet_data.dart'; import '../../../../models/keys/xpriv_data.dart'; import '../../../../notifications/show_flush_bar.dart'; import '../../../../themes/stack_colors.dart'; @@ -39,6 +40,7 @@ import '../../../../widgets/rounded_white_container.dart'; import '../../../../widgets/stack_dialog.dart'; import '../../../add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart'; import '../../../wallet_view/transaction_views/transaction_details_view.dart'; +import '../../sub_widgets/view_only_wallet_data_widget.dart'; import 'cn_wallet_keys.dart'; import 'wallet_xprivs.dart'; @@ -87,9 +89,10 @@ class WalletBackupView extends ConsumerWidget { Padding( padding: const EdgeInsets.all(10), child: CustomTextButton( - text: switch (keyData.runtimeType) { - const (XPrivData) => "xpriv(s)", - const (CWKeyData) => "keys", + text: switch (keyData) { + final XPrivData _ => "xpriv(s)", + final CWKeyData _ => "keys", + final ViewOnlyWalletData _ => "keys", _ => throw UnimplementedError( "Don't forget to add your KeyDataInterface here! ${keyData.runtimeType}", ), @@ -426,7 +429,7 @@ class _FrostKeys extends StatelessWidget { } } -class MobileKeyDataView extends StatelessWidget { +class MobileKeyDataView extends ConsumerWidget { const MobileKeyDataView({ super.key, required this.walletId, @@ -441,7 +444,7 @@ class MobileKeyDataView extends StatelessWidget { final KeyDataInterface keyData; @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { return Background( child: Scaffold( backgroundColor: Theme.of(context).extension()!.background, @@ -452,9 +455,10 @@ class MobileKeyDataView extends StatelessWidget { }, ), title: Text( - "Wallet ${switch (keyData.runtimeType) { - const (XPrivData) => "xpriv(s)", - const (CWKeyData) => "keys", + "Wallet ${switch (keyData) { + final XPrivData _ => "xpriv(s)", + final CWKeyData _ => "keys", + final ViewOnlyWalletData _ => "keys", _ => throw UnimplementedError( "Don't forget to add your KeyDataInterface here!", ), @@ -474,14 +478,18 @@ class MobileKeyDataView extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Expanded( - child: switch (keyData.runtimeType) { - const (XPrivData) => WalletXPrivs( + child: switch (keyData) { + final XPrivData e => WalletXPrivs( walletId: walletId, - xprivData: keyData as XPrivData, + xprivData: e, ), - const (CWKeyData) => CNWalletKeys( + final CWKeyData e => CNWalletKeys( walletId: walletId, - cwKeyData: keyData as CWKeyData, + cwKeyData: e, + ), + final ViewOnlyWalletData e => + ViewOnlyWalletDataWidget( + data: e, ), _ => throw UnimplementedError( "Don't forget to add your KeyDataInterface here!", diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_xprivs.dart b/lib/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_xprivs.dart index 594db33a7..264032961 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_xprivs.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_xprivs.dart @@ -49,7 +49,7 @@ class WalletXPrivsState extends ConsumerState { late String _currentDropDownValue; String _current(String key) => - widget.xprivData.xprivs.firstWhere((e) => e.path == key).xpriv; + widget.xprivData.xprivs.firstWhere((e) => e.path == key).encoded; Future _copy() async { await widget.clipboardInterface.setData( diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart index e15fad011..1be577f1d 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart @@ -16,7 +16,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:tuple/tuple.dart'; -import 'package:wakelock/wakelock.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; import '../../../../providers/providers.dart'; import '../../../../route_generator.dart'; @@ -125,7 +125,7 @@ class _WalletNetworkSettingsViewState } Future _attemptRescan() async { - if (!Platform.isLinux) await Wakelock.enable(); + if (!Platform.isLinux) await WakelockPlus.enable(); try { if (mounted) { @@ -180,7 +180,7 @@ class _WalletNetworkSettingsViewState ); } } catch (e) { - if (!Platform.isLinux) await Wakelock.disable(); + if (!Platform.isLinux) await WakelockPlus.disable(); if (mounted) { // pop rescanning dialog @@ -212,7 +212,7 @@ class _WalletNetworkSettingsViewState } } } finally { - if (!Platform.isLinux) await Wakelock.disable(); + if (!Platform.isLinux) await WakelockPlus.disable(); } } diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart index 6985bb00c..4948876bd 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart @@ -19,6 +19,7 @@ import '../../../db/hive/db.dart'; import '../../../db/sqlite/firo_cache.dart'; import '../../../models/epicbox_config_model.dart'; import '../../../models/keys/key_data_interface.dart'; +import '../../../models/keys/view_only_wallet_data.dart'; import '../../../notifications/show_flush_bar.dart'; import '../../../providers/global/wallets_provider.dart'; import '../../../providers/ui/transaction_filter_provider.dart'; @@ -36,9 +37,10 @@ import '../../../wallets/crypto_currency/intermediate/frost_currency.dart'; import '../../../wallets/crypto_currency/intermediate/nano_currency.dart'; import '../../../wallets/wallet/impl/bitcoin_frost_wallet.dart'; import '../../../wallets/wallet/impl/epiccash_wallet.dart'; -import '../../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; +import '../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart'; import '../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; +import '../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; import '../../../widgets/background.dart'; import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../widgets/desktop/secondary_button.dart'; @@ -98,8 +100,13 @@ class _WalletSettingsViewState extends ConsumerState { void initState() { walletId = widget.walletId; coin = widget.coin; - xPubEnabled = - ref.read(pWallets).getWallet(walletId) is ExtendedKeysInterface; + + final wallet = ref.read(pWallets).getWallet(walletId); + if (wallet is ViewOnlyOptionInterface && wallet.isViewOnly) { + xPubEnabled = false; + } else { + xPubEnabled = wallet is ExtendedKeysInterface; + } xpub = ""; @@ -165,6 +172,15 @@ class _WalletSettingsViewState extends ConsumerState { @override Widget build(BuildContext context) { debugPrint("BUILD: $runtimeType"); + final wallet = ref.read(pWallets).getWallet(widget.walletId); + + bool canBackup = true; + if (wallet is ViewOnlyOptionInterface && + wallet.isViewOnly && + wallet.viewOnlyType == ViewOnlyWalletType.addressOnly) { + canBackup = false; + } + return Background( child: Scaffold( backgroundColor: Theme.of(context).extension()!.background, @@ -247,106 +263,155 @@ class _WalletSettingsViewState extends ConsumerState { ); }, ), - const SizedBox( - height: 8, - ), - Consumer( - builder: (_, ref, __) { - return SettingsListButton( - iconAssetName: Assets.svg.lock, - iconSize: 16, - title: "Wallet backup", - onPressed: () async { - final wallet = ref - .read(pWallets) - .getWallet(widget.walletId); + if (canBackup) + const SizedBox( + height: 8, + ), + if (canBackup) + Consumer( + builder: (_, ref, __) { + return SettingsListButton( + iconAssetName: Assets.svg.lock, + iconSize: 16, + title: "Wallet backup", + onPressed: () async { + // TODO: [prio=med] take wallets that don't have a mnemonic into account - // TODO: [prio=med] take wallets that don't have a mnemonic into account - - List? mnemonic; - ({ - String myName, - String config, - String keys, + List? mnemonic; ({ + String myName, String config, - String keys - })? prevGen, - })? frostWalletData; - if (wallet is BitcoinFrostWallet) { - final futures = [ - wallet.getSerializedKeys(), - wallet.getMultisigConfig(), - wallet.getSerializedKeysPrevGen(), - wallet.getMultisigConfigPrevGen(), - ]; + String keys, + ({ + String config, + String keys + })? prevGen, + })? frostWalletData; + if (wallet is BitcoinFrostWallet) { + final futures = [ + wallet.getSerializedKeys(), + wallet.getMultisigConfig(), + wallet.getSerializedKeysPrevGen(), + wallet.getMultisigConfigPrevGen(), + ]; - final results = - await Future.wait(futures); + final results = + await Future.wait(futures); - if (results.length == 4) { - frostWalletData = ( - myName: wallet.frostInfo.myName, - config: results[1]!, - keys: results[0]!, - prevGen: results[2] == null || - results[3] == null - ? null - : ( - config: results[3]!, - keys: results[2]!, - ), - ); + if (results.length == 4) { + frostWalletData = ( + myName: wallet.frostInfo.myName, + config: results[1]!, + keys: results[0]!, + prevGen: results[2] == null || + results[3] == null + ? null + : ( + config: results[3]!, + keys: results[2]!, + ), + ); + } + } else { + if (wallet is MnemonicInterface) { + if (wallet + is ViewOnlyOptionInterface && + !(wallet + as ViewOnlyOptionInterface) + .isViewOnly) { + mnemonic = await wallet + .getMnemonicAsWords(); + } + } } - } else if (wallet - is MnemonicInterface) { - mnemonic = - await wallet.getMnemonicAsWords(); - } - KeyDataInterface? keyData; - if (wallet is ExtendedKeysInterface) { - keyData = await wallet.getXPrivs(); - } else if (wallet is CwBasedInterface) { - keyData = await wallet.getKeys(); - } + KeyDataInterface? keyData; + if (wallet + is ViewOnlyOptionInterface && + wallet.isViewOnly) { + keyData = await wallet + .getViewOnlyWalletData(); + } else if (wallet + is ExtendedKeysInterface) { + keyData = await wallet.getXPrivs(); + } else if (wallet + is LibMoneroWallet) { + keyData = await wallet.getKeys(); + } - if (context.mounted) { - await Navigator.push( - context, - RouteGenerator.getRoute( - shouldUseMaterialRoute: - RouteGenerator - .useMaterialPageRoute, - builder: (_) => LockscreenView( - routeOnSuccessArguments: ( - walletId: walletId, - mnemonic: mnemonic ?? [], - frostWalletData: - frostWalletData, - keyData: keyData, + if (context.mounted) { + if (keyData != null && + wallet + is ViewOnlyOptionInterface && + wallet.isViewOnly) { + await Navigator.push( + context, + RouteGenerator.getRoute( + shouldUseMaterialRoute: + RouteGenerator + .useMaterialPageRoute, + builder: (_) => + LockscreenView( + routeOnSuccessArguments: ( + walletId: walletId, + keyData: keyData, + ), + showBackButton: true, + routeOnSuccess: + MobileKeyDataView + .routeName, + biometricsCancelButtonString: + "CANCEL", + biometricsLocalizedReason: + "Authenticate to view recovery data", + biometricsAuthenticationTitle: + "View recovery data", + ), + settings: const RouteSettings( + name: + "/viewRecoveryDataLockscreen", + ), ), - showBackButton: true, - routeOnSuccess: - WalletBackupView.routeName, - biometricsCancelButtonString: - "CANCEL", - biometricsLocalizedReason: - "Authenticate to view recovery phrase", - biometricsAuthenticationTitle: - "View recovery phrase", - ), - settings: const RouteSettings( - name: - "/viewRecoverPhraseLockscreen", - ), - ), - ); - } - }, - ); - }, - ), + ); + } else { + await Navigator.push( + context, + RouteGenerator.getRoute( + shouldUseMaterialRoute: + RouteGenerator + .useMaterialPageRoute, + builder: (_) => + LockscreenView( + routeOnSuccessArguments: ( + walletId: walletId, + mnemonic: mnemonic ?? [], + frostWalletData: + frostWalletData, + keyData: keyData, + ), + showBackButton: true, + routeOnSuccess: + WalletBackupView + .routeName, + biometricsCancelButtonString: + "CANCEL", + biometricsLocalizedReason: + "Authenticate to view recovery phrase", + biometricsAuthenticationTitle: + "View recovery phrase", + ), + settings: const RouteSettings( + name: + "/viewRecoverPhraseLockscreen", + ), + ), + ); + } + } + }, + ); + }, + ), const SizedBox( height: 8, ), diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_view_only_wallet_keys_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_view_only_wallet_keys_view.dart new file mode 100644 index 000000000..d2d2bef2a --- /dev/null +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_view_only_wallet_keys_view.dart @@ -0,0 +1,177 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../../app_config.dart'; +import '../../../../models/keys/view_only_wallet_data.dart'; +import '../../../../pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/delete_wallet_keys_popup.dart'; +import '../../../../providers/global/secure_store_provider.dart'; +import '../../../../providers/global/wallets_provider.dart'; +import '../../../../route_generator.dart'; +import '../../../../themes/stack_colors.dart'; +import '../../../../utilities/text_styles.dart'; +import '../../../../utilities/util.dart'; +import '../../../../wallets/isar/providers/wallet_info_provider.dart'; +import '../../../../widgets/conditional_parent.dart'; +import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; +import '../../../../widgets/desktop/primary_button.dart'; +import '../../../../widgets/rounded_white_container.dart'; +import '../../../../widgets/stack_dialog.dart'; +import '../../../home_view/home_view.dart'; +import '../../sub_widgets/view_only_wallet_data_widget.dart'; + +class DeleteViewOnlyWalletKeysView extends ConsumerStatefulWidget { + const DeleteViewOnlyWalletKeysView({ + super.key, + required this.walletId, + required this.data, + }); + + static const routeName = "/deleteWalletViewOnlyData"; + + final String walletId; + final ViewOnlyWalletData data; + + @override + ConsumerState createState() => + _DeleteViewOnlyWalletKeysViewState(); +} + +class _DeleteViewOnlyWalletKeysViewState + extends ConsumerState { + bool _lock = false; + void _continuePressed() async { + if (_lock) { + return; + } + _lock = true; + try { + if (Util.isDesktop) { + await Navigator.of(context).push( + RouteGenerator.getRoute( + builder: (context) { + return ConfirmDelete( + walletId: widget.walletId, + ); + }, + settings: const RouteSettings( + name: "/desktopConfirmDelete", + ), + ), + ); + } else { + await showDialog( + barrierDismissible: true, + context: context, + builder: (_) => StackDialog( + title: "Thanks! Your wallet will be deleted.", + leftButton: TextButton( + style: Theme.of(context) + .extension()! + .getSecondaryEnabledButtonStyle(context), + onPressed: () { + Navigator.pop(context); + }, + child: Text( + "Cancel", + style: STextStyles.button(context).copyWith( + color: Theme.of(context) + .extension()! + .accentColorDark, + ), + ), + ), + rightButton: TextButton( + style: Theme.of(context) + .extension()! + .getPrimaryEnabledButtonStyle(context), + onPressed: () async { + await ref.read(pWallets).deleteWallet( + ref.read(pWalletInfo(widget.walletId)), + ref.read(secureStoreProvider), + ); + + if (mounted) { + Navigator.of(context).popUntil( + ModalRoute.withName(HomeView.routeName), + ); + } + }, + child: Text( + "Ok", + style: STextStyles.button(context), + ), + ), + ), + ); + } + } finally { + _lock = false; + } + } + + @override + Widget build(BuildContext context) { + return ConditionalParent( + condition: !Util.isDesktop, + builder: (child) => Scaffold( + backgroundColor: Theme.of(context).extension()!.background, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + body: SafeArea( + child: LayoutBuilder( + builder: (context, cons) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints(minHeight: cons.maxHeight), + child: IntrinsicHeight( + child: Padding( + padding: const EdgeInsets.all(16), + child: child, + ), + ), + ), + ); + }, + ), + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + RoundedWhiteContainer( + child: Text( + "Please write down your backup data. Keep it safe and " + "never share it with anyone. " + "Your backup data is the only way you can access your " + "wallet if you forget your PIN, lose your phone, etc." + "\n\n" + "${AppConfig.appName} does not keep nor is able to restore " + "your backup data. " + "Only you have access to your wallet.", + style: STextStyles.label(context), + ), + ), + const SizedBox( + height: 24, + ), + ViewOnlyWalletDataWidget( + data: widget.data, + ), + if (!Util.isDesktop) const Spacer(), + const SizedBox( + height: 16, + ), + PrimaryButton( + label: "Continue", + onPressed: _continuePressed, + ), + ], + ), + ); + } +} diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_recovery_phrase_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_recovery_phrase_view.dart index f418ed230..16c48cbb6 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_recovery_phrase_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_recovery_phrase_view.dart @@ -14,11 +14,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../../app_config.dart'; import '../../../../notifications/show_flush_bar.dart'; -import '../../../add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart'; -import '../../../home_view/home_view.dart'; -import '../../../wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart'; import '../../../../providers/global/secure_store_provider.dart'; import '../../../../providers/global/wallets_provider.dart'; import '../../../../themes/stack_colors.dart'; @@ -35,6 +33,9 @@ import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/detail_item.dart'; import '../../../../widgets/rounded_white_container.dart'; import '../../../../widgets/stack_dialog.dart'; +import '../../../add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart'; +import '../../../home_view/home_view.dart'; +import '../../../wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart'; class DeleteWalletRecoveryPhraseView extends ConsumerStatefulWidget { const DeleteWalletRecoveryPhraseView({ @@ -69,7 +70,6 @@ class _DeleteWalletRecoveryPhraseViewState late ClipboardInterface _clipboardInterface; bool _lock = false; - void _continuePressed() { if (_lock) { return; diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_warning_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_warning_view.dart index afb5137b4..39a723d66 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_warning_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_warning_view.dart @@ -12,14 +12,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../app_config.dart'; +import '../../../../models/keys/view_only_wallet_data.dart'; import '../../../../providers/providers.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../wallets/wallet/impl/bitcoin_frost_wallet.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; +import '../../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; import '../../../../widgets/background.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/rounded_container.dart'; +import 'delete_view_only_wallet_keys_view.dart'; import 'delete_wallet_recovery_phrase_view.dart'; class DeleteWalletWarningView extends ConsumerWidget { @@ -118,6 +121,7 @@ class DeleteWalletWarningView extends ConsumerWidget { String keys, ({String config, String keys})? prevGen, })? frostWalletData; + ViewOnlyWalletData? viewOnlyData; if (wallet is BitcoinFrostWallet) { final futures = [ @@ -142,18 +146,33 @@ class DeleteWalletWarningView extends ConsumerWidget { ), ); } - } else if (wallet is MnemonicInterface) { - mnemonic = await wallet.getMnemonicAsWords(); + } else { + if (wallet is ViewOnlyOptionInterface && + wallet.isViewOnly) { + viewOnlyData = await wallet.getViewOnlyWalletData(); + } else if (wallet is MnemonicInterface) { + mnemonic = await wallet.getMnemonicAsWords(); + } } if (context.mounted) { - await Navigator.of(context).pushNamed( - DeleteWalletRecoveryPhraseView.routeName, - arguments: ( - walletId: walletId, - mnemonicWords: mnemonic ?? [], - frostWalletData: frostWalletData, - ), - ); + if (viewOnlyData != null) { + await Navigator.of(context).pushNamed( + DeleteViewOnlyWalletKeysView.routeName, + arguments: ( + walletId: walletId, + data: viewOnlyData, + ), + ); + } else { + await Navigator.of(context).pushNamed( + DeleteWalletRecoveryPhraseView.routeName, + arguments: ( + walletId: walletId, + mnemonicWords: mnemonic ?? [], + frostWalletData: frostWalletData, + ), + ); + } } }, child: Text( diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart index cb572a72c..bf41d6261 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart @@ -11,19 +11,20 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../../../models/keys/view_only_wallet_data.dart'; import '../../../../providers/db/main_db_provider.dart'; import '../../../../providers/providers.dart'; import '../../../../route_generator.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/constants.dart'; import '../../../../utilities/text_styles.dart'; -import '../../../../utilities/util.dart'; import '../../../../wallets/isar/models/wallet_info.dart'; import '../../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/multi_address_interface.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/rbf_interface.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/spark_interface.dart'; +import '../../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; import '../../../../widgets/background.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/custom_buttons/draggable_switch_button.dart'; @@ -53,82 +54,93 @@ class WalletSettingsWalletSettingsView extends ConsumerStatefulWidget { class _WalletSettingsWalletSettingsViewState extends ConsumerState { - bool _switchReuseAddressToggledLock = false; // Mutex. - Future _switchReuseAddressToggled(bool newValue) async { - if (newValue) { - await showDialog( - context: context, - builder: (context) { - final isDesktop = Util.isDesktop; - return StackDialog( - title: "Warning!", - message: - "Reusing addresses reduces your privacy and security. Are you sure you want to reuse addresses by default?", - leftButton: TextButton( - style: Theme.of(context) - .extension()! - .getSecondaryEnabledButtonStyle(context), - child: Text( - "Cancel", - style: STextStyles.itemSubtitle12(context), - ), - onPressed: () { - Navigator.of(context).pop(false); - }, - ), - rightButton: TextButton( - style: Theme.of(context) - .extension()! - .getPrimaryEnabledButtonStyle(context), - child: Text( - "Continue", - style: STextStyles.button(context), - ), - onPressed: () { - Navigator.of(context).pop(true); - }, - ), - ); - }, - ).then((confirmed) async { - if (_switchReuseAddressToggledLock) { - return; - } - _switchReuseAddressToggledLock = true; // Lock mutex. + late final DSBController _switchController; - try { - if (confirmed == true) { - await ref.read(pWalletInfo(widget.walletId)).updateOtherData( - newEntries: { - WalletInfoKeys.reuseAddress: true, - }, - isar: ref.read(mainDBProvider).isar, + bool _switchReuseAddressToggledLock = false; // Mutex. + Future _switchReuseAddressToggled() async { + if (_switchReuseAddressToggledLock) { + return; + } + _switchReuseAddressToggledLock = true; // Lock mutex. + + try { + if (_switchController.isOn?.call() != true) { + final canContinue = await showDialog( + context: context, + builder: (context) { + return StackDialog( + title: "Warning!", + message: + "Reusing addresses reduces your privacy and security. Are you sure you want to reuse addresses by default?", + leftButton: TextButton( + style: Theme.of(context) + .extension()! + .getSecondaryEnabledButtonStyle(context), + child: Text( + "Cancel", + style: STextStyles.itemSubtitle12(context), + ), + onPressed: () { + Navigator.of(context).pop(false); + }, + ), + rightButton: TextButton( + style: Theme.of(context) + .extension()! + .getPrimaryEnabledButtonStyle(context), + child: Text( + "Continue", + style: STextStyles.button(context), + ), + onPressed: () { + Navigator.of(context).pop(true); + }, + ), ); - } else { - await ref.read(pWalletInfo(widget.walletId)).updateOtherData( - newEntries: { - WalletInfoKeys.reuseAddress: false, - }, - isar: ref.read(mainDBProvider).isar, - ); - } - } finally { - // ensure _switchReuseAddressToggledLock is set to false no matter what. - _switchReuseAddressToggledLock = false; + }, + ); + + if (canContinue == true) { + await _updateAddressReuse(true); } - }); - } else { - await ref.read(pWalletInfo(widget.walletId)).updateOtherData( - newEntries: { - WalletInfoKeys.reuseAddress: false, - }, - isar: ref.read(mainDBProvider).isar, - ); + } else { + await _updateAddressReuse(false); + } + } finally { + // ensure _switchReuseAddressToggledLock is set to false no matter what. + _switchReuseAddressToggledLock = false; + } + } + + Future _updateAddressReuse(bool shouldReuse) async { + await ref.read(pWalletInfo(widget.walletId)).updateOtherData( + newEntries: { + WalletInfoKeys.reuseAddress: shouldReuse, + }, + isar: ref.read(mainDBProvider).isar, + ); + + if (_switchController.isOn != null) { + if (_switchController.isOn!.call() != shouldReuse) { + _switchController.activate?.call(); + } } } + @override + void initState() { + _switchController = DSBController(); + super.initState(); + } + @override Widget build(BuildContext context) { + final wallet = ref.watch(pWallets).getWallet(widget.walletId); + + final isViewOnlyNoAddressGen = wallet is ViewOnlyOptionInterface && + wallet.isViewOnly && + wallet.viewOnlyType == ViewOnlyWalletType.addressOnly; + return Background( child: Scaffold( backgroundColor: Theme.of(context).extension()!.background, @@ -185,11 +197,11 @@ class _WalletSettingsWalletSettingsViewState ), ), ), - const SizedBox( - height: 8, - ), - if (ref.watch(pWallets).getWallet(widget.walletId) - is RbfInterface) + if (wallet is RbfInterface) + const SizedBox( + height: 8, + ), + if (wallet is RbfInterface) RoundedWhiteContainer( padding: const EdgeInsets.all(0), child: RawMaterialButton( @@ -221,70 +233,60 @@ class _WalletSettingsWalletSettingsViewState ), ), ), - if (ref.watch(pWallets).getWallet(widget.walletId) - is RbfInterface) + if (wallet is MultiAddressInterface && !isViewOnlyNoAddressGen) const SizedBox( height: 8, ), - if (ref.watch(pWallets).getWallet(widget.walletId) - is MultiAddressInterface) + if (wallet is MultiAddressInterface && !isViewOnlyNoAddressGen) RoundedWhiteContainer( - child: Consumer( - builder: (_, ref, __) { - return RawMaterialButton( - // splashColor: Theme.of(context).extension()!.highlight, - materialTapTargetSize: - MaterialTapTargetSize.shrinkWrap, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, + padding: const EdgeInsets.all(0), + child: RawMaterialButton( + // splashColor: Theme.of(context).extension()!.highlight, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + onPressed: _switchReuseAddressToggled, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 12.0, + vertical: 20, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Reuse receiving address", + style: STextStyles.titleBold12(context), + textAlign: TextAlign.left, ), - ), - onPressed: null, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Reuse receiving address by default", - style: STextStyles.titleBold12(context), - textAlign: TextAlign.left, + SizedBox( + height: 20, + width: 40, + child: IgnorePointer( + child: DraggableSwitchButton( + isOn: ref.watch( + pWalletInfo(widget.walletId).select( + (value) => value.otherData, + ), + )[WalletInfoKeys.reuseAddress] as bool? ?? + false, + controller: _switchController, ), - SizedBox( - height: 20, - width: 40, - child: DraggableSwitchButton( - isOn: ref.watch( - pWalletInfo(widget.walletId).select( - (value) => value.otherData), - )[WalletInfoKeys.reuseAddress] - as bool? ?? - false, - onValueChanged: (newValue) { - _switchReuseAddressToggled(newValue); - }, - ), - ), - ], + ), ), - ), - ); - }, + ], + ), + ), ), ), - if (ref.watch(pWallets).getWallet(widget.walletId) - is MultiAddressInterface) + if (wallet is LelantusInterface && !wallet.isViewOnly) const SizedBox( height: 8, ), - if (ref.watch(pWallets).getWallet(widget.walletId) - is LelantusInterface) - const SizedBox( - height: 8, - ), - if (ref.watch(pWallets).getWallet(widget.walletId) - is LelantusInterface) + if (wallet is LelantusInterface && !wallet.isViewOnly) RoundedWhiteContainer( padding: const EdgeInsets.all(0), child: RawMaterialButton( @@ -316,13 +318,11 @@ class _WalletSettingsWalletSettingsViewState ), ), ), - if (ref.watch(pWallets).getWallet(widget.walletId) - is SparkInterface) + if (wallet is SparkInterface && !wallet.isViewOnly) const SizedBox( height: 8, ), - if (ref.watch(pWallets).getWallet(widget.walletId) - is SparkInterface) + if (wallet is SparkInterface && !wallet.isViewOnly) RoundedWhiteContainer( padding: const EdgeInsets.all(0), child: RawMaterialButton( @@ -354,11 +354,9 @@ class _WalletSettingsWalletSettingsViewState ), ), ), - if (ref.watch(pWallets).getWallet(widget.walletId) - is RbfInterface) - const SizedBox( - height: 8, - ), + const SizedBox( + height: 8, + ), RoundedWhiteContainer( padding: const EdgeInsets.all(0), child: RawMaterialButton( @@ -371,7 +369,7 @@ class _WalletSettingsWalletSettingsViewState materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, padding: const EdgeInsets.all(0), onPressed: () { - showDialog( + showDialog( barrierDismissible: true, context: context, builder: (_) => StackDialog( diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart index 09200fb1b..2eddd3078 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart @@ -57,7 +57,7 @@ class XPubViewState extends ConsumerState { late String _currentDropDownValue; String _current(String key) => - widget.xpubData.xpubs.firstWhere((e) => e.path == key).xpub; + widget.xpubData.xpubs.firstWhere((e) => e.path == key).encoded; Future _copy() async { await widget.clipboardInterface.setData( diff --git a/lib/pages/special/firo_rescan_recovery_error_dialog.dart b/lib/pages/special/firo_rescan_recovery_error_dialog.dart index 92d226d4b..fa59d841a 100644 --- a/lib/pages/special/firo_rescan_recovery_error_dialog.dart +++ b/lib/pages/special/firo_rescan_recovery_error_dialog.dart @@ -12,7 +12,7 @@ import '../../utilities/assets.dart'; import '../../utilities/text_styles.dart'; import '../../utilities/util.dart'; import '../../wallets/isar/providers/wallet_info_provider.dart'; -import '../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; +import '../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart'; import '../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; import '../../widgets/background.dart'; @@ -270,7 +270,7 @@ class _FiroRescanRecoveryErrorViewState KeyDataInterface? keyData; if (wallet is ExtendedKeysInterface) { keyData = await wallet.getXPrivs(); - } else if (wallet is CwBasedInterface) { + } else if (wallet is LibMoneroWallet) { keyData = await wallet.getKeys(); } diff --git a/lib/pages/stack_privacy_calls.dart b/lib/pages/stack_privacy_calls.dart index 8aa34c811..5ebaca345 100644 --- a/lib/pages/stack_privacy_calls.dart +++ b/lib/pages/stack_privacy_calls.dart @@ -105,192 +105,199 @@ class _StackPrivacyCalls extends ConsumerState { constraints: BoxConstraints( maxWidth: isDesktop ? 480 : double.infinity, ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - "Choose your ${AppConfig.prefix} experience", - style: isDesktop - ? STextStyles.desktopH2(context) - : STextStyles.pageTitleH1(context), - ), - SizedBox( - height: isDesktop ? 16 : 8, - ), - Text( - !widget.isSettings - ? "You can change it later in Settings" - : "", - style: isDesktop - ? STextStyles.desktopSubtitleH2(context) - : STextStyles.subtitle(context), - ), - SizedBox( - height: isDesktop ? 32 : 36, - ), - Padding( - padding: EdgeInsets.symmetric( - horizontal: isDesktop ? 0 : 16, + child: ConditionalParent( + condition: isDesktop, + builder: (child) => SingleChildScrollView( + child: child, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "Choose your ${AppConfig.prefix} experience", + textAlign: TextAlign.center, + style: isDesktop + ? STextStyles.desktopH2(context) + : STextStyles.pageTitleH1(context), ), - child: PrivacyToggle( - externalCallsEnabled: isEasy, - onChanged: (externalCalls) { - isEasy = externalCalls; - setState(() { - infoToggle = isEasy; - }); - }, + SizedBox( + height: isDesktop ? 16 : 8, ), - ), - SizedBox( - height: isDesktop ? 16 : 36, - ), - Padding( - padding: isDesktop - ? const EdgeInsets.all(0) - : const EdgeInsets.all(16.0), - child: RoundedWhiteContainer( - child: Center( - child: RichText( - textAlign: TextAlign.left, - text: TextSpan( - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.label(context).copyWith( - fontSize: 12.0, - ), - children: infoToggle - ? [ - if (Constants.enableExchange) + Text( + !widget.isSettings + ? "You can change it later in Settings" + : "", + style: isDesktop + ? STextStyles.desktopSubtitleH2(context) + : STextStyles.subtitle(context), + ), + SizedBox( + height: isDesktop ? 32 : 36, + ), + Padding( + padding: EdgeInsets.symmetric( + horizontal: isDesktop ? 0 : 16, + ), + child: PrivacyToggle( + externalCallsEnabled: isEasy, + onChanged: (externalCalls) { + isEasy = externalCalls; + setState(() { + infoToggle = isEasy; + }); + }, + ), + ), + SizedBox( + height: isDesktop ? 16 : 36, + ), + Padding( + padding: isDesktop + ? const EdgeInsets.all(0) + : const EdgeInsets.all(16.0), + child: RoundedWhiteContainer( + child: Center( + child: RichText( + textAlign: TextAlign.left, + text: TextSpan( + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ) + : STextStyles.label(context).copyWith( + fontSize: 12.0, + ), + children: infoToggle + ? [ + if (Constants.enableExchange) + const TextSpan( + text: + "Exchange data preloaded for a seamless experience.\n\n", + ), const TextSpan( text: - "Exchange data preloaded for a seamless experience.\n\n", + "CoinGecko enabled: (24 hour price change shown in-app, total wallet value shown in USD or other currency).\n\n", ), - const TextSpan( - text: - "CoinGecko enabled: (24 hour price change shown in-app, total wallet value shown in USD or other currency).\n\n", - ), - TextSpan( - text: - "Recommended for most crypto users.", - style: isDesktop - ? STextStyles - .desktopTextExtraExtraSmall600( - context, - ) - : TextStyle( - color: Theme.of(context) - .extension()! - .textDark, - fontWeight: FontWeight.w600, - ), - ), - ] - : [ - if (Constants.enableExchange) + TextSpan( + text: + "Recommended for most crypto users.", + style: isDesktop + ? STextStyles + .desktopTextExtraExtraSmall600( + context, + ) + : TextStyle( + color: Theme.of(context) + .extension()! + .textDark, + fontWeight: FontWeight.w600, + ), + ), + ] + : [ + if (Constants.enableExchange) + const TextSpan( + text: + "Exchange data not preloaded (slower experience).\n\n", + ), const TextSpan( text: - "Exchange data not preloaded (slower experience).\n\n", + "CoinGecko disabled (price changes not shown, no wallet value shown in other currencies).\n\n", ), - const TextSpan( - text: - "CoinGecko disabled (price changes not shown, no wallet value shown in other currencies).\n\n", - ), - TextSpan( - text: - "Recommended for the privacy conscious.", - style: isDesktop - ? STextStyles - .desktopTextExtraExtraSmall600( - context, - ) - : TextStyle( - color: Theme.of(context) - .extension()! - .textDark, - fontWeight: FontWeight.w600, - ), - ), - ], + TextSpan( + text: + "Recommended for the privacy conscious.", + style: isDesktop + ? STextStyles + .desktopTextExtraExtraSmall600( + context, + ) + : TextStyle( + color: Theme.of(context) + .extension()! + .textDark, + fontWeight: FontWeight.w600, + ), + ), + ], + ), ), ), ), ), - ), - if (!isDesktop) - const Spacer( - flex: 4, - ), - if (isDesktop) - const SizedBox( - height: 32, - ), - Padding( - padding: isDesktop - ? const EdgeInsets.all(0) - : const EdgeInsets.symmetric( - horizontal: 16, - vertical: 16, - ), - child: Row( - children: [ - Expanded( - child: PrimaryButton( - label: !widget.isSettings - ? "Continue" - : "Save changes", - onPressed: () { - ref - .read(prefsChangeNotifierProvider) - .externalCalls = isEasy; + if (!isDesktop) + const Spacer( + flex: 4, + ), + if (isDesktop) + const SizedBox( + height: 32, + ), + Padding( + padding: isDesktop + ? const EdgeInsets.all(0) + : const EdgeInsets.symmetric( + horizontal: 16, + vertical: 16, + ), + child: Row( + children: [ + Expanded( + child: PrimaryButton( + label: !widget.isSettings + ? "Continue" + : "Save changes", + onPressed: () { + ref + .read(prefsChangeNotifierProvider) + .externalCalls = isEasy; - DB.instance - .put( - boxName: DB.boxNamePrefs, - key: "externalCalls", - value: isEasy, - ) - .then((_) { - if (isEasy) { - if (AppConfig.hasFeature(AppFeature.swap)) { - unawaited( - ExchangeDataLoadingService.instance - .loadAll(), + DB.instance + .put( + boxName: DB.boxNamePrefs, + key: "externalCalls", + value: isEasy, + ) + .then((_) { + if (isEasy) { + if (AppConfig.hasFeature(AppFeature.swap)) { + unawaited( + ExchangeDataLoadingService.instance + .loadAll(), + ); + } + // unawaited( + // BuyDataLoadingService().loadAll(ref)); + ref + .read(priceAnd24hChangeNotifierProvider) + .start(true); + } + }); + if (!widget.isSettings) { + if (isDesktop) { + Navigator.of(context).pushNamed( + CreatePasswordView.routeName, + ); + } else { + Navigator.of(context).pushNamed( + CreatePinView.routeName, ); } - // unawaited( - // BuyDataLoadingService().loadAll(ref)); - ref - .read(priceAnd24hChangeNotifierProvider) - .start(true); - } - }); - if (!widget.isSettings) { - if (isDesktop) { - Navigator.of(context).pushNamed( - CreatePasswordView.routeName, - ); } else { - Navigator.of(context).pushNamed( - CreatePinView.routeName, - ); + Navigator.pop(context); } - } else { - Navigator.pop(context); - } - }, + }, + ), ), - ), - ], + ], + ), ), - ), - if (isDesktop) - const SizedBox( - height: kDesktopAppBarHeight, - ), - ], + if (isDesktop) + const SizedBox( + height: kDesktopAppBarHeight, + ), + ], + ), ), ), ), diff --git a/lib/pages/token_view/sub_widgets/token_summary.dart b/lib/pages/token_view/sub_widgets/token_summary.dart index 442721b54..f5b051ef7 100644 --- a/lib/pages/token_view/sub_widgets/token_summary.dart +++ b/lib/pages/token_view/sub_widgets/token_summary.dart @@ -218,6 +218,9 @@ class TokenWalletOptions extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final prefs = ref.watch(prefsChangeNotifierProvider); + final showExchange = prefs.enableExchange; + return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -251,11 +254,11 @@ class TokenWalletOptions extends ConsumerWidget { subLabel: "Send", iconAssetPathSVG: Assets.svg.arrowUpRight, ), - if (AppConfig.hasFeature(AppFeature.swap)) + if (AppConfig.hasFeature(AppFeature.swap) && showExchange) const SizedBox( width: 16, ), - if (AppConfig.hasFeature(AppFeature.swap)) + if (AppConfig.hasFeature(AppFeature.swap) && showExchange) TokenOptionsButton( onPressed: () => _onExchangePressed(context), subLabel: "Swap", @@ -265,11 +268,11 @@ class TokenWalletOptions extends ConsumerWidget { ), ), ), - if (AppConfig.hasFeature(AppFeature.buy)) + if (AppConfig.hasFeature(AppFeature.buy) && showExchange) const SizedBox( width: 16, ), - if (AppConfig.hasFeature(AppFeature.buy)) + if (AppConfig.hasFeature(AppFeature.buy) && showExchange) TokenOptionsButton( onPressed: () => _onBuyPressed(context), subLabel: "Buy", diff --git a/lib/pages/wallet_view/sub_widgets/tx_icon.dart b/lib/pages/wallet_view/sub_widgets/tx_icon.dart index 94f9b033a..1ea611a80 100644 --- a/lib/pages/wallet_view/sub_widgets/tx_icon.dart +++ b/lib/pages/wallet_view/sub_widgets/tx_icon.dart @@ -13,6 +13,7 @@ import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; import '../../../models/isar/models/isar_models.dart'; import '../../../models/isar/stack_theme.dart'; @@ -106,6 +107,11 @@ class TxIcon extends ConsumerWidget { !tx.isConfirmed( currentHeight, ref.watch(pWallets).getWallet(tx.walletId).cryptoCurrency.minConfirms, + ref + .watch(pWallets) + .getWallet(tx.walletId) + .cryptoCurrency + .minCoinbaseConfirms, ), tx.subType, tx.type, diff --git a/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart b/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart index 6862f878a..08d9152b8 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart @@ -81,14 +81,16 @@ class WalletSummaryInfo extends ConsumerWidget { priceAnd24hChangeNotifierProvider.select((value) => value.getPrice(coin)), ); - final _showAvailable = - ref.watch(walletBalanceToggleStateProvider.state).state == - WalletBalanceToggleState.available; + final _showAvailable = ref.watch(walletBalanceToggleStateProvider) == + WalletBalanceToggleState.available; final Amount balanceToShow; final String title; + final bool toggleBalance; + if (coin is Firo) { + toggleBalance = false; final type = ref.watch(publicPrivateBalanceStateProvider.state).state; title = "${_showAvailable ? "Available" : "Full"} ${type.name.capitalize()} balance"; @@ -109,6 +111,7 @@ class WalletSummaryInfo extends ConsumerWidget { break; } } else { + toggleBalance = true; balanceToShow = _showAvailable ? balance.spendable : balance.total; title = _showAvailable ? "Available balance" : "Full balance"; } @@ -141,7 +144,20 @@ class WalletSummaryInfo extends ConsumerWidget { children: [ GestureDetector( onTap: () { - showSheet(context); + if (toggleBalance) { + if (ref.read(walletBalanceToggleStateProvider) == + WalletBalanceToggleState.available) { + ref + .read(walletBalanceToggleStateProvider.notifier) + .state = WalletBalanceToggleState.full; + } else { + ref + .read(walletBalanceToggleStateProvider.notifier) + .state = WalletBalanceToggleState.available; + } + } else { + showSheet(context); + } }, child: Row( children: [ @@ -153,21 +169,38 @@ class WalletSummaryInfo extends ConsumerWidget { .textFavoriteCard, ), ), - const SizedBox( - width: 4, - ), - SvgPicture.asset( - Assets.svg.chevronDown, - color: Theme.of(context) - .extension()! - .textFavoriteCard, - width: 8, - height: 4, - ), + if (!toggleBalance) ...[ + const SizedBox( + width: 4, + ), + SvgPicture.asset( + Assets.svg.chevronDown, + color: Theme.of(context) + .extension()! + .textFavoriteCard, + width: 8, + height: 4, + ), + ], ], ), ), const Spacer(), + if (ref.watch(pWalletInfo(walletId)).isViewOnly) + FittedBox( + fit: BoxFit.scaleDown, + child: SelectableText( + "(View only)", + style: STextStyles.pageTitleH1(context).copyWith( + fontSize: 18, + color: Theme.of(context) + .extension()! + .textFavoriteCard + .withOpacity(0.7), + ), + ), + ), + const Spacer(), FittedBox( fit: BoxFit.scaleDown, child: SelectableText( diff --git a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart index dad531969..1071ffdae 100644 --- a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart +++ b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart @@ -15,14 +15,12 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:tuple/tuple.dart'; +import 'package:url_launcher/url_launcher.dart'; + import '../../../models/isar/models/blockchain_data/transaction.dart'; import '../../../models/isar/models/ethereum/eth_contract.dart'; import '../../../notifications/show_flush_bar.dart'; -import '../../receive_view/addresses/address_details_view.dart'; -import '../sub_widgets/tx_icon.dart'; -import 'dialogs/cancelling_transaction_progress_dialog.dart'; -import 'edit_note_view.dart'; -import '../wallet_view.dart'; import '../../../providers/db/main_db_provider.dart'; import '../../../providers/global/address_book_service_provider.dart'; import '../../../providers/providers.dart'; @@ -52,8 +50,11 @@ import '../../../widgets/icon_widgets/copy_icon.dart'; import '../../../widgets/icon_widgets/pencil_icon.dart'; import '../../../widgets/rounded_white_container.dart'; import '../../../widgets/stack_dialog.dart'; -import 'package:tuple/tuple.dart'; -import 'package:url_launcher/url_launcher.dart'; +import '../../receive_view/addresses/address_details_view.dart'; +import '../sub_widgets/tx_icon.dart'; +import '../wallet_view.dart'; +import 'dialogs/cancelling_transaction_progress_dialog.dart'; +import 'edit_note_view.dart'; class TransactionDetailsView extends ConsumerStatefulWidget { const TransactionDetailsView({ @@ -133,13 +134,15 @@ class _TransactionDetailsViewState } String whatIsIt(Transaction tx, int height) { + String prettyConfirms() => "(${tx.getConfirmations(height)}/$minConfirms)"; + final type = tx.type; if (coin is Firo) { if (tx.subType == TransactionSubType.mint) { if (tx.isConfirmed(height, minConfirms)) { return "Minted"; } else { - return "Minting"; + return "Minting ${prettyConfirms()}"; } } } @@ -156,7 +159,7 @@ class _TransactionDetailsViewState } else if ((_transaction.numberOfMessages ?? 0) > 1) { return "Receiving (waiting for confirmations)"; // TODO test if the sender still has to open again after the receiver has 2 messages present, ie. sender->receiver->sender->node (yes) vs. sender->receiver->node (no) } else { - return "Receiving"; + return "Receiving ${prettyConfirms()}"; } } } else if (type == TransactionType.outgoing) { @@ -168,7 +171,7 @@ class _TransactionDetailsViewState } else if ((_transaction.numberOfMessages ?? 0) > 1) { return "Sending (waiting for confirmations)"; } else { - return "Sending"; + return "Sending ${prettyConfirms()}"; } } } @@ -181,16 +184,20 @@ class _TransactionDetailsViewState if (tx.isConfirmed(height, minConfirms)) { return "Received"; } else { - return "Receiving"; + return "Receiving ${prettyConfirms()}"; } } else if (type == TransactionType.outgoing) { if (tx.isConfirmed(height, minConfirms)) { return "Sent"; } else { - return "Sending"; + return "Sending ${prettyConfirms()}"; } } else if (type == TransactionType.sentToSelf) { - return "Sent to self"; + if (tx.isConfirmed(height, minConfirms)) { + return "Sent to self"; + } else { + return "Sent to self ${prettyConfirms()}"; + } } else { return type.name; } @@ -1157,59 +1164,95 @@ class _TransactionDetailsViewState : const SizedBox( height: 12, ), - RoundedWhiteContainer( - padding: isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), - child: Builder( - builder: (context) { - final String height; + Builder( + builder: (context) { + final String height; + final String confirmations; + final confirms = _transaction.getConfirmations( + currentHeight, + ); - if (widget.coin is Bitcoincash || - widget.coin is Ecash) { + if (widget.coin is Bitcoincash || + widget.coin is Ecash) { + height = _transaction.height != null && + _transaction.height! > 0 + ? "${_transaction.height!}" + : "Pending"; + confirmations = confirms.toString(); + } else if (widget.coin is Epiccash && + _transaction.slateId == null) { + confirmations = "Unknown"; + height = "Unknown"; + } else { + final confirmed = _transaction.isConfirmed( + currentHeight, + minConfirms, + ); + if (widget.coin is! Epiccash && confirmed) { height = - "${_transaction.height != null && _transaction.height! > 0 ? _transaction.height! : "Pending"}"; + "${_transaction.height == 0 ? "Unknown" : _transaction.height}"; } else { - height = widget.coin is! Epiccash && - _transaction.isConfirmed( - currentHeight, - minConfirms, - ) - ? "${_transaction.height == 0 ? "Unknown" : _transaction.height}" - : _transaction.getConfirmations( - currentHeight, - ) > - 0 - ? "${_transaction.height}" - : "Pending"; + height = confirms > 0 + ? "${_transaction.height}" + : "Pending"; } - return Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Column( + confirmations = confirms.toString(); + } + + return Column( + children: [ + RoundedWhiteContainer( + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "Block height", - style: isDesktop - ? STextStyles - .desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "Block height", + style: isDesktop + ? STextStyles + .desktopTextExtraExtraSmall( + context, + ) + : STextStyles.itemSubtitle( + context, + ), + ), + if (isDesktop) + const SizedBox( + height: 2, + ), + if (isDesktop) + SelectableText( + height, + style: isDesktop + ? STextStyles + .desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of( + context) + .extension< + StackColors>()! + .textDark, + ) + : STextStyles + .itemSubtitle12( + context, + ), + ), + ], ), - if (isDesktop) - const SizedBox( - height: 2, - ), - if (isDesktop) + if (!isDesktop) SelectableText( height, style: isDesktop @@ -1226,30 +1269,91 @@ class _TransactionDetailsViewState context, ), ), + if (isDesktop) + IconCopyButton(data: height), ], ), - if (!isDesktop) - SelectableText( - height, - style: isDesktop - ? STextStyles - .desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of(context) - .extension()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, + ), + isDesktop + ? const _Divider() + : const SizedBox( + height: 12, + ), + RoundedWhiteContainer( + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "Confirmations", + style: isDesktop + ? STextStyles + .desktopTextExtraExtraSmall( + context, + ) + : STextStyles.itemSubtitle( + context, + ), + ), + if (isDesktop) + const SizedBox( + height: 2, ), - ), - if (isDesktop) - IconCopyButton(data: height), - ], - ); - }, - ), + if (isDesktop) + SelectableText( + confirmations, + style: isDesktop + ? STextStyles + .desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of( + context) + .extension< + StackColors>()! + .textDark, + ) + : STextStyles + .itemSubtitle12( + context, + ), + ), + ], + ), + if (!isDesktop) + SelectableText( + confirmations, + style: isDesktop + ? STextStyles + .desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension< + StackColors>()! + .textDark, + ) + : STextStyles.itemSubtitle12( + context, + ), + ), + if (isDesktop) + IconCopyButton(data: height), + ], + ), + ), + ], + ); + }, ), if (coin is Ethereum) isDesktop diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart b/lib/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart index 24dd77005..ed6e007d2 100644 --- a/lib/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart +++ b/lib/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart @@ -15,14 +15,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:isar/isar.dart'; + import '../../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; import '../../../../models/isar/models/contact_entry.dart'; import '../../../../models/isar/models/isar_models.dart'; import '../../../../models/transaction_filter.dart'; -import '../../sub_widgets/tx_icon.dart'; -import '../transaction_search_filter_view.dart'; -import 'transaction_v2_card.dart'; -import 'transaction_v2_details_view.dart'; import '../../../../providers/db/main_db_provider.dart'; import '../../../../providers/global/address_book_service_provider.dart'; import '../../../../providers/providers.dart'; @@ -49,6 +46,10 @@ import '../../../../widgets/loading_indicator.dart'; import '../../../../widgets/rounded_white_container.dart'; import '../../../../widgets/stack_text_field.dart'; import '../../../../widgets/textfield_icon_button.dart'; +import '../../sub_widgets/tx_icon.dart'; +import '../transaction_search_filter_view.dart'; +import 'transaction_v2_card.dart'; +import 'transaction_v2_details_view.dart'; typedef _GroupedTransactions = ({ String label, @@ -866,6 +867,11 @@ class _DesktopTransactionCardRowState String whatIsIt(TransactionV2 tx, int height) => tx.statusLabel( currentChainHeight: height, minConfirms: minConfirms, + minCoinbaseConfirms: ref + .read(pWallets) + .getWallet(widget.walletId) + .cryptoCurrency + .minCoinbaseConfirms, ); @override diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_card.dart b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_card.dart index 0cce71e0f..34e5c662b 100644 --- a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_card.dart +++ b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_card.dart @@ -60,6 +60,11 @@ class _TransactionCardStateV2 extends ConsumerState { .getWallet(walletId) .cryptoCurrency .minConfirms, + minCoinbaseConfirms: ref + .read(pWallets) + .getWallet(walletId) + .cryptoCurrency + .minCoinbaseConfirms, ); @override diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart index ce5635c1c..a1d4bb3fd 100644 --- a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart +++ b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart @@ -48,6 +48,7 @@ import '../../../../widgets/background.dart'; import '../../../../widgets/conditional_parent.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/custom_buttons/blue_text_button.dart'; +import '../../../../widgets/custom_buttons/simple_copy_button.dart'; import '../../../../widgets/desktop/desktop_dialog.dart'; import '../../../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../../../widgets/desktop/primary_button.dart'; @@ -373,6 +374,11 @@ class _TransactionV2DetailsViewState String whatIsIt(TransactionV2 tx, int height) => tx.statusLabel( currentChainHeight: height, minConfirms: minConfirms, + minCoinbaseConfirms: ref + .read(pWallets) + .getWallet(walletId) + .cryptoCurrency + .minCoinbaseConfirms, ); Future fetchContactNameFor(String address) async { @@ -567,6 +573,7 @@ class _TransactionV2DetailsViewState final confirmedTxn = _transaction.isConfirmed( currentHeight, coin.minConfirms, + coin.minCoinbaseConfirms, ); return ConditionalParent( @@ -1053,6 +1060,12 @@ class _TransactionV2DetailsViewState ], ), ), + if (coin is Epiccash) + isDesktop + ? const _Divider() + : const SizedBox( + height: 12, + ), if (coin is Epiccash) RoundedWhiteContainer( padding: isDesktop @@ -1361,6 +1374,7 @@ class _TransactionV2DetailsViewState ? _transaction.isConfirmed( currentHeight, minConfirms, + coin.minCoinbaseConfirms, ) ? ref .watch(pAmountFormatter(coin)) @@ -1457,59 +1471,95 @@ class _TransactionV2DetailsViewState : const SizedBox( height: 12, ), - RoundedWhiteContainer( - padding: isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), - child: Builder( - builder: (context) { - final String height; + Builder( + builder: (context) { + final String height; + final String confirmations; + final confirms = _transaction.getConfirmations( + currentHeight, + ); - if (widget.coin is Bitcoincash || - widget.coin is Ecash) { + if (widget.coin is Bitcoincash || + widget.coin is Ecash) { + height = _transaction.height != null && + _transaction.height! > 0 + ? "${_transaction.height!}" + : "Pending"; + confirmations = confirms.toString(); + } else if (widget.coin is Epiccash && + _transaction.slateId == null) { + confirmations = "Unknown"; + height = "Unknown"; + } else { + final confirmed = _transaction.isConfirmed( + currentHeight, + minConfirms, + coin.minCoinbaseConfirms); + if (widget.coin is! Epiccash && confirmed) { height = - "${_transaction.height != null && _transaction.height! > 0 ? _transaction.height! : "Pending"}"; + "${_transaction.height == 0 ? "Unknown" : _transaction.height}"; } else { - height = widget.coin is! Epiccash && - _transaction.isConfirmed( - currentHeight, - minConfirms, - ) - ? "${_transaction.height == 0 ? "Unknown" : _transaction.height}" - : _transaction.getConfirmations( - currentHeight, - ) > - 0 - ? "${_transaction.height}" - : "Pending"; + height = confirms > 0 + ? "${_transaction.height}" + : "Pending"; } - return Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Column( + confirmations = confirms.toString(); + } + + return Column( + children: [ + RoundedWhiteContainer( + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "Block height", - style: isDesktop - ? STextStyles - .desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "Block height", + style: isDesktop + ? STextStyles + .desktopTextExtraExtraSmall( + context, + ) + : STextStyles.itemSubtitle( + context, + ), + ), + if (isDesktop) + const SizedBox( + height: 2, + ), + if (isDesktop) + SelectableText( + height, + style: isDesktop + ? STextStyles + .desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of( + context) + .extension< + StackColors>()! + .textDark, + ) + : STextStyles + .itemSubtitle12( + context, + ), + ), + ], ), - if (isDesktop) - const SizedBox( - height: 2, - ), - if (isDesktop) + if (!isDesktop) SelectableText( height, style: isDesktop @@ -1526,30 +1576,91 @@ class _TransactionV2DetailsViewState context, ), ), + if (isDesktop) + IconCopyButton(data: height), ], ), - if (!isDesktop) - SelectableText( - height, - style: isDesktop - ? STextStyles - .desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of(context) - .extension()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, + ), + isDesktop + ? const _Divider() + : const SizedBox( + height: 12, + ), + RoundedWhiteContainer( + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "Confirmations", + style: isDesktop + ? STextStyles + .desktopTextExtraExtraSmall( + context, + ) + : STextStyles.itemSubtitle( + context, + ), + ), + if (isDesktop) + const SizedBox( + height: 2, ), - ), - if (isDesktop) - IconCopyButton(data: height), - ], - ); - }, - ), + if (isDesktop) + SelectableText( + confirmations, + style: isDesktop + ? STextStyles + .desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of( + context) + .extension< + StackColors>()! + .textDark, + ) + : STextStyles + .itemSubtitle12( + context, + ), + ), + ], + ), + if (!isDesktop) + SelectableText( + confirmations, + style: isDesktop + ? STextStyles + .desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension< + StackColors>()! + .textDark, + ) + : STextStyles.itemSubtitle12( + context, + ), + ), + if (isDesktop) + IconCopyButton(data: height), + ], + ), + ), + ], + ); + }, ), if (kDebugMode) @@ -1612,16 +1723,27 @@ class _TransactionV2DetailsViewState crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "Transaction ID", - style: isDesktop - ? STextStyles - .desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + ConditionalParent( + condition: !isDesktop, + builder: (child) => Row( + children: [ + Expanded(child: child), + SimpleCopyButton( + data: _transaction.txid, + ), + ], + ), + child: Text( + "Transaction ID", + style: isDesktop + ? STextStyles + .desktopTextExtraExtraSmall( + context, + ) + : STextStyles.itemSubtitle( + context, + ), + ), ), const SizedBox( height: 8, diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index ea9ca50e4..d3a98ef16 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -50,11 +50,13 @@ import '../../wallets/crypto_currency/intermediate/frost_currency.dart'; import '../../wallets/isar/providers/wallet_info_provider.dart'; import '../../wallets/wallet/impl/bitcoin_frost_wallet.dart'; import '../../wallets/wallet/impl/firo_wallet.dart'; +import '../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart'; import '../../wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart'; import '../../wallets/wallet/wallet_mixin_interfaces/ordinals_interface.dart'; import '../../wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart'; import '../../wallets/wallet/wallet_mixin_interfaces/spark_interface.dart'; +import '../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; import '../../widgets/background.dart'; import '../../widgets/conditional_parent.dart'; import '../../widgets/custom_buttons/app_bar_icon_button.dart'; @@ -66,6 +68,7 @@ import '../../widgets/loading_indicator.dart'; import '../../widgets/small_tor_icon.dart'; import '../../widgets/stack_dialog.dart'; import '../../widgets/wallet_navigation_bar/components/icons/buy_nav_icon.dart'; +import '../../widgets/wallet_navigation_bar/components/icons/churn_nav_icon.dart'; import '../../widgets/wallet_navigation_bar/components/icons/coin_control_nav_icon.dart'; import '../../widgets/wallet_navigation_bar/components/icons/exchange_nav_icon.dart'; import '../../widgets/wallet_navigation_bar/components/icons/frost_sign_nav_icon.dart'; @@ -78,6 +81,7 @@ import '../../widgets/wallet_navigation_bar/components/wallet_navigation_bar_ite import '../../widgets/wallet_navigation_bar/wallet_navigation_bar.dart'; import '../buy_view/buy_in_wallet_view.dart'; import '../cashfusion/cashfusion_view.dart'; +import '../churning/churning_view.dart'; import '../coin_control/coin_control_view.dart'; import '../exchange_view/wallet_initiated_exchange_view.dart'; import '../monkey/monkey_view.dart'; @@ -518,6 +522,13 @@ class _WalletViewState extends ConsumerState { final coin = ref.watch(pWalletCoin(walletId)); + final prefs = ref.watch(prefsChangeNotifierProvider); + final showExchange = prefs.enableExchange; + + final wallet = ref.watch(pWallets).getWallet(walletId); + + final viewOnly = wallet is ViewOnlyOptionInterface && wallet.isViewOnly; + return ConditionalParent( condition: _rescanningOnOpen, builder: (child) { @@ -1020,40 +1031,42 @@ class _WalletViewState extends ConsumerState { icon: const FrostSignNavIcon(), onTap: () => _onFrostSignPressed(context), ), - WalletNavigationBarItemData( - label: "Send", - icon: const SendNavIcon(), - onTap: () { - // not sure what this is supposed to accomplish? - // switch (ref - // .read(walletBalanceToggleStateProvider.state) - // .state) { - // case WalletBalanceToggleState.full: - // ref - // .read(publicPrivateBalanceStateProvider.state) - // .state = "Public"; - // break; - // case WalletBalanceToggleState.available: - // ref - // .read(publicPrivateBalanceStateProvider.state) - // .state = "Private"; - // break; - // } - Navigator.of(context).pushNamed( - ref.read(pWallets).getWallet(walletId) - is BitcoinFrostWallet - ? FrostSendView.routeName - : SendView.routeName, - arguments: ( - walletId: walletId, - coin: coin, - ), - ); - }, - ), - if (Constants.enableExchange && + if (!viewOnly) + WalletNavigationBarItemData( + label: "Send", + icon: const SendNavIcon(), + onTap: () { + // not sure what this is supposed to accomplish? + // switch (ref + // .read(walletBalanceToggleStateProvider.state) + // .state) { + // case WalletBalanceToggleState.full: + // ref + // .read(publicPrivateBalanceStateProvider.state) + // .state = "Public"; + // break; + // case WalletBalanceToggleState.available: + // ref + // .read(publicPrivateBalanceStateProvider.state) + // .state = "Private"; + // break; + // } + Navigator.of(context).pushNamed( + wallet is BitcoinFrostWallet + ? FrostSendView.routeName + : SendView.routeName, + arguments: ( + walletId: walletId, + coin: coin, + ), + ); + }, + ), + if (!viewOnly && + Constants.enableExchange && ref.watch(pWalletCoin(walletId)) is! FrostCurrency && - AppConfig.hasFeature(AppFeature.swap)) + AppConfig.hasFeature(AppFeature.swap) && + showExchange) WalletNavigationBarItemData( label: "Swap", icon: const ExchangeNavIcon(), @@ -1061,7 +1074,8 @@ class _WalletViewState extends ConsumerState { ), if (Constants.enableExchange && ref.watch(pWalletCoin(walletId)) is! FrostCurrency && - AppConfig.hasFeature(AppFeature.buy)) + AppConfig.hasFeature(AppFeature.buy) && + showExchange) WalletNavigationBarItemData( label: "Buy", icon: const BuyNavIcon(), @@ -1105,12 +1119,7 @@ class _WalletViewState extends ConsumerState { ); }, ), - if (ref.watch( - pWallets.select( - (value) => value.getWallet(widget.walletId) - is CoinControlInterface, - ), - ) && + if (wallet is CoinControlInterface && ref.watch( prefsChangeNotifierProvider.select( (value) => value.enableCoinControl, @@ -1129,12 +1138,7 @@ class _WalletViewState extends ConsumerState { ); }, ), - if (ref.watch( - pWallets.select( - (value) => - value.getWallet(widget.walletId) is PaynymInterface, - ), - )) + if (!viewOnly && wallet is PaynymInterface) WalletNavigationBarItemData( label: "PayNym", icon: const PaynymNavIcon(), @@ -1171,8 +1175,10 @@ class _WalletViewState extends ConsumerState { // check if account exists and for matching code to see if claimed if (account.value != null && - account.value!.nonSegwitPaymentCode.claimed && - account.value!.segwit) { + account.value!.nonSegwitPaymentCode.claimed + // && + // account.value!.segwit + ) { ref.read(myPaynymAccountStateProvider.state).state = account.value!; @@ -1205,12 +1211,7 @@ class _WalletViewState extends ConsumerState { ); }, ), - if (ref.watch( - pWallets.select( - (value) => value.getWallet(widget.walletId) - is CashFusionInterface, - ), - )) + if (wallet is CashFusionInterface && !viewOnly) WalletNavigationBarItemData( label: "Fusion", icon: const FusionNavIcon(), @@ -1221,6 +1222,17 @@ class _WalletViewState extends ConsumerState { ); }, ), + if (wallet is LibMoneroWallet && !viewOnly) + WalletNavigationBarItemData( + label: "Churn", + icon: const ChurnNavIcon(), + onTap: () { + Navigator.of(context).pushNamed( + ChurningView.routeName, + arguments: walletId, + ); + }, + ), ], ), ], diff --git a/lib/pages/wallets_view/sub_widgets/favorite_card.dart b/lib/pages/wallets_view/sub_widgets/favorite_card.dart index ab91cc759..26e11624a 100644 --- a/lib/pages/wallets_view/sub_widgets/favorite_card.dart +++ b/lib/pages/wallets_view/sub_widgets/favorite_card.dart @@ -13,7 +13,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import '../../wallet_view/wallet_view.dart'; + import '../../../pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart'; import '../../../providers/providers.dart'; import '../../../themes/coin_icon_provider.dart'; @@ -22,13 +22,15 @@ import '../../../utilities/amount/amount.dart'; import '../../../utilities/amount/amount_formatter.dart'; import '../../../utilities/constants.dart'; import '../../../utilities/show_loading.dart'; +import '../../../utilities/show_node_tor_settings_mismatch.dart'; import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; import '../../../wallets/crypto_currency/coins/firo.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; -import '../../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; +import '../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../../widgets/coin_card.dart'; import '../../../widgets/conditional_parent.dart'; +import '../../wallet_view/wallet_view.dart'; class FavoriteCard extends ConsumerStatefulWidget { const FavoriteCard({ @@ -116,8 +118,21 @@ class _FavoriteCardState extends ConsumerState { onTap: () async { final wallet = ref.read(pWallets).getWallet(walletId); + final canContinue = await checkShowNodeTorSettingsMismatch( + context: context, + currency: coin, + prefs: ref.read(prefsChangeNotifierProvider), + nodeService: ref.read(nodeServiceChangeNotifierProvider), + allowCancel: true, + rootNavigator: Util.isDesktop, + ); + + if (!canContinue) { + return; + } + final Future loadFuture; - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { loadFuture = wallet.init().then((value) async => await (wallet).open()); } else { diff --git a/lib/pages/wallets_view/sub_widgets/wallet_list_item.dart b/lib/pages/wallets_view/sub_widgets/wallet_list_item.dart index 5602d2419..767770ba9 100644 --- a/lib/pages/wallets_view/sub_widgets/wallet_list_item.dart +++ b/lib/pages/wallets_view/sub_widgets/wallet_list_item.dart @@ -21,10 +21,11 @@ import '../../../themes/stack_colors.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/constants.dart'; import '../../../utilities/show_loading.dart'; +import '../../../utilities/show_node_tor_settings_mismatch.dart'; import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; import '../../../wallets/crypto_currency/crypto_currency.dart'; -import '../../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; +import '../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../../widgets/dialogs/tor_warning_dialog.dart'; import '../../../widgets/rounded_white_container.dart'; import '../../wallet_view/wallet_view.dart'; @@ -83,8 +84,22 @@ class WalletListItem extends ConsumerWidget { .read(pWallets) .wallets .firstWhere((e) => e.info.coin == coin); + + final canContinue = await checkShowNodeTorSettingsMismatch( + context: context, + currency: coin, + prefs: ref.read(prefsChangeNotifierProvider), + nodeService: ref.read(nodeServiceChangeNotifierProvider), + allowCancel: true, + rootNavigator: Util.isDesktop, + ); + + if (!canContinue) { + return; + } + final Future loadFuture; - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { loadFuture = wallet.init().then((value) async => await (wallet).open()); } else { diff --git a/lib/pages/wallets_view/wallets_overview.dart b/lib/pages/wallets_view/wallets_overview.dart index 786ce78f6..2567a83cf 100644 --- a/lib/pages/wallets_view/wallets_overview.dart +++ b/lib/pages/wallets_view/wallets_overview.dart @@ -12,7 +12,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:isar/isar.dart'; -import 'package:tuple/tuple.dart'; import '../../app_config.dart'; import '../../models/add_wallet_list_entity/sub_classes/coin_entity.dart'; @@ -60,6 +59,8 @@ class WalletsOverview extends ConsumerStatefulWidget { ConsumerState createState() => _EthWalletsOverviewState(); } +typedef WalletListItemData = ({Wallet wallet, List contracts}); + class _EthWalletsOverviewState extends ConsumerState { final isDesktop = Util.isDesktop; @@ -68,28 +69,39 @@ class _EthWalletsOverviewState extends ConsumerState { String _searchString = ""; - final List>> wallets = []; + final Map wallets = {}; + + List _filter(String searchTerm) { + // clean out deleted wallets + final existingWalletIds = ref + .read(mainDBProvider) + .isar + .walletInfo + .where() + .walletIdProperty() + .findAllSync(); + wallets.removeWhere((k, v) => !existingWalletIds.contains(k)); - List>> _filter(String searchTerm) { if (searchTerm.isEmpty) { - return wallets; + return wallets.values.toList() + ..sort((a, b) => a.wallet.info.name.compareTo(b.wallet.info.name)); } - final List>> results = []; + final Map results = {}; final term = searchTerm.toLowerCase(); - for (final tuple in wallets) { + for (final entry in wallets.entries) { bool includeManager = false; // search wallet name and total balance - includeManager |= _elementContains(tuple.item1.info.name, term); + includeManager |= _elementContains(entry.value.wallet.info.name, term); includeManager |= _elementContains( - tuple.item1.info.cachedBalance.total.decimal.toString(), + entry.value.wallet.info.cachedBalance.total.decimal.toString(), term, ); final List contracts = []; - for (final contract in tuple.item2) { + for (final contract in entry.value.contracts) { if (_elementContains(contract.name, term)) { contracts.add(contract); } else if (_elementContains(contract.symbol, term)) { @@ -102,11 +114,12 @@ class _EthWalletsOverviewState extends ConsumerState { } if (includeManager || contracts.isNotEmpty) { - results.add(Tuple2(tuple.item1, contracts)); + results.addEntries([entry]); } } - return results; + return results.values.toList() + ..sort((a, b) => a.wallet.info.name.compareTo(b.wallet.info.name)); } bool _elementContains(String element, String term) { @@ -141,13 +154,11 @@ class _EthWalletsOverviewState extends ConsumerState { } // add tuple to list - wallets.add( - Tuple2( - ref.read(pWallets).getWallet( - data.walletId, - ), - contracts, - ), + wallets[data.walletId] = ( + wallet: ref.read(pWallets).getWallet( + data.walletId, + ), + contracts: contracts, ); } } else { @@ -155,13 +166,11 @@ class _EthWalletsOverviewState extends ConsumerState { for (final data in walletsData) { // desktop single coin apps may cause issues so lets just ignore the error and move on try { - wallets.add( - Tuple2( - ref.read(pWallets).getWallet( - data.walletId, - ), - [], - ), + wallets[data.walletId] = ( + wallet: ref.read(pWallets).getWallet( + data.walletId, + ), + contracts: [], ); } catch (_) { // lol bandaid for single coin based apps @@ -315,24 +324,27 @@ class _EthWalletsOverviewState extends ConsumerState { final data = _filter(_searchString); return ListView.separated( itemBuilder: (_, index) { - final element = data[index]; + final entry = data[index]; + final wallet = entry.wallet; - if (element.item1.cryptoCurrency.hasTokenSupport) { + if (wallet.cryptoCurrency.hasTokenSupport) { if (isDesktop) { return DesktopExpandingWalletCard( key: Key( - "${element.item1.info.name}_${element.item2.map((e) => e.address).join()}", + "${wallet.walletId}_${entry.contracts.map((e) => e.address).join()}", ), - data: element, + data: entry, navigatorState: widget.navigatorState!, ); } else { return MasterWalletCard( - walletId: element.item1.walletId, + key: Key(wallet.walletId), + walletId: wallet.walletId, ); } } else { return ConditionalParent( + key: Key(wallet.walletId), condition: isDesktop, builder: (child) => RoundedWhiteContainer( padding: const EdgeInsets.symmetric( @@ -345,7 +357,7 @@ class _EthWalletsOverviewState extends ConsumerState { child: child, ), child: SimpleWalletCard( - walletId: element.item1.walletId, + walletId: wallet.walletId, popPrevious: widget .overrideSimpleWalletCardPopPreviousValueWith == null diff --git a/lib/pages_desktop_specific/addresses/sub_widgets/desktop_address_list.dart b/lib/pages_desktop_specific/addresses/sub_widgets/desktop_address_list.dart index 9ac4fab33..a0a183afe 100644 --- a/lib/pages_desktop_specific/addresses/sub_widgets/desktop_address_list.dart +++ b/lib/pages_desktop_specific/addresses/sub_widgets/desktop_address_list.dart @@ -69,6 +69,19 @@ class _DesktopAddressListState extends ConsumerState { .and() .not() .typeEqualTo(AddressType.nonWallet) + .and() + .group( + (q) => q + .group( + (q2) => q2 + .typeEqualTo(AddressType.frostMS) + .and() + .zSafeFrostEqualTo(true), + ) + .or() + .not() + .typeEqualTo(AddressType.frostMS), + ) .sortByDerivationIndex() .idProperty() .findAllSync(); @@ -118,6 +131,19 @@ class _DesktopAddressListState extends ConsumerState { .and() .not() .typeEqualTo(AddressType.nonWallet) + .and() + .group( + (q) => q + .group( + (q2) => q2 + .typeEqualTo(AddressType.frostMS) + .and() + .zSafeFrostEqualTo(true), + ) + .or() + .not() + .typeEqualTo(AddressType.frostMS), + ) .sortByDerivationIndex() .idProperty() .findAllSync(); diff --git a/lib/pages_desktop_specific/cashfusion/sub_widgets/fusion_dialog.dart b/lib/pages_desktop_specific/cashfusion/sub_widgets/fusion_dialog.dart index 09bce3a3b..8eb43d648 100644 --- a/lib/pages_desktop_specific/cashfusion/sub_widgets/fusion_dialog.dart +++ b/lib/pages_desktop_specific/cashfusion/sub_widgets/fusion_dialog.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:wakelock/wakelock.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; import '../../../providers/cash_fusion/fusion_progress_ui_state_provider.dart'; import '../../../providers/global/prefs_provider.dart'; @@ -139,7 +139,7 @@ class _FusionDialogViewState extends ConsumerState { message: "Stopping fusion", ); - await Wakelock.disable(); + await WakelockPlus.disable(); return true; } else { @@ -156,7 +156,7 @@ class _FusionDialogViewState extends ConsumerState { @override dispose() { - Wakelock.disable(); + WakelockPlus.disable(); super.dispose(); } @@ -173,7 +173,7 @@ class _FusionDialogViewState extends ConsumerState { .fusionRoundsCompleted; if (!Platform.isLinux) { - Wakelock.enable(); + WakelockPlus.enable(); } return DesktopDialog( diff --git a/lib/pages_desktop_specific/churning/desktop_churning_view.dart b/lib/pages_desktop_specific/churning/desktop_churning_view.dart new file mode 100644 index 000000000..e3a7b0736 --- /dev/null +++ b/lib/pages_desktop_specific/churning/desktop_churning_view.dart @@ -0,0 +1,424 @@ +import 'dart:async'; + +import 'package:dropdown_button2/dropdown_button2.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; + +import '../../pages/churning/churning_rounds_selection_sheet.dart'; +import '../../providers/churning/churning_service_provider.dart'; +import '../../themes/stack_colors.dart'; +import '../../utilities/assets.dart'; +import '../../utilities/constants.dart'; +import '../../utilities/extensions/extensions.dart'; +import '../../utilities/text_styles.dart'; +import '../../widgets/custom_buttons/app_bar_icon_button.dart'; +import '../../widgets/custom_buttons/checkbox_text_button.dart'; +import '../../widgets/desktop/desktop_app_bar.dart'; +import '../../widgets/desktop/desktop_dialog.dart'; +import '../../widgets/desktop/desktop_dialog_close_button.dart'; +import '../../widgets/desktop/desktop_scaffold.dart'; +import '../../widgets/desktop/primary_button.dart'; +import '../../widgets/rounded_white_container.dart'; +import '../../widgets/stack_text_field.dart'; +import 'sub_widgets/churning_dialog.dart'; + +class DesktopChurningView extends ConsumerStatefulWidget { + const DesktopChurningView({ + super.key, + required this.walletId, + }); + + static const String routeName = "/desktopChurningView"; + + final String walletId; + + @override + ConsumerState createState() => _DesktopChurning(); +} + +class _DesktopChurning extends ConsumerState { + late final TextEditingController churningRoundController; + late final FocusNode churningRoundFocusNode; + + bool _enableStartButton = false; + + ChurnOption _option = ChurnOption.continuous; + + Future _startChurn() async { + final churningService = ref.read(pChurningService(widget.walletId)); + + final int rounds = _option == ChurnOption.continuous + ? 0 + : int.parse(churningRoundController.text); + + churningService.rounds = rounds; + + await showDialog( + context: context, + barrierDismissible: false, + builder: (context) { + return ChurnDialogView( + walletId: widget.walletId, + ); + }, + ); + } + + @override + void initState() { + churningRoundController = TextEditingController(); + + churningRoundFocusNode = FocusNode(); + + final rounds = ref.read(pChurningService(widget.walletId)).rounds; + + _option = rounds == 0 ? ChurnOption.continuous : ChurnOption.custom; + churningRoundController.text = rounds.toString(); + + _enableStartButton = churningRoundController.text.isNotEmpty; + + super.initState(); + } + + @override + void dispose() { + churningRoundController.dispose(); + churningRoundFocusNode.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + debugPrint("BUILD: $runtimeType"); + + return DesktopScaffold( + appBar: DesktopAppBar( + background: Theme.of(context).extension()!.popupBG, + isCompactHeight: true, + useSpacers: false, + leading: Expanded( + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + // const SizedBox( + // width: 32, + // ), + AppBarIconButton( + size: 32, + color: Theme.of(context) + .extension()! + .textFieldDefaultBG, + shadows: const [], + icon: SvgPicture.asset( + Assets.svg.arrowLeft, + width: 18, + height: 18, + color: Theme.of(context) + .extension()! + .topNavIconPrimary, + ), + onPressed: Navigator.of(context).pop, + ), + const SizedBox( + width: 15, + ), + SvgPicture.asset( + Assets.svg.churn, + width: 32, + height: 32, + color: Theme.of(context) + .extension()! + .textSubtitle1, + ), + const SizedBox( + width: 12, + ), + Text( + "Churning", + style: STextStyles.desktopH3(context), + ), + ], + ), + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () {}, + child: Row( + children: [ + SvgPicture.asset( + Assets.svg.circleQuestion, + color: Theme.of(context) + .extension()! + .radioButtonIconBorder, + ), + const SizedBox( + width: 8, + ), + RichText( + text: TextSpan( + text: "What is churning?", + style: STextStyles.richLink(context).copyWith( + fontSize: 16, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + showDialog( + context: context, + useSafeArea: false, + barrierDismissible: true, + builder: (context) { + return DesktopDialog( + maxWidth: 580, + maxHeight: double.infinity, + child: Padding( + padding: const EdgeInsets.only( + top: 10, + left: 20, + bottom: 20, + right: 10, + ), + child: Column( + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment + .spaceBetween, + children: [ + Text( + "What is churning?", + style: STextStyles.desktopH2( + context, + ), + ), + DesktopDialogCloseButton( + onPressedOverride: () => + Navigator.of(context) + .pop(true), + ), + ], + ), + const SizedBox( + height: 16, + ), + Text( + "Churning in a Monero wallet involves" + " sending Monero to oneself in multiple" + " transactions, which can enhance privacy" + " by making it harder for observers to " + "link your transactions. This process" + " re-mixes the funds within the network," + " helping obscure transaction history. " + "Churning is optional and mainly beneficial" + " in scenarios where maximum privacy is" + " desired or if you received the Monero from" + " a source from which you'd like to disassociate.", + style: + STextStyles.desktopTextMedium( + context, + ).copyWith( + color: Theme.of(context) + .extension()! + .textDark3, + ), + ), + ], + ), + ), + ); + }, + ); + }, + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + body: Row( + children: [ + Padding( + padding: const EdgeInsets.all(24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 460, + child: RoundedWhiteContainer( + child: Row( + children: [ + Text( + "Churning helps anonymize your coins by mixing them.", + style: + STextStyles.desktopTextExtraExtraSmall(context), + ), + ], + ), + ), + ), + const SizedBox( + height: 24, + ), + SizedBox( + width: 460, + child: RoundedWhiteContainer( + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Configuration", + style: + STextStyles.desktopTextExtraExtraSmall(context), + ), + const SizedBox( + height: 10, + ), + DropdownButtonHideUnderline( + child: DropdownButton2( + value: _option, + items: [ + ...ChurnOption.values.map( + (e) => DropdownMenuItem( + value: e, + child: Text( + e.name.capitalize(), + style: STextStyles.smallMed14(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ), + ), + ), + ), + ], + onChanged: (value) { + if (value is ChurnOption) { + setState(() { + _option = value; + }); + } + }, + isExpanded: true, + iconStyleData: IconStyleData( + icon: SvgPicture.asset( + Assets.svg.chevronDown, + width: 12, + height: 6, + color: Theme.of(context) + .extension()! + .textFieldActiveSearchIconRight, + ), + ), + dropdownStyleData: DropdownStyleData( + offset: const Offset(0, -10), + elevation: 0, + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .textFieldActiveBG, + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + ), + menuItemStyleData: const MenuItemStyleData( + padding: EdgeInsets.symmetric( + horizontal: 16, + vertical: 8, + ), + ), + ), + ), + if (_option == ChurnOption.custom) + const SizedBox( + height: 10, + ), + if (_option == ChurnOption.custom) + SizedBox( + width: 460, + child: RoundedWhiteContainer( + padding: EdgeInsets.zero, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + autocorrect: false, + enableSuggestions: false, + controller: churningRoundController, + focusNode: churningRoundFocusNode, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], + onChanged: (value) { + setState(() { + _enableStartButton = value.isNotEmpty; + }); + }, + style: STextStyles.field(context), + decoration: standardInputDecoration( + "Number of churns", + churningRoundFocusNode, + context, + desktopMed: true, + ).copyWith( + labelText: "Enter number of churns..", + ), + ), + ), + ], + ), + ), + ), + const SizedBox( + height: 20, + ), + CheckboxTextButton( + label: "Pause on errors", + initialValue: !ref + .read(pChurningService(widget.walletId)) + .ignoreErrors, + onChanged: (value) { + ref + .read(pChurningService(widget.walletId)) + .ignoreErrors = !value; + }, + ), + const SizedBox( + height: 20, + ), + PrimaryButton( + label: "Start", + enabled: _enableStartButton, + buttonHeight: ButtonHeight.l, + onPressed: _startChurn, + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages_desktop_specific/churning/sub_widgets/churning_dialog.dart b/lib/pages_desktop_specific/churning/sub_widgets/churning_dialog.dart new file mode 100644 index 000000000..726d73594 --- /dev/null +++ b/lib/pages_desktop_specific/churning/sub_widgets/churning_dialog.dart @@ -0,0 +1,328 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; + +import '../../../pages/churning/churn_error_dialog.dart'; +import '../../../providers/churning/churning_service_provider.dart'; +import '../../../themes/stack_colors.dart'; +import '../../../utilities/assets.dart'; +import '../../../utilities/text_styles.dart'; +import '../../../widgets/churning/churn_progress_item.dart'; +import '../../../widgets/desktop/desktop_dialog.dart'; +import '../../../widgets/desktop/desktop_dialog_close_button.dart'; +import '../../../widgets/desktop/primary_button.dart'; +import '../../../widgets/desktop/secondary_button.dart'; +import '../../../widgets/monero_chan_dance.dart'; +import '../../../widgets/rounded_container.dart'; +import '../../../widgets/rounded_white_container.dart'; + +class ChurnDialogView extends ConsumerStatefulWidget { + const ChurnDialogView({ + super.key, + required this.walletId, + }); + + final String walletId; + + @override + ConsumerState createState() => _ChurnDialogViewState(); +} + +class _ChurnDialogViewState extends ConsumerState { + Future _requestAndProcessCancel() async { + final bool? shouldCancel = await showDialog( + context: context, + barrierDismissible: false, + builder: (_) => DesktopDialog( + maxWidth: 580, + maxHeight: double.infinity, + child: Padding( + padding: const EdgeInsets.only( + left: 32, + right: 0, + top: 0, + bottom: 32, + ), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Cancel churning?", + style: STextStyles.desktopH3(context), + ), + DesktopDialogCloseButton( + onPressedOverride: () => Navigator.of(context).pop(false), + ), + ], + ), + Padding( + padding: const EdgeInsets.only( + left: 0, + right: 32, + top: 0, + bottom: 0, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Do you really want to cancel the churning process?", + style: STextStyles.smallMed14(context), + textAlign: TextAlign.left, + ), + const SizedBox(height: 40), + Row( + children: [ + Expanded( + child: SecondaryButton( + label: "No", + buttonHeight: ButtonHeight.l, + onPressed: () { + Navigator.of(context).pop(false); + }, + ), + ), + const SizedBox(width: 16), + Expanded( + child: PrimaryButton( + label: "Yes", + buttonHeight: ButtonHeight.l, + onPressed: () { + Navigator.of(context).pop(true); + }, + ), + ), + ], + ), + ], + ), + ), + ], + ), + ), + ), + ); + + if (shouldCancel == true && mounted) { + ref.read(pChurningService(widget.walletId)).stopChurning(); + + await WakelockPlus.disable(); + + return true; + } else { + return false; + } + } + + @override + void initState() { + super.initState(); + + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) ref.read(pChurningService(widget.walletId)).churn(); + }); + } + + @override + dispose() { + WakelockPlus.disable(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final bool _succeeded = ref.watch( + pChurningService(widget.walletId).select((s) => s.done), + ); + + final int _roundsCompleted = ref.watch( + pChurningService(widget.walletId).select((s) => s.roundsCompleted), + ); + + if (!Platform.isLinux) { + WakelockPlus.enable(); + } + + ref.listen( + pChurningService(widget.walletId).select((s) => s.lastSeenError), + (p, n) { + if (!ref.read(pChurningService(widget.walletId)).ignoreErrors && + n != null) { + if (context.mounted) { + showDialog( + context: context, + builder: (context) => ChurnErrorDialog( + error: n.toString(), + walletId: widget.walletId, + ), + ); + } + } + }, + ); + + return DesktopDialog( + maxHeight: 600, + child: SingleChildScrollView( + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only(left: 32), + child: Text( + "Churn progress", + style: STextStyles.desktopH2(context), + ), + ), + DesktopDialogCloseButton( + onPressedOverride: () async { + if (_succeeded) { + Navigator.of(context).pop(); + } else { + if (await _requestAndProcessCancel()) { + if (context.mounted) { + Navigator.of(context).pop(); + } + } + } + }, + ), + ], + ), + Padding( + padding: const EdgeInsets.only( + top: 20, + left: 32, + right: 32, + bottom: 32, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _roundsCompleted > 0 + ? RoundedWhiteContainer( + child: Text( + "Churn rounds completed: $_roundsCompleted", + style: STextStyles.w500_14(context).copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle1, + ), + textAlign: TextAlign.center, + ), + ) + : RoundedContainer( + color: Theme.of(context) + .extension()! + .snackBarBackError, + child: Text( + "Do not close this window. If you exit, " + "the process will be canceled.", + style: STextStyles.smallMed14(context).copyWith( + color: Theme.of(context) + .extension()! + .snackBarTextError, + ), + textAlign: TextAlign.center, + ), + ), + const SizedBox( + height: 20, + ), + const MoneroChanDance(), + const SizedBox( + height: 20, + ), + ProgressItem( + iconAsset: Assets.svg.alertCircle, + label: "Waiting for balance to unlock ${ref.watch( + pChurningService(widget.walletId) + .select((s) => s.confirmsInfo), + ) ?? ""}", + status: ref.watch( + pChurningService(widget.walletId) + .select((s) => s.waitingForUnlockedBalance), + ), + ), + const SizedBox( + height: 12, + ), + ProgressItem( + iconAsset: Assets.svg.churn, + label: "Creating churn transaction", + status: ref.watch( + pChurningService(widget.walletId) + .select((s) => s.makingChurnTransaction), + ), + ), + const SizedBox( + height: 12, + ), + ProgressItem( + iconAsset: Assets.svg.checkCircle, + label: "Complete", + status: ref.watch( + pChurningService(widget.walletId) + .select((s) => s.completedStatus), + ), + ), + const SizedBox( + height: 12, + ), + Row( + children: [ + if (_succeeded) + Expanded( + child: PrimaryButton( + buttonHeight: ButtonHeight.m, + label: "Churn again", + onPressed: ref + .read(pChurningService(widget.walletId)) + .churn, + ), + ), + if (_succeeded) + const SizedBox( + width: 16, + ), + if (!_succeeded) const Spacer(), + if (!_succeeded) + const SizedBox( + width: 16, + ), + Expanded( + child: SecondaryButton( + buttonHeight: ButtonHeight.m, + enabled: true, + label: _succeeded ? "Done" : "Cancel", + onPressed: () async { + if (_succeeded) { + Navigator.of(context).pop(); + } else { + if (await _requestAndProcessCancel()) { + if (context.mounted) { + Navigator.of(context).pop(); + } + } + } + }, + ), + ), + ], + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages_desktop_specific/coin_control/utxo_row.dart b/lib/pages_desktop_specific/coin_control/utxo_row.dart index ca33fe8f2..26204375c 100644 --- a/lib/pages_desktop_specific/coin_control/utxo_row.dart +++ b/lib/pages_desktop_specific/coin_control/utxo_row.dart @@ -11,6 +11,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; + import '../../db/isar/main_db.dart'; import '../../models/isar/models/isar_models.dart'; import '../../pages/coin_control/utxo_details_view.dart'; @@ -141,6 +142,11 @@ class _UtxoRowState extends ConsumerState { .getWallet(widget.walletId) .cryptoCurrency .minConfirms, + ref + .watch(pWallets) + .getWallet(widget.walletId) + .cryptoCurrency + .minCoinbaseConfirms, ) ? UTXOStatusIconStatus.confirmed : UTXOStatusIconStatus.unconfirmed, diff --git a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart index 63275921d..c6769e68a 100644 --- a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart +++ b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart @@ -206,7 +206,8 @@ class _StepScaffoldState extends ConsumerState { void sendFromStack() { final trade = ref.read(desktopExchangeModelProvider)!.trade!; final address = trade.payInAddress; - final coin = AppConfig.getCryptoCurrencyForTicker(trade.payInCurrency)!; + final coin = AppConfig.getCryptoCurrencyForTicker(trade.payInCurrency) ?? + AppConfig.getCryptoCurrencyByPrettyName(trade.payInCurrency); final amount = Decimal.parse(trade.payInAmount).toAmount( fractionDigits: coin.fractionDigits, ); diff --git a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart index 094ee452d..a7f76c7e8 100644 --- a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart +++ b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart @@ -357,6 +357,8 @@ class _DesktopStep2State extends ConsumerState { focusNode: _toFocusNode, style: STextStyles.field(context), onChanged: (value) { + ref.read(desktopExchangeModelProvider)!.recipientAddress = + _toController.text; widget.enableNextChanged.call( _next(), ); @@ -504,6 +506,8 @@ class _DesktopStep2State extends ConsumerState { focusNode: _refundFocusNode, style: STextStyles.field(context), onChanged: (value) { + ref.read(desktopExchangeModelProvider)!.refundAddress = + _refundController.text; widget.enableNextChanged.call( _next(), ); diff --git a/lib/pages_desktop_specific/desktop_menu.dart b/lib/pages_desktop_specific/desktop_menu.dart index 81b99fd8f..9c9b9cc1d 100644 --- a/lib/pages_desktop_specific/desktop_menu.dart +++ b/lib/pages_desktop_specific/desktop_menu.dart @@ -11,16 +11,15 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import '../app_config.dart'; import '../providers/desktop/current_desktop_menu_item.dart'; +import '../providers/providers.dart'; import '../themes/stack_colors.dart'; import '../utilities/assets.dart'; import '../utilities/text_styles.dart'; -import '../wallets/crypto_currency/crypto_currency.dart'; import '../widgets/desktop/desktop_tor_status_button.dart'; import '../widgets/desktop/living_stack_icon.dart'; import 'desktop_menu_item.dart'; @@ -60,6 +59,7 @@ class _DesktopMenuState extends ConsumerState { late final DMIController torButtonController; double _width = expandedWidth; + bool get _isMinimized => _width < expandedWidth; void updateSelectedMenuItem(DesktopMenuItemId idKey) { widget.onSelectionWillChange?.call(idKey); @@ -114,6 +114,10 @@ class _DesktopMenuState extends ConsumerState { @override Widget build(BuildContext context) { + final prefs = ref.watch(prefsChangeNotifierProvider); + + final showExchange = prefs.enableExchange; + return Material( color: Theme.of(context).extension()!.popupBG, child: AnimatedContainer( @@ -163,7 +167,7 @@ class _DesktopMenuState extends ConsumerState { onPressed: () { ref.read(currentDesktopMenuItemProvider.state).state = DesktopMenuItemId.settings; - ref.watch(selectedSettingsMenuItemStateProvider.state).state = + ref.read(selectedSettingsMenuItemStateProvider.state).state = 4; }, ), @@ -181,114 +185,134 @@ class _DesktopMenuState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ DesktopMenuItem( + key: const ValueKey('myStack'), duration: duration, icon: const DesktopMyStackIcon(), label: "My ${AppConfig.prefix}", value: DesktopMenuItemId.myStack, onChanged: updateSelectedMenuItem, controller: controllers[0], + isExpandedInitially: !_isMinimized, ), - if (AppConfig.hasFeature(AppFeature.swap)) + if (AppConfig.hasFeature(AppFeature.swap) && + showExchange) ...[ const SizedBox( height: 2, ), - if (AppConfig.hasFeature(AppFeature.swap)) DesktopMenuItem( + key: const ValueKey('swap'), duration: duration, icon: const DesktopExchangeIcon(), label: "Swap", value: DesktopMenuItemId.exchange, onChanged: updateSelectedMenuItem, controller: controllers[1], + isExpandedInitially: !_isMinimized, ), - if (AppConfig.hasFeature(AppFeature.buy)) + ], + if (AppConfig.hasFeature(AppFeature.buy) && + showExchange) ...[ const SizedBox( height: 2, ), - if (AppConfig.hasFeature(AppFeature.buy)) DesktopMenuItem( + key: const ValueKey('buy'), duration: duration, icon: const DesktopBuyIcon(), label: "Buy crypto", value: DesktopMenuItemId.buy, onChanged: updateSelectedMenuItem, controller: controllers[2], + isExpandedInitially: !_isMinimized, ), + ], const SizedBox( height: 2, ), DesktopMenuItem( + key: const ValueKey('notifications'), duration: duration, icon: const DesktopNotificationsIcon(), label: "Notifications", value: DesktopMenuItemId.notifications, onChanged: updateSelectedMenuItem, controller: controllers[3], + isExpandedInitially: !_isMinimized, ), const SizedBox( height: 2, ), DesktopMenuItem( + key: const ValueKey('addressBook'), duration: duration, icon: const DesktopAddressBookIcon(), label: "Address Book", value: DesktopMenuItemId.addressBook, onChanged: updateSelectedMenuItem, controller: controllers[4], + isExpandedInitially: !_isMinimized, ), const SizedBox( height: 2, ), DesktopMenuItem( + key: const ValueKey('settings'), duration: duration, icon: const DesktopSettingsIcon(), label: "Settings", value: DesktopMenuItemId.settings, onChanged: updateSelectedMenuItem, controller: controllers[5], + isExpandedInitially: !_isMinimized, ), const SizedBox( height: 2, ), DesktopMenuItem( + key: const ValueKey('support'), duration: duration, icon: const DesktopSupportIcon(), label: "Support", value: DesktopMenuItemId.support, onChanged: updateSelectedMenuItem, controller: controllers[6], + isExpandedInitially: !_isMinimized, ), const SizedBox( height: 2, ), DesktopMenuItem( + key: const ValueKey('about'), duration: duration, icon: const DesktopAboutIcon(), label: "About", value: DesktopMenuItemId.about, onChanged: updateSelectedMenuItem, controller: controllers[7], + isExpandedInitially: !_isMinimized, ), const Spacer(), if (!Platform.isIOS) DesktopMenuItem( + key: const ValueKey('exit'), duration: duration, labelLength: 123, icon: const DesktopExitIcon(), label: "Exit", value: 7, onChanged: (_) { - // todo: save stuff/ notify before exit? - if (AppConfig.coins - .where((e) => e is Monero || e is Wownero) - .isNotEmpty) { - // hack to insta kill because xmr/wow native lib code sucks - exit(0); - } else { - SystemNavigator.pop(); - } + // // todo: save stuff/ notify before exit? + // if (AppConfig.coins + // .where((e) => e is Monero || e is Wownero) + // .isNotEmpty) { + // // hack to insta kill because xmr/wow native lib code sucks + exit(0); + // } else { + // SystemNavigator.pop(); + // } }, controller: controllers[8], + isExpandedInitially: !_isMinimized, ), ], ), diff --git a/lib/pages_desktop_specific/desktop_menu_item.dart b/lib/pages_desktop_specific/desktop_menu_item.dart index 1fde1281c..ea0d69a81 100644 --- a/lib/pages_desktop_specific/desktop_menu_item.dart +++ b/lib/pages_desktop_specific/desktop_menu_item.dart @@ -24,6 +24,9 @@ import 'desktop_menu.dart'; class DMIController { VoidCallback? toggle; + + DMIController(); + void dispose() { toggle = null; } @@ -237,6 +240,7 @@ class DesktopMenuItem extends ConsumerStatefulWidget { required this.duration, this.labelLength = 125, this.controller, + required this.isExpandedInitially, }); final Widget icon; @@ -246,6 +250,7 @@ class DesktopMenuItem extends ConsumerStatefulWidget { final Duration duration; final double labelLength; final DMIController? controller; + final bool isExpandedInitially; @override ConsumerState> createState() => _DesktopMenuItemState(); @@ -287,11 +292,17 @@ class _DesktopMenuItemState extends ConsumerState> labelLength = widget.labelLength; controller = widget.controller; + _iconOnly = !widget.isExpandedInitially; controller?.toggle = toggle; animationController = AnimationController( vsync: this, duration: duration, - )..forward(); + ); + if (_iconOnly) { + animationController.value = 0; + } else { + animationController.value = 1; + } super.initState(); } diff --git a/lib/pages_desktop_specific/my_stack_view/coin_wallets_table.dart b/lib/pages_desktop_specific/my_stack_view/coin_wallets_table.dart index e440af8fe..8da907ca3 100644 --- a/lib/pages_desktop_specific/my_stack_view/coin_wallets_table.dart +++ b/lib/pages_desktop_specific/my_stack_view/coin_wallets_table.dart @@ -10,17 +10,21 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'wallet_view/desktop_wallet_view.dart'; + import '../../providers/global/active_wallet_provider.dart'; +import '../../providers/global/node_service_provider.dart'; +import '../../providers/global/prefs_provider.dart'; import '../../providers/global/wallets_provider.dart'; import '../../themes/stack_colors.dart'; import '../../utilities/constants.dart'; import '../../utilities/show_loading.dart'; +import '../../utilities/show_node_tor_settings_mismatch.dart'; import '../../utilities/util.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; -import '../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; +import '../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../widgets/rounded_container.dart'; import '../../widgets/wallet_info_row/wallet_info_row.dart'; +import 'wallet_view/desktop_wallet_view.dart'; class CoinWalletsTable extends ConsumerWidget { const CoinWalletsTable({ @@ -80,8 +84,24 @@ class CoinWalletsTable extends ConsumerWidget { final wallet = ref.read(pWallets).getWallet(walletIds[i]); + + final canContinue = + await checkShowNodeTorSettingsMismatch( + context: context, + currency: coin, + prefs: ref.read(prefsChangeNotifierProvider), + nodeService: + ref.read(nodeServiceChangeNotifierProvider), + allowCancel: true, + rootNavigator: Util.isDesktop, + ); + + if (!canContinue) { + return; + } + final Future loadFuture; - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { loadFuture = wallet .init() .then((value) async => await (wallet).open()); diff --git a/lib/pages_desktop_specific/my_stack_view/dialogs/desktop_expanding_wallet_card.dart b/lib/pages_desktop_specific/my_stack_view/dialogs/desktop_expanding_wallet_card.dart index 5246a6b16..aa3244ece 100644 --- a/lib/pages_desktop_specific/my_stack_view/dialogs/desktop_expanding_wallet_card.dart +++ b/lib/pages_desktop_specific/my_stack_view/dialogs/desktop_expanding_wallet_card.dart @@ -11,19 +11,18 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/svg.dart'; -import '../../../models/isar/models/ethereum/eth_contract.dart'; + +import '../../../pages/wallets_view/wallets_overview.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/assets.dart'; import '../../../utilities/constants.dart'; import '../../../utilities/text_styles.dart'; -import '../../../wallets/wallet/wallet.dart'; import '../../../widgets/animated_widgets/rotate_icon.dart'; import '../../../widgets/expandable.dart'; import '../../../widgets/rounded_white_container.dart'; import '../../../widgets/wallet_card.dart'; import '../../../widgets/wallet_info_row/sub_widgets/wallet_info_row_balance.dart'; import '../../../widgets/wallet_info_row/sub_widgets/wallet_info_row_coin_icon.dart'; -import 'package:tuple/tuple.dart'; class DesktopExpandingWalletCard extends StatefulWidget { const DesktopExpandingWalletCard({ @@ -32,7 +31,7 @@ class DesktopExpandingWalletCard extends StatefulWidget { required this.navigatorState, }); - final Tuple2> data; + final WalletListItemData data; final NavigatorState navigatorState; @override @@ -48,9 +47,9 @@ class _DesktopExpandingWalletCardState @override void initState() { - if (widget.data.item1.cryptoCurrency.hasTokenSupport) { + if (widget.data.wallet.cryptoCurrency.hasTokenSupport) { tokenContractAddresses.addAll( - widget.data.item2.map((e) => e.address), + widget.data.contracts.map((e) => e.address), ); } @@ -63,7 +62,7 @@ class _DesktopExpandingWalletCardState padding: EdgeInsets.zero, borderColor: Theme.of(context).extension()!.backgroundAppBar, child: Expandable( - initialState: widget.data.item1.cryptoCurrency.hasTokenSupport + initialState: widget.data.wallet.cryptoCurrency.hasTokenSupport ? ExpandableState.expanded : ExpandableState.collapsed, controller: expandableController, @@ -89,13 +88,13 @@ class _DesktopExpandingWalletCardState child: Row( children: [ WalletInfoCoinIcon( - coin: widget.data.item1.info.coin, + coin: widget.data.wallet.info.coin, ), const SizedBox( width: 12, ), Text( - widget.data.item1.info.name, + widget.data.wallet.info.name, style: STextStyles.desktopTextExtraSmall(context) .copyWith( color: Theme.of(context) @@ -109,7 +108,7 @@ class _DesktopExpandingWalletCardState Expanded( flex: 4, child: WalletInfoRowBalance( - walletId: widget.data.item1.walletId, + walletId: widget.data.wallet.walletId, ), ), ], @@ -173,7 +172,7 @@ class _DesktopExpandingWalletCardState bottom: 14, ), child: SimpleWalletCard( - walletId: widget.data.item1.walletId, + walletId: widget.data.wallet.walletId, popPrevious: true, desktopNavigatorState: widget.navigatorState, ), @@ -187,7 +186,7 @@ class _DesktopExpandingWalletCardState bottom: 14, ), child: SimpleWalletCard( - walletId: widget.data.item1.walletId, + walletId: widget.data.wallet.walletId, contractAddress: e, popPrevious: true, desktopNavigatorState: widget.navigatorState, diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_summary_table.dart b/lib/pages_desktop_specific/my_stack_view/wallet_summary_table.dart index 983f01a42..5df82cc0f 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_summary_table.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_summary_table.dart @@ -19,15 +19,20 @@ import '../../providers/providers.dart'; import '../../themes/coin_icon_provider.dart'; import '../../themes/stack_colors.dart'; import '../../utilities/amount/amount.dart'; +import '../../utilities/show_loading.dart'; +import '../../utilities/show_node_tor_settings_mismatch.dart'; import '../../utilities/text_styles.dart'; +import '../../utilities/util.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; import '../../wallets/isar/providers/all_wallets_info_provider.dart'; +import '../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../widgets/breathing.dart'; import '../../widgets/conditional_parent.dart'; import '../../widgets/desktop/desktop_dialog.dart'; import '../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../widgets/dialogs/tor_warning_dialog.dart'; import '../../widgets/rounded_white_container.dart'; +import 'wallet_view/desktop_wallet_view.dart'; class WalletSummaryTable extends ConsumerStatefulWidget { const WalletSummaryTable({super.key}); @@ -86,10 +91,7 @@ class DesktopWalletSummaryRow extends ConsumerStatefulWidget { class _DesktopWalletSummaryRowState extends ConsumerState { - bool _hovering = false; - - void _onPressed() async { - // Check if Tor is enabled... + Future _checkTor() async { if (ref.read(prefsChangeNotifierProvider).useTor) { // ... and if the coin supports Tor. if (!widget.coin.torSupport) { @@ -106,44 +108,110 @@ class _DesktopWalletSummaryRowState } } } + } - showDialog( - context: context, - builder: (_) => DesktopDialog( - maxHeight: 600, - maxWidth: 700, - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + bool get goStraightIntoWallet => + Util.isDesktop && widget.walletCount == 1 && !widget.coin.hasTokenSupport; + + bool _buttonLock = false; + Future _onPressedSingleWalletDesktop() async { + if (_buttonLock) return; + _buttonLock = true; + try { + await _checkTor(); + + if (mounted) { + final wallet = ref.read(pWallets).wallets.firstWhere( + (e) => e.cryptoCurrency.identifier == widget.coin.identifier); + + final canContinue = await checkShowNodeTorSettingsMismatch( + context: context, + currency: wallet.cryptoCurrency, + prefs: ref.read(prefsChangeNotifierProvider), + nodeService: ref.read(nodeServiceChangeNotifierProvider), + allowCancel: true, + rootNavigator: Util.isDesktop, + ); + + if (!canContinue) { + return; + } + + final Future loadFuture; + if (wallet is LibMoneroWallet) { + loadFuture = + wallet.init().then((value) async => await (wallet).open()); + } else { + loadFuture = wallet.init(); + } + await showLoading( + whileFuture: loadFuture, + context: context, + message: 'Opening ${wallet.info.name}', + rootNavigator: Util.isDesktop, + ); + + if (mounted) { + await Navigator.of(context).pushNamed( + DesktopWalletView.routeName, + arguments: wallet.walletId, + ); + } + } + } finally { + _buttonLock = false; + } + } + + void _onPressed() async { + if (_buttonLock) return; + _buttonLock = true; + try { + // Check if Tor is enabled... + await _checkTor(); + + if (mounted) { + await showDialog( + context: context, + builder: (_) => DesktopDialog( + maxHeight: 600, + maxWidth: 700, + child: Column( children: [ - Padding( - padding: const EdgeInsets.only(left: 32), - child: Text( - "${widget.coin.prettyName} (${widget.coin.ticker}) wallets", - style: STextStyles.desktopH3(context), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only(left: 32), + child: Text( + "${widget.coin.prettyName} (${widget.coin.ticker}) wallets", + style: STextStyles.desktopH3(context), + ), + ), + const DesktopDialogCloseButton(), + ], + ), + Expanded( + child: Padding( + padding: const EdgeInsets.only( + left: 32, + right: 32, + bottom: 32, + ), + child: WalletsOverview( + coin: widget.coin, + navigatorState: Navigator.of(context), + ), ), ), - const DesktopDialogCloseButton(), ], ), - Expanded( - child: Padding( - padding: const EdgeInsets.only( - left: 32, - right: 32, - bottom: 32, - ), - child: WalletsOverview( - coin: widget.coin, - navigatorState: Navigator.of(context), - ), - ), - ), - ], - ), - ), - ); + ), + ); + } + } finally { + _buttonLock = false; + } } @override @@ -152,7 +220,8 @@ class _DesktopWalletSummaryRowState child: RoundedWhiteContainer( padding: const EdgeInsets.all(20), hoverColor: Colors.transparent, - onPressed: _onPressed, + onPressed: + goStraightIntoWallet ? _onPressedSingleWalletDesktop : _onPressed, child: Row( children: [ Expanded( diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart index 0105c0006..9893a6cae 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart @@ -21,6 +21,7 @@ import 'package:isar/isar.dart'; import '../../../db/sqlite/firo_cache.dart'; import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; import '../../../models/isar/models/isar_models.dart'; +import '../../../models/keys/view_only_wallet_data.dart'; import '../../../pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart'; import '../../../pages/token_view/my_tokens_view.dart'; import '../../../pages/wallet_view/sub_widgets/transactions_list.dart'; @@ -44,6 +45,7 @@ import '../../../utilities/wallet_tools.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../wallets/wallet/impl/banano_wallet.dart'; import '../../../wallets/wallet/impl/firo_wallet.dart'; +import '../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../widgets/custom_buttons/blue_text_button.dart'; import '../../../widgets/desktop/desktop_app_bar.dart'; @@ -168,6 +170,11 @@ class _DesktopWalletViewState extends ConsumerState { final monke = wallet is BananoWallet ? wallet.getMonkeyImageBytes() : null; + // if the view only wallet watches a single address there are no keys of any kind + final showKeysButton = !(wallet is ViewOnlyOptionInterface && + wallet.isViewOnly && + wallet.viewOnlyType == ViewOnlyWalletType.addressOnly); + return DesktopScaffold( appBar: DesktopAppBar( background: Theme.of(context).extension()!.popupBG, @@ -216,6 +223,19 @@ class _DesktopWalletViewState extends ConsumerState { ), ), ), + if (ref.watch(pWalletInfo(widget.walletId)).isViewOnly) + const SizedBox( + width: 20, + ), + if (ref.watch(pWalletInfo(widget.walletId)).isViewOnly) + Text( + "(View only)", + style: STextStyles.desktopTextExtraSmall(context).copyWith( + color: Theme.of(context) + .extension()! + .textFieldActiveSearchIconLeft, + ), + ), if (kDebugMode) const Spacer(), if (kDebugMode) Column( @@ -312,12 +332,14 @@ class _DesktopWalletViewState extends ConsumerState { walletId: widget.walletId, eventBus: eventBus, ), - const SizedBox( - width: 2, - ), - WalletKeysButton( - walletId: widget.walletId, - ), + if (showKeysButton) + const SizedBox( + width: 2, + ), + if (showKeysButton) + WalletKeysButton( + walletId: widget.walletId, + ), const SizedBox( width: 2, ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_attention_delete_wallet.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_attention_delete_wallet.dart index 21d4bfbf5..16032b36b 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_attention_delete_wallet.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_attention_delete_wallet.dart @@ -10,18 +10,21 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:tuple/tuple.dart'; + import '../../../../app_config.dart'; -import 'delete_wallet_keys_popup.dart'; +import '../../../../pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_view_only_wallet_keys_view.dart'; import '../../../../providers/global/wallets_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; +import '../../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; import '../../../../widgets/desktop/desktop_dialog.dart'; import '../../../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/rounded_container.dart'; -import 'package:tuple/tuple.dart'; +import 'delete_wallet_keys_popup.dart'; class DesktopAttentionDeleteWallet extends ConsumerStatefulWidget { const DesktopAttentionDeleteWallet({ @@ -114,6 +117,58 @@ class _DesktopAttentionDeleteWallet onPressed: () async { final wallet = ref.read(pWallets).getWallet(widget.walletId); + + if (wallet is ViewOnlyOptionInterface && + wallet.isViewOnly) { + final data = await wallet.getViewOnlyWalletData(); + if (context.mounted) { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (builder) => DesktopDialog( + maxWidth: 614, + maxHeight: double.infinity, + child: Column( + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only( + left: 32, + ), + child: Text( + "Wallet keys", + style: STextStyles.desktopH3( + context, + ), + ), + ), + DesktopDialogCloseButton( + onPressedOverride: () { + Navigator.of( + context, + rootNavigator: true, + ).pop(); + }, + ), + ], + ), + Padding( + padding: const EdgeInsets.all(32), + child: DeleteViewOnlyWalletKeysView( + walletId: widget.walletId, + data: data, + ), + ), + ], + ), + ), + ), + ); + } + } else + // TODO: [prio=med] handle other types wallet deletion // All wallets currently are mnemonic based if (wallet is MnemonicInterface) { diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart index 3b25f0dad..6326a6474 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart @@ -8,9 +8,9 @@ * */ -import 'package:cw_core/monero_transaction_priority.dart'; import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; +import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; @@ -86,7 +86,7 @@ class _DesktopFeeDropDownState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.fast.raw!, + lib_monero.TransactionPriority.high.value, ); ref.read(feeSheetSessionCacheProvider).fast[amount] = fee; } else if (coin is Firo) { @@ -136,7 +136,7 @@ class _DesktopFeeDropDownState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.regular.raw!, + lib_monero.TransactionPriority.medium.value, ); ref.read(feeSheetSessionCacheProvider).average[amount] = fee; } else if (coin is Firo) { @@ -186,7 +186,7 @@ class _DesktopFeeDropDownState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.slow.raw!, + lib_monero.TransactionPriority.normal.value, ); ref.read(feeSheetSessionCacheProvider).slow[amount] = fee; } else if (coin is Firo) { diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_receive.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_receive.dart index 09de81b26..b90cc2f6a 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_receive.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_receive.dart @@ -19,6 +19,7 @@ import 'package:isar/isar.dart'; import 'package:tuple/tuple.dart'; import '../../../../models/isar/models/isar_models.dart'; +import '../../../../models/keys/view_only_wallet_data.dart'; import '../../../../notifications/show_flush_bar.dart'; import '../../../../pages/receive_view/generate_receiving_uri_qr_code_view.dart'; import '../../../../providers/db/main_db_provider.dart'; @@ -38,8 +39,10 @@ import '../../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../../wallets/wallet/impl/bitcoin_wallet.dart'; import '../../../../wallets/wallet/intermediate/bip39_hd_wallet.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/bcash_interface.dart'; +import '../../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/multi_address_interface.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/spark_interface.dart'; +import '../../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; import '../../../../widgets/conditional_parent.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/custom_loading_overlay.dart'; @@ -104,15 +107,38 @@ class _DesktopReceiveState extends ConsumerState { final Address? address; if (wallet is Bip39HDWallet && wallet is! BCashInterface) { - final type = DerivePathType.values.firstWhere( - (e) => e.getAddressType() == _walletAddressTypes[_currentIndex], - ); + DerivePathType? type; + if (wallet.isViewOnly && wallet is ExtendedKeysInterface) { + final voData = await wallet.getViewOnlyWalletData() + as ExtendedKeysViewOnlyWalletData; + for (final t in wallet.cryptoCurrency.supportedDerivationPathTypes) { + final testPath = wallet.cryptoCurrency.constructDerivePath( + derivePathType: t, + chain: 0, + index: 0, + ); + if (testPath.startsWith(voData.xPubs.first.path)) { + type = t; + break; + } + } + } else { + type = DerivePathType.values.firstWhere( + (e) => e.getAddressType() == _walletAddressTypes[_currentIndex], + ); + } address = await wallet.generateNextReceivingAddress( - derivePathType: type, + derivePathType: type!, ); - await ref.read(mainDBProvider).isar.writeTxn(() async { - await ref.read(mainDBProvider).isar.addresses.put(address!); + final isar = ref.read(mainDBProvider).isar; + await isar.writeTxn(() async { + await isar.addresses.put(address!); }); + final info = ref.read(pWalletInfo(walletId)); + await info.updateReceivingAddress( + newAddress: address.value, + isar: isar, + ); } else { await wallet.generateNewReceivingAddress(); address = null; @@ -172,8 +198,6 @@ class _DesktopReceiveState extends ConsumerState { } } - StreamSubscription? _streamSub; - @override void initState() { walletId = widget.walletId; @@ -181,8 +205,15 @@ class _DesktopReceiveState extends ConsumerState { clipboard = widget.clipboard; final wallet = ref.read(pWallets).getWallet(walletId); supportsSpark = ref.read(pWallets).getWallet(walletId) is SparkInterface; - showMultiType = supportsSpark || - ref.read(pWallets).getWallet(walletId) is MultiAddressInterface; + + if (wallet is ViewOnlyOptionInterface && wallet.isViewOnly) { + showMultiType = false; + } else { + showMultiType = supportsSpark || + (wallet is! BCashInterface && + wallet is Bip39HDWallet && + wallet.supportedAddressTypes.length > 1); + } _walletAddressTypes.add(wallet.info.mainAddressType); @@ -236,7 +267,9 @@ class _DesktopReceiveState extends ConsumerState { @override void dispose() { - _streamSub?.cancel(); + for (final subscription in _addressSubMap.values) { + subscription.cancel(); + } super.dispose(); } @@ -251,6 +284,18 @@ class _DesktopReceiveState extends ConsumerState { address = ref.watch(pWalletReceivingAddress(walletId)); } + final wallet = + ref.watch(pWallets.select((value) => value.getWallet(walletId))); + + final bool canGen; + if (wallet is ViewOnlyOptionInterface && + wallet.isViewOnly && + wallet.viewOnlyType == ViewOnlyWalletType.addressOnly) { + canGen = false; + } else { + canGen = (wallet is MultiAddressInterface || supportsSpark); + } + return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ @@ -422,16 +467,12 @@ class _DesktopReceiveState extends ConsumerState { ), ), - if (ref.watch(pWallets.select((value) => value.getWallet(walletId))) - is MultiAddressInterface || - supportsSpark) + if (canGen) const SizedBox( height: 20, ), - if (ref.watch(pWallets.select((value) => value.getWallet(walletId))) - is MultiAddressInterface || - supportsSpark) + if (canGen) SecondaryButton( buttonHeight: ButtonHeight.l, onPressed: supportsSpark && @@ -446,7 +487,7 @@ class _DesktopReceiveState extends ConsumerState { Center( child: QR( data: AddressUtils.buildUriString( - coin, + coin.uriScheme, address, {}, ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart index e5cc93d92..7fb764cfa 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart @@ -10,7 +10,7 @@ import 'dart:async'; -import 'package:cw_core/monero_transaction_priority.dart'; +import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:decimal/decimal.dart'; import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; @@ -18,6 +18,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import '../../../../models/isar/models/blockchain_data/utxo.dart'; import '../../../../models/isar/models/contact_entry.dart'; import '../../../../models/paynym/paynym_account_lite.dart'; import '../../../../models/send_view_auto_fill_data.dart'; @@ -59,11 +60,13 @@ import '../../../../widgets/desktop/desktop_dialog.dart'; import '../../../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../../../widgets/desktop/desktop_fee_dialog.dart'; import '../../../../widgets/desktop/primary_button.dart'; +import '../../../../widgets/desktop/qr_code_scanner_dialog.dart'; import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/dialogs/firo_exchange_address_dialog.dart'; import '../../../../widgets/fee_slider.dart'; import '../../../../widgets/icon_widgets/addressbook_icon.dart'; import '../../../../widgets/icon_widgets/clipboard_icon.dart'; +import '../../../../widgets/icon_widgets/qrcode_icon.dart'; import '../../../../widgets/icon_widgets/x_icon.dart'; import '../../../../widgets/rounded_container.dart'; import '../../../../widgets/stack_text_field.dart'; @@ -140,6 +143,33 @@ class _DesktopSendState extends ConsumerState { "Calculating...", ]; + Future scanWebcam() async { + try { + await showDialog( + context: context, + builder: (context) { + return QrCodeScannerDialog( + onQrCodeDetected: (qrCodeData) { + try { + _processQrCodeData(qrCodeData); + } catch (e, s) { + Logging.instance.log( + "Error processing QR code data: $e\n$s", + level: LogLevel.Error, + ); + } + }, + ); + }, + ); + } catch (e, s) { + Logging.instance.log( + "Error opening QR code scanner dialog: $e\n$s", + level: LogLevel.Error, + ); + } + } + Future previewSend() async { final wallet = ref.read(pWallets).getWallet(walletId); @@ -481,7 +511,7 @@ class _DesktopSendState extends ConsumerState { ); } } catch (e, s) { - Logging.instance.log("Desktop send: $e\n$s", level: LogLevel.Warning); + Logging.instance.log("Desktop send: $e\n$s", level: LogLevel.Error); if (mounted) { // pop building dialog Navigator.of( @@ -629,58 +659,44 @@ class _DesktopSendState extends ConsumerState { // return null; // } - Future scanQr() async { + void _processQrCodeData(String qrCodeData) { try { - if (FocusScope.of(context).hasFocus) { - FocusScope.of(context).unfocus(); - await Future.delayed(const Duration(milliseconds: 75)); - } - - final qrResult = await scanner.scan(); - - Logging.instance.log( - "qrResult content: ${qrResult.rawContent}", - level: LogLevel.Info, + final paymentData = AddressUtils.parsePaymentUri( + qrCodeData, + logging: Logging.instance, ); - final results = AddressUtils.parseUri(qrResult.rawContent); - - Logging.instance.log("qrResult parsed: $results", level: LogLevel.Info); - - if (results.isNotEmpty && results["scheme"] == coin.uriScheme) { - // auto fill address - _address = results["address"] ?? ""; + if (paymentData != null && + paymentData.coin?.uriScheme == coin.uriScheme) { + // Auto fill address. + _address = paymentData.address.trim(); sendToController.text = _address!; - // autofill notes field - if (results["message"] != null) { - _note = results["message"]!; - } else if (results["label"] != null) { - _note = results["label"]!; - } - - // autofill amount field - if (results["amount"] != null) { - final amount = Decimal.parse(results["amount"]!).toAmount( + // Amount. + if (paymentData.amount != null) { + final Amount amount = Decimal.parse(paymentData.amount!).toAmount( fractionDigits: coin.fractionDigits, ); - cryptoAmountController.text = ref - .read(pAmountFormatter(coin)) - .format(amount, withUnitName: false); + cryptoAmountController.text = ref.read(pAmountFormatter(coin)).format( + amount, + withUnitName: false, + ); ref.read(pSendAmount.notifier).state = amount; } + // Note/message. + if (paymentData.message != null) { + _note = paymentData.message; + } else if (paymentData.label != null) { + _note = paymentData.label; + } + + _setValidAddressProviders(_address); setState(() { _addressToggleFlag = sendToController.text.isNotEmpty; }); - - // now check for non standard encoded basic address - } else if (ref - .read(pWallets) - .getWallet(walletId) - .cryptoCurrency - .validateAddress(qrResult.rawContent)) { - _address = qrResult.rawContent; + } else { + _address = qrCodeData.split("\n").first.trim(); sendToController.text = _address ?? ""; _setValidAddressProviders(_address); @@ -688,13 +704,9 @@ class _DesktopSendState extends ConsumerState { _addressToggleFlag = sendToController.text.isNotEmpty; }); } - } on PlatformException catch (e, s) { - // here we ignore the exception caused by not giving permission - // to use the camera to scan a qr code - Logging.instance.log( - "Failed to get camera permissions while trying to scan qr code in SendView: $e\n$s", - level: LogLevel.Warning, - ); + } catch (e, s) { + Logging.instance + .log("Error processing QR code data: $e\n$s", level: LogLevel.Error); } } @@ -737,18 +749,70 @@ class _DesktopSendState extends ConsumerState { content = content.substring(0, content.indexOf("\n")); } - if (coin is Epiccash) { - // strip http:// and https:// if content contains @ - content = formatAddress(content); + try { + final paymentData = AddressUtils.parsePaymentUri( + content, + logging: Logging.instance, + ); + if (paymentData != null && + paymentData.coin?.uriScheme == coin.uriScheme) { + // auto fill address + _address = paymentData.address; + sendToController.text = _address!; + + // autofill notes field. + if (paymentData.message != null) { + _note = paymentData.message; + } else if (paymentData.label != null) { + _note = paymentData.label; + } + + // autofill amoutn field + if (paymentData.amount != null) { + final amount = Decimal.parse(paymentData.amount!).toAmount( + fractionDigits: coin.fractionDigits, + ); + cryptoAmountController.text = ref + .read(pAmountFormatter(coin)) + .format(amount, withUnitName: false); + ref.read(pSendAmount.notifier).state = amount; + } + + // Trigger validation after pasting. + _setValidAddressProviders(_address); + setState(() { + _addressToggleFlag = sendToController.text.isNotEmpty; + }); + } else { + content = content.split("\n").first.trim(); + if (coin is Epiccash) { + content = AddressUtils().formatAddress(content); + } + + sendToController.text = content; + _address = content; + + _setValidAddressProviders(_address); + setState(() { + _addressToggleFlag = sendToController.text.isNotEmpty; + }); + } + } catch (e) { + // If parsing fails, treat it as a plain address. + if (coin is Epiccash) { + // strip http:// and https:// if content contains @ + content = AddressUtils().formatAddress(content); + } + + sendToController.text = content; + _address = content; + + // Trigger validation after pasting. + _setValidAddressProviders(_address); + setState(() { + _addressToggleFlag = sendToController.text.isNotEmpty; + }); } - - sendToController.text = content; - _address = content; - - _setValidAddressProviders(_address); - setState(() { - _addressToggleFlag = sendToController.text.isNotEmpty; - }); } } @@ -817,30 +881,45 @@ class _DesktopSendState extends ConsumerState { ref.read(pSendAmount.notifier).state = amount; } - Future sendAllTapped() async { - final info = ref.read(pWalletInfo(walletId)); + String _getSendAllTitle(bool showCoinControl, Set selectedUTXOs) { + if (showCoinControl && selectedUTXOs.isNotEmpty) { + return "Send all selected"; + } - if (coin is Firo) { + return "Send all ${coin.ticker}"; + } + + Amount _selectedUtxosAmount(Set utxos) => Amount( + rawValue: + utxos.map((e) => BigInt.from(e.value)).reduce((v, e) => v += e), + fractionDigits: ref.read(pWalletCoin(walletId)).fractionDigits, + ); + + Future _sendAllTapped(bool showCoinControl) async { + final Amount amount; + + if (showCoinControl && ref.read(desktopUseUTXOs).isNotEmpty) { + amount = _selectedUtxosAmount(ref.read(desktopUseUTXOs)); + } else if (coin is Firo) { switch (ref.read(publicPrivateBalanceStateProvider.state).state) { case FiroType.public: - cryptoAmountController.text = info.cachedBalance.spendable.decimal - .toStringAsFixed(coin.fractionDigits); + amount = ref.read(pWalletBalance(walletId)).spendable; break; case FiroType.lelantus: - cryptoAmountController.text = info - .cachedBalanceSecondary.spendable.decimal - .toStringAsFixed(coin.fractionDigits); + amount = ref.read(pWalletBalanceSecondary(walletId)).spendable; break; case FiroType.spark: - cryptoAmountController.text = info - .cachedBalanceTertiary.spendable.decimal - .toStringAsFixed(coin.fractionDigits); + amount = ref.read(pWalletBalanceTertiary(walletId)).spendable; break; } } else { - cryptoAmountController.text = info.cachedBalance.spendable.decimal - .toStringAsFixed(coin.fractionDigits); + amount = ref.read(pWalletBalance(walletId)).spendable; } + + cryptoAmountController.text = ref.read(pAmountFormatter(coin)).format( + amount, + withUnitName: false, + ); } void _showDesktopCoinControl() async { @@ -1165,8 +1244,11 @@ class _DesktopSendState extends ConsumerState { ), if (coin is! Ethereum && coin is! Tezos) CustomTextButton( - text: "Send all ${coin.ticker}", - onTap: sendAllTapped, + text: _getSendAllTitle( + showCoinControl, + ref.watch(desktopUseUTXOs), + ), + onTap: () => _sendAllTapped(showCoinControl), ), ], ), @@ -1476,12 +1558,16 @@ class _DesktopSendState extends ConsumerState { }, child: const AddressBookIcon(), ), - // if (sendToController.text.isEmpty) - // TextFieldIconButton( - // key: const Key("sendViewScanQrButtonKey"), - // onTap: scanQr, - // child: const QrCodeIcon(), - // ) + if (sendToController.text.isEmpty) + TextFieldIconButton( + semanticsLabel: + "Scan QR Button. Opens Camera For Scanning QR Code.", + key: const Key( + "sendViewScanQrButtonKey", + ), + onTap: scanWebcam, + child: const QrCodeIcon(), + ), ], ), ), @@ -1722,7 +1808,7 @@ class _DesktopSendState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.regular.raw!, + lib_monero.TransactionPriority.medium.value, ); ref .read(feeSheetSessionCacheProvider) diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_token_send.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_token_send.dart index 490465490..158beed86 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_token_send.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_token_send.dart @@ -14,14 +14,12 @@ import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../../models/isar/models/contact_entry.dart'; import '../../../../models/paynym/paynym_account_lite.dart'; import '../../../../models/send_view_auto_fill_data.dart'; import '../../../../pages/send_view/confirm_transaction_view.dart'; import '../../../../pages/send_view/sub_widgets/building_transaction_dialog.dart'; -import '../../../desktop_home_view.dart'; -import 'address_book_address_chooser/address_book_address_chooser.dart'; -import 'desktop_fee_dropdown.dart'; import '../../../../providers/providers.dart'; import '../../../../providers/ui/fee_rate_type_state_provider.dart'; import '../../../../providers/ui/preview_tx_button_state_provider.dart'; @@ -30,6 +28,7 @@ import '../../../../utilities/address_utils.dart'; import '../../../../utilities/amount/amount.dart'; import '../../../../utilities/amount/amount_formatter.dart'; import '../../../../utilities/amount/amount_input_formatter.dart'; +import '../../../../utilities/amount/amount_unit.dart'; import '../../../../utilities/barcode_scanner_interface.dart'; import '../../../../utilities/clipboard_interface.dart'; import '../../../../utilities/constants.dart'; @@ -51,6 +50,9 @@ import '../../../../widgets/icon_widgets/clipboard_icon.dart'; import '../../../../widgets/icon_widgets/x_icon.dart'; import '../../../../widgets/stack_text_field.dart'; import '../../../../widgets/textfield_icon_button.dart'; +import '../../../desktop_home_view.dart'; +import 'address_book_address_chooser/address_book_address_chooser.dart'; +import 'desktop_fee_dropdown.dart'; // const _kCryptoAmountRegex = r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$'; @@ -393,19 +395,13 @@ class _DesktopTokenSendState extends ConsumerState { void _cryptoAmountChanged() async { if (!_cryptoAmountChangeLock) { - final String cryptoAmount = cryptoAmountController.text; - if (cryptoAmount.isNotEmpty && - cryptoAmount != "." && - cryptoAmount != ",") { - _amountToSend = cryptoAmount.contains(",") - ? Decimal.parse(cryptoAmount.replaceFirst(",", ".")).toAmount( - fractionDigits: - ref.read(pCurrentTokenWallet)!.tokenContract.decimals, - ) - : Decimal.parse(cryptoAmount).toAmount( - fractionDigits: - ref.read(pCurrentTokenWallet)!.tokenContract.decimals, - ); + final cryptoAmount = ref.read(pAmountFormatter(coin)).tryParse( + cryptoAmountController.text, + ethContract: ref.read(pCurrentTokenWallet)!.tokenContract, + ); + + if (cryptoAmount != null) { + _amountToSend = cryptoAmount; if (_cachedAmountToSend != null && _cachedAmountToSend == _amountToSend) { return; @@ -480,25 +476,30 @@ class _DesktopTokenSendState extends ConsumerState { level: LogLevel.Info, ); - final results = AddressUtils.parseUri(qrResult.rawContent); + final paymentData = AddressUtils.parsePaymentUri( + qrResult.rawContent, + logging: Logging.instance, + ); - Logging.instance.log("qrResult parsed: $results", level: LogLevel.Info); + Logging.instance + .log("qrResult parsed: $paymentData", level: LogLevel.Info); - if (results.isNotEmpty && results["scheme"] == coin.uriScheme) { + if (paymentData != null && + paymentData.coin?.uriScheme == coin.uriScheme) { // auto fill address - _address = results["address"] ?? ""; + _address = paymentData.address.trim(); sendToController.text = _address!; // autofill notes field - if (results["message"] != null) { - _note = results["message"]!; - } else if (results["label"] != null) { - _note = results["label"]!; + if (paymentData.message != null) { + _note = paymentData.message!; + } else if (paymentData.label != null) { + _note = paymentData.label!; } // autofill amount field - if (results["amount"] != null) { - final amount = Decimal.parse(results["amount"]!).toAmount( + if (paymentData.amount != null) { + final Amount amount = Decimal.parse(paymentData.amount!).toAmount( fractionDigits: ref.read(pCurrentTokenWallet)!.tokenContract.decimals, ); @@ -516,12 +517,8 @@ class _DesktopTokenSendState extends ConsumerState { }); // now check for non standard encoded basic address - } else if (ref - .read(pWallets) - .getWallet(walletId) - .cryptoCurrency - .validateAddress(qrResult.rawContent)) { - _address = qrResult.rawContent; + } else { + _address = qrResult.rawContent.split("\n").first.trim(); sendToController.text = _address ?? ""; _updatePreviewButtonState(_address, _amountToSend); @@ -803,7 +800,7 @@ class _DesktopTokenSendState extends ConsumerState { child: Padding( padding: const EdgeInsets.all(12), child: Text( - tokenContract.symbol, + ref.watch(pAmountUnit(coin)).unitForContract(tokenContract), style: STextStyles.smallMed14(context).copyWith( color: Theme.of(context) .extension()! diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart index 0691cc9de..2499d4754 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart @@ -39,12 +39,14 @@ import '../../../../wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface import '../../../../wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/ordinals_interface.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart'; +import '../../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; import '../../../../widgets/custom_loading_overlay.dart'; import '../../../../widgets/desktop/desktop_dialog.dart'; import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/loading_indicator.dart'; import '../../../cashfusion/desktop_cashfusion_view.dart'; +import '../../../churning/desktop_churning_view.dart'; import '../../../coin_control/desktop_coin_control_view.dart'; import '../../../desktop_menu.dart'; import '../../../ordinals/desktop_ordinals_view.dart'; @@ -92,6 +94,7 @@ class _DesktopWalletFeaturesState extends ConsumerState { onOrdinalsPressed: _onOrdinalsPressed, onMonkeyPressed: _onMonkeyPressed, onFusionPressed: _onFusionPressed, + onChurnPressed: _onChurnPressed, ), ); } @@ -116,7 +119,7 @@ class _DesktopWalletFeaturesState extends ConsumerState { barrierDismissible: false, builder: (context) => DesktopDialog( maxWidth: 500, - maxHeight: 210, + maxHeight: double.infinity, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 20), child: Column( @@ -204,7 +207,7 @@ class _DesktopWalletFeaturesState extends ConsumerState { // await firoWallet.anonymizeAllLelantus(); await firoWallet.anonymizeAllSpark(); shouldPop = true; - if (context.mounted) { + if (mounted) { Navigator.of(context, rootNavigator: true).pop(); Navigator.of(context).popUntil( ModalRoute.withName(DesktopWalletView.routeName), @@ -219,7 +222,7 @@ class _DesktopWalletFeaturesState extends ConsumerState { } } catch (e) { shouldPop = true; - if (context.mounted) { + if (mounted) { Navigator.of(context, rootNavigator: true).pop(); Navigator.of(context).popUntil( ModalRoute.withName(DesktopWalletView.routeName), @@ -299,13 +302,14 @@ class _DesktopWalletFeaturesState extends ConsumerState { level: LogLevel.Info, ); - if (context.mounted) { + if (mounted) { Navigator.of(context, rootNavigator: true).pop(); // check if account exists and for matching code to see if claimed - if (account.value != null && - account.value!.nonSegwitPaymentCode.claimed && - account.value!.segwit) { + if (account.value != null && account.value!.nonSegwitPaymentCode.claimed + // && + // account.value!.segwit + ) { ref.read(myPaynymAccountStateProvider.state).state = account.value!; await Navigator.of(context).pushNamed( @@ -348,11 +352,23 @@ class _DesktopWalletFeaturesState extends ConsumerState { ); } + void _onChurnPressed() { + Navigator.of(context, rootNavigator: true).pop(); + + Navigator.of(context).pushNamed( + DesktopChurningView.routeName, + arguments: widget.walletId, + ); + } + @override Widget build(BuildContext context) { final wallet = ref.watch(pWallets).getWallet(widget.walletId); final coin = wallet.info.coin; + final prefs = ref.watch(prefsChangeNotifierProvider); + final showExchange = prefs.enableExchange; + final showMore = wallet is PaynymInterface || (wallet is CoinControlInterface && ref.watch( @@ -366,9 +382,14 @@ class _DesktopWalletFeaturesState extends ConsumerState { wallet is OrdinalsInterface || wallet is CashFusionInterface; + final isViewOnly = wallet is ViewOnlyOptionInterface && wallet.isViewOnly; + return Row( children: [ - if (Constants.enableExchange && AppConfig.hasFeature(AppFeature.swap)) + if (!isViewOnly && + Constants.enableExchange && + AppConfig.hasFeature(AppFeature.swap) && + showExchange) SecondaryButton( label: "Swap", width: buttonWidth, @@ -383,11 +404,15 @@ class _DesktopWalletFeaturesState extends ConsumerState { ), onPressed: () => _onSwapPressed(), ), - if (Constants.enableExchange && AppConfig.hasFeature(AppFeature.buy)) + if (Constants.enableExchange && + AppConfig.hasFeature(AppFeature.buy) && + showExchange) const SizedBox( width: 16, ), - if (Constants.enableExchange && AppConfig.hasFeature(AppFeature.buy)) + if (Constants.enableExchange && + AppConfig.hasFeature(AppFeature.buy) && + showExchange) SecondaryButton( label: "Buy", width: buttonWidth, diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart index 6072612bb..0869b0b50 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart @@ -14,16 +14,17 @@ import 'package:flutter_svg/flutter_svg.dart'; import '../../../../../app_config.dart'; import '../../../../../db/sqlite/firo_cache.dart'; +import '../../../../../models/keys/view_only_wallet_data.dart'; import '../../../../../providers/db/main_db_provider.dart'; import '../../../../../providers/global/prefs_provider.dart'; import '../../../../../providers/global/wallets_provider.dart'; import '../../../../../themes/stack_colors.dart'; import '../../../../../utilities/assets.dart'; import '../../../../../utilities/text_styles.dart'; -import '../../../../../utilities/util.dart'; import '../../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../../wallets/isar/models/wallet_info.dart'; import '../../../../../wallets/isar/providers/wallet_info_provider.dart'; +import '../../../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart'; @@ -31,6 +32,7 @@ import '../../../../../wallets/wallet/wallet_mixin_interfaces/ordinals_interface import '../../../../../wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/rbf_interface.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/spark_interface.dart'; +import '../../../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; import '../../../../../widgets/custom_buttons/draggable_switch_button.dart'; import '../../../../../widgets/desktop/desktop_dialog.dart'; import '../../../../../widgets/desktop/desktop_dialog_close_button.dart'; @@ -49,6 +51,7 @@ class MoreFeaturesDialog extends ConsumerStatefulWidget { required this.onOrdinalsPressed, required this.onMonkeyPressed, required this.onFusionPressed, + required this.onChurnPressed, }); final String walletId; @@ -59,6 +62,7 @@ class MoreFeaturesDialog extends ConsumerStatefulWidget { final VoidCallback? onOrdinalsPressed; final VoidCallback? onMonkeyPressed; final VoidCallback? onFusionPressed; + final VoidCallback? onChurnPressed; @override ConsumerState createState() => _MoreFeaturesDialogState(); @@ -106,117 +110,122 @@ class _MoreFeaturesDialogState extends ConsumerState { } } + late final DSBController _switchController; + bool _switchReuseAddressToggledLock = false; // Mutex. - Future _switchReuseAddressToggled(bool newValue) async { - if (newValue) { - await showDialog( - context: context, - builder: (context) { - final isDesktop = Util.isDesktop; - return DesktopDialog( - maxWidth: 576, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: const EdgeInsets.only(left: 32), - child: Text( - "Warning!", - style: STextStyles.desktopH3(context), - ), - ), - const DesktopDialogCloseButton(), - ], - ), - Padding( - padding: const EdgeInsets.only( - top: 8, - left: 32, - right: 32, - bottom: 32, - ), - child: Column( - mainAxisSize: MainAxisSize.min, + Future _switchReuseAddressToggled() async { + if (_switchReuseAddressToggledLock) { + return; + } + _switchReuseAddressToggledLock = true; // Lock mutex. + + try { + if (_switchController.isOn?.call() != true) { + final canContinue = await showDialog( + context: context, + builder: (context) { + return DesktopDialog( + maxWidth: 576, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - "Reusing addresses reduces your privacy and security. Are you sure you want to reuse addresses by default?", - style: STextStyles.desktopTextSmall(context), - ), - const SizedBox( - height: 43, - ), - Row( - children: [ - Expanded( - child: SecondaryButton( - buttonHeight: ButtonHeight.l, - onPressed: () { - Navigator.of(context).pop(false); - }, - label: "Cancel", - ), - ), - const SizedBox( - width: 16, - ), - Expanded( - child: PrimaryButton( - buttonHeight: ButtonHeight.l, - onPressed: () { - Navigator.of(context).pop(true); - }, - label: "Continue", - ), - ), - ], + Padding( + padding: const EdgeInsets.only(left: 32), + child: Text( + "Warning!", + style: STextStyles.desktopH3(context), + ), ), + const DesktopDialogCloseButton(), ], ), - ), - ], - ), - ); - }, - ).then((confirmed) async { - if (_switchReuseAddressToggledLock) { - return; - } - _switchReuseAddressToggledLock = true; // Lock mutex. + Padding( + padding: const EdgeInsets.only( + top: 8, + left: 32, + right: 32, + bottom: 32, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Reusing addresses reduces your privacy and security. Are you sure you want to reuse addresses by default?", + style: STextStyles.desktopTextSmall(context), + ), + const SizedBox( + height: 43, + ), + Row( + children: [ + Expanded( + child: SecondaryButton( + buttonHeight: ButtonHeight.l, + onPressed: () { + Navigator.of(context).pop(false); + }, + label: "Cancel", + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: PrimaryButton( + buttonHeight: ButtonHeight.l, + onPressed: () { + Navigator.of(context).pop(true); + }, + label: "Continue", + ), + ), + ], + ), + ], + ), + ), + ], + ), + ); + }, + ); - try { - if (confirmed == true) { - await ref.read(pWalletInfo(widget.walletId)).updateOtherData( - newEntries: { - WalletInfoKeys.reuseAddress: true, - }, - isar: ref.read(mainDBProvider).isar, - ); - } else { - await ref.read(pWalletInfo(widget.walletId)).updateOtherData( - newEntries: { - WalletInfoKeys.reuseAddress: false, - }, - isar: ref.read(mainDBProvider).isar, - ); - } - } finally { - // ensure _switchReuseAddressToggledLock is set to false no matter what. - _switchReuseAddressToggledLock = false; + if (canContinue == true) { + await _updateAddressReuse(true); } - }); - } else { - await ref.read(pWalletInfo(widget.walletId)).updateOtherData( - newEntries: { - WalletInfoKeys.reuseAddress: false, - }, - isar: ref.read(mainDBProvider).isar, - ); + } else { + await _updateAddressReuse(false); + } + } finally { + // ensure _switchReuseAddressToggledLock is set to false no matter what. + _switchReuseAddressToggledLock = false; } } + Future _updateAddressReuse(bool shouldReuse) async { + await ref.read(pWalletInfo(widget.walletId)).updateOtherData( + newEntries: { + WalletInfoKeys.reuseAddress: shouldReuse, + }, + isar: ref.read(mainDBProvider).isar, + ); + + if (_switchController.isOn != null) { + if (_switchController.isOn!.call() != shouldReuse) { + _switchController.activate?.call(); + } + } + } + + @override + void initState() { + _switchController = DSBController(); + super.initState(); + } + @override Widget build(BuildContext context) { final wallet = ref.watch( @@ -231,7 +240,13 @@ class _MoreFeaturesDialogState extends ConsumerState { ), ); + final isViewOnly = wallet is ViewOnlyOptionInterface && wallet.isViewOnly; + final isViewOnlyNoAddressGen = wallet is ViewOnlyOptionInterface && + wallet.isViewOnly && + wallet.viewOnlyType == ViewOnlyWalletType.addressOnly; + return DesktopDialog( + maxHeight: double.infinity, child: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -250,7 +265,7 @@ class _MoreFeaturesDialogState extends ConsumerState { const DesktopDialogCloseButton(), ], ), - if (wallet.info.coin is Firo) + if (!isViewOnly && wallet.info.coin is Firo) _MoreFeaturesItem( label: "Anonymize funds", detail: "Anonymize funds", @@ -272,7 +287,7 @@ class _MoreFeaturesDialogState extends ConsumerState { iconAsset: Assets.svg.coinControl.gamePad, onPressed: () async => widget.onCoinControlPressed?.call(), ), - if (wallet is PaynymInterface) + if (!isViewOnly && wallet is PaynymInterface) _MoreFeaturesItem( label: "PayNym", detail: "Increased address privacy using BIP47", @@ -293,18 +308,25 @@ class _MoreFeaturesDialogState extends ConsumerState { iconAsset: Assets.svg.monkey, onPressed: () async => widget.onMonkeyPressed?.call(), ), - if (wallet is CashFusionInterface) + if (!isViewOnly && wallet is CashFusionInterface) _MoreFeaturesItem( label: "Fusion", detail: "Decentralized mixing protocol", iconAsset: Assets.svg.cashFusion, onPressed: () async => widget.onFusionPressed?.call(), ), - if (wallet is SparkInterface) + if (!isViewOnly && wallet is LibMoneroWallet) + _MoreFeaturesItem( + label: "Churn", + detail: "Churning", + iconAsset: Assets.svg.churn, + onPressed: () async => widget.onChurnPressed?.call(), + ), + if (wallet is SparkInterface && !isViewOnly) _MoreFeaturesClearSparkCacheItem( cryptoCurrency: wallet.cryptoCurrency, ), - if (wallet is LelantusInterface) + if (wallet is LelantusInterface && !isViewOnly) _MoreFeaturesItemBase( child: Row( children: [ @@ -369,37 +391,41 @@ class _MoreFeaturesDialogState extends ConsumerState { ), ), // reuseAddress preference. - _MoreFeaturesItemBase( - child: Row( - children: [ - const SizedBox(width: 3), - SizedBox( - height: 20, - width: 40, - child: DraggableSwitchButton( - isOn: ref.watch( - pWalletInfo(widget.walletId) - .select((value) => value.otherData), - )[WalletInfoKeys.reuseAddress] as bool? ?? - false, - onValueChanged: _switchReuseAddressToggled, - ), - ), - const SizedBox( - width: 16, - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Reuse receiving address by default", - style: STextStyles.w600_20(context), + if (!isViewOnlyNoAddressGen) + _MoreFeaturesItemBase( + onPressed: _switchReuseAddressToggled, + child: Row( + children: [ + const SizedBox(width: 3), + SizedBox( + height: 20, + width: 40, + child: IgnorePointer( + child: DraggableSwitchButton( + isOn: ref.watch( + pWalletInfo(widget.walletId) + .select((value) => value.otherData), + )[WalletInfoKeys.reuseAddress] as bool? ?? + false, + controller: _switchController, + ), ), - ], - ), - ], + ), + const SizedBox( + width: 16, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Reuse receiving address", + style: STextStyles.w600_20(context), + ), + ], + ), + ], + ), ), - ), const SizedBox( height: 28, ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/my_wallet.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/my_wallet.dart index 092f9884f..622630c4a 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/my_wallet.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/my_wallet.dart @@ -10,20 +10,22 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../../frost_route_generator.dart'; import '../../../../pages/send_view/frost_ms/frost_send_view.dart'; import '../../../../pages/wallet_view/transaction_views/tx_v2/transaction_v2_list.dart'; -import '../../my_stack_view.dart'; -import 'desktop_receive.dart'; -import 'desktop_send.dart'; -import 'desktop_token_send.dart'; import '../../../../providers/global/wallets_provider.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../wallets/wallet/impl/bitcoin_frost_wallet.dart'; +import '../../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; import '../../../../widgets/custom_tab_view.dart'; import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/frost_scaffold.dart'; import '../../../../widgets/rounded_white_container.dart'; +import '../../my_stack_view.dart'; +import 'desktop_receive.dart'; +import 'desktop_send.dart'; +import 'desktop_token_send.dart'; class MyWallet extends ConsumerStatefulWidget { const MyWallet({ @@ -48,6 +50,7 @@ class _MyWalletState extends ConsumerState { late final bool isEth; late final CryptoCurrency coin; late final bool isFrost; + late final bool isViewOnly; @override void initState() { @@ -60,11 +63,34 @@ class _MyWalletState extends ConsumerState { titles.add("Transactions"); } + isViewOnly = wallet is ViewOnlyOptionInterface && wallet.isViewOnly; + if (isViewOnly) { + titles.remove("Receive"); + } + super.initState(); } @override Widget build(BuildContext context) { + if (isViewOnly) { + return ListView( + primary: false, + children: [ + RoundedWhiteContainer( + padding: EdgeInsets.zero, + child: Padding( + padding: const EdgeInsets.all(20), + child: DesktopReceive( + walletId: widget.walletId, + contractAddress: widget.contractAddress, + ), + ), + ), + ], + ); + } + return ListView( primary: false, children: [ diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart index e332c4543..f2357448c 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart @@ -23,9 +23,10 @@ import '../../../../utilities/assets.dart'; import '../../../../utilities/constants.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../wallets/wallet/impl/bitcoin_frost_wallet.dart'; -import '../../../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; +import '../../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; +import '../../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; import '../../../../widgets/desktop/desktop_dialog.dart'; import '../../../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../../../widgets/desktop/primary_button.dart'; @@ -82,7 +83,9 @@ class _UnlockWalletKeysDesktopState .verifyPassphrase(passwordController.text); if (verified) { - Navigator.of(context, rootNavigator: true).pop(); + if (mounted) { + Navigator.of(context, rootNavigator: true).pop(); + } final wallet = ref.read(pWallets).getWallet(widget.walletId); ({String keys, String config})? frostData; @@ -100,13 +103,20 @@ class _UnlockWalletKeysDesktopState throw Exception("FIXME ~= see todo in code"); } } else { - words = await wallet.getMnemonicAsWords(); + if (wallet is ViewOnlyOptionInterface && + (wallet as ViewOnlyOptionInterface).isViewOnly) { + // TODO: is something needed here? + } else { + words = await wallet.getMnemonicAsWords(); + } } KeyDataInterface? keyData; - if (wallet is ExtendedKeysInterface) { + if (wallet is ViewOnlyOptionInterface && wallet.isViewOnly) { + keyData = await wallet.getViewOnlyWalletData(); + } else if (wallet is ExtendedKeysInterface) { keyData = await wallet.getXPrivs(); - } else if (wallet is CwBasedInterface) { + } else if (wallet is LibMoneroWallet) { keyData = await wallet.getKeys(); } @@ -122,17 +132,20 @@ class _UnlockWalletKeysDesktopState ); } } else { - Navigator.of(context, rootNavigator: true).pop(); + if (mounted) { + Navigator.of(context, rootNavigator: true).pop(); + } await Future.delayed(const Duration(milliseconds: 300)); - - unawaited( - showFloatingFlushBar( - type: FlushBarType.warning, - message: "Invalid passphrase!", - context: context, - ), - ); + if (mounted) { + unawaited( + showFloatingFlushBar( + type: FlushBarType.warning, + message: "Invalid passphrase!", + context: context, + ), + ); + } } } @@ -327,7 +340,10 @@ class _UnlockWalletKeysDesktopState .verifyPassphrase(passwordController.text); if (verified) { - Navigator.of(context, rootNavigator: true).pop(); + if (context.mounted) { + Navigator.of(context, rootNavigator: true) + .pop(); + } ({String keys, String config})? frostData; List? words; @@ -347,13 +363,22 @@ class _UnlockWalletKeysDesktopState throw Exception("FIXME ~= see todo in code"); } } else { - words = await wallet.getMnemonicAsWords(); + if (wallet is ViewOnlyOptionInterface && + (wallet as ViewOnlyOptionInterface) + .isViewOnly) { + // TODO: is something needed here? + } else { + words = await wallet.getMnemonicAsWords(); + } } KeyDataInterface? keyData; - if (wallet is ExtendedKeysInterface) { + if (wallet is ViewOnlyOptionInterface && + wallet.isViewOnly) { + keyData = await wallet.getViewOnlyWalletData(); + } else if (wallet is ExtendedKeysInterface) { keyData = await wallet.getXPrivs(); - } else if (wallet is CwBasedInterface) { + } else if (wallet is LibMoneroWallet) { keyData = await wallet.getKeys(); } @@ -370,19 +395,23 @@ class _UnlockWalletKeysDesktopState ); } } else { - Navigator.of(context, rootNavigator: true).pop(); + if (context.mounted) { + Navigator.of(context, rootNavigator: true) + .pop(); + } await Future.delayed( const Duration(milliseconds: 300), ); - - unawaited( - showFloatingFlushBar( - type: FlushBarType.warning, - message: "Invalid passphrase!", - context: context, - ), - ); + if (context.mounted) { + unawaited( + showFloatingFlushBar( + type: FlushBarType.warning, + message: "Invalid passphrase!", + context: context, + ), + ); + } } } : null, diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart index f1bc2d9f6..bb8ba8089 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart @@ -16,9 +16,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../models/keys/cw_key_data.dart'; import '../../../../models/keys/key_data_interface.dart'; +import '../../../../models/keys/view_only_wallet_data.dart'; import '../../../../models/keys/xpriv_data.dart'; import '../../../../notifications/show_flush_bar.dart'; import '../../../../pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart'; +import '../../../../pages/settings_views/sub_widgets/view_only_wallet_data_widget.dart'; import '../../../../pages/settings_views/wallet_settings_view/wallet_backup_views/cn_wallet_keys.dart'; import '../../../../pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_xprivs.dart'; import '../../../../pages/wallet_view/transaction_views/transaction_details_view.dart'; @@ -180,31 +182,39 @@ class WalletKeysDesktopPopup extends ConsumerWidget { ], ) : keyData != null - ? CustomTabView( - titles: [ - "Mnemonic", - if (keyData is XPrivData) "XPriv(s)", - if (keyData is CWKeyData) "Keys", - ], - children: [ - Padding( - padding: const EdgeInsets.only(top: 16), - child: _Mnemonic( - words: words, + ? keyData is ViewOnlyWalletData + ? Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: ViewOnlyWalletDataWidget( + data: keyData as ViewOnlyWalletData, ), - ), - if (keyData is XPrivData) - WalletXPrivs( - xprivData: keyData as XPrivData, - walletId: walletId, - ), - if (keyData is CWKeyData) - CNWalletKeys( - cwKeyData: keyData as CWKeyData, - walletId: walletId, - ), - ], - ) + ) + : CustomTabView( + titles: [ + if (words.isNotEmpty) "Mnemonic", + if (keyData is XPrivData) "XPriv(s)", + if (keyData is CWKeyData) "Keys", + ], + children: [ + if (words.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: 16), + child: _Mnemonic( + words: words, + ), + ), + if (keyData is XPrivData) + WalletXPrivs( + xprivData: keyData as XPrivData, + walletId: walletId, + ), + if (keyData is CWKeyData) + CNWalletKeys( + cwKeyData: keyData as CWKeyData, + walletId: walletId, + ), + ], + ) : _Mnemonic( words: words, ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart index 4f955c00f..e0da6d5e9 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart @@ -31,6 +31,7 @@ import '../../../../wallets/crypto_currency/intermediate/frost_currency.dart'; import '../../../../wallets/crypto_currency/intermediate/nano_currency.dart'; import '../../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart'; +import '../../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; import '../../../addresses/desktop_wallet_addresses_view.dart'; import '../../../lelantus_coins/lelantus_coins_view.dart'; import '../../../spark_coins/spark_coins_view.dart'; @@ -293,10 +294,15 @@ class WalletOptionsPopupMenu extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final coin = ref.watch(pWalletCoin(walletId)); - final firoDebug = kDebugMode && (coin is Firo); + bool firoDebug = kDebugMode && (coin is Firo); - final bool xpubEnabled = - ref.watch(pWallets).getWallet(walletId) is ExtendedKeysInterface; + final wallet = ref.watch(pWallets).getWallet(walletId); + bool xpubEnabled = wallet is ExtendedKeysInterface; + + if (wallet is ViewOnlyOptionInterface && wallet.isViewOnly) { + xpubEnabled = false; + firoDebug = false; + } final bool canChangeRep = coin is NanoCurrency; diff --git a/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart b/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart index d5a46cbf2..e96a5288c 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart @@ -17,6 +17,7 @@ import '../../../../pages/settings_views/global_settings_view/advanced_views/man import '../../../../providers/global/prefs_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; +import '../../../../utilities/constants.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../widgets/custom_buttons/draggable_switch_button.dart'; import '../../../../widgets/desktop/primary_button.dart'; @@ -162,6 +163,50 @@ class _AdvancedSettings extends ConsumerState { ], ), ), + // showExchange pref. + if (Constants.enableExchange) + const Padding( + padding: EdgeInsets.all(10.0), + child: Divider( + thickness: 0.5, + ), + ), + if (Constants.enableExchange) + Padding( + padding: const EdgeInsets.all(10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Enable exchange features", + style: + STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ), + textAlign: TextAlign.left, + ), + SizedBox( + height: 20, + width: 40, + child: DraggableSwitchButton( + isOn: ref.watch( + prefsChangeNotifierProvider.select( + (value) => value.enableExchange, + ), + ), + onValueChanged: (newValue) { + ref + .read(prefsChangeNotifierProvider) + .enableExchange = newValue; + }, + ), + ), + ], + ), + ), const Padding( padding: EdgeInsets.all(10.0), child: Divider( diff --git a/lib/pages_desktop_specific/settings/settings_menu/desktop_about_view.dart b/lib/pages_desktop_specific/settings/settings_menu/desktop_about_view.dart index 34c8a8dba..98a7dec34 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/desktop_about_view.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/desktop_about_view.dart @@ -434,63 +434,63 @@ class DesktopAboutView extends ConsumerWidget { ); }, ), - if (AppConfig.coins - .whereType() - .isNotEmpty) - FutureBuilder( - future: GitStatus - .getMoneroCommitStatus(), - builder: ( - context, - AsyncSnapshot - snapshot, - ) { - CommitStatus stateOfCommit = - CommitStatus.notLoaded; - - if (snapshot.connectionState == - ConnectionState - .done && - snapshot.hasData) { - stateOfCommit = - snapshot.data!; - } - return Column( - mainAxisSize: - MainAxisSize.min, - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Text( - "Monero Build Commit", - style: STextStyles - .desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of( - context, - ) - .extension< - StackColors>()! - .textDark, - ), - ), - const SizedBox( - height: 2, - ), - SelectableText( - GitStatus.moneroCommit, - style: GitStatus - .styleForStatus( - stateOfCommit, - context, - ), - ), - ], - ); - }, - ), + // if (AppConfig.coins + // .whereType() + // .isNotEmpty) + // FutureBuilder( + // future: GitStatus + // .getMoneroCommitStatus(), + // builder: ( + // context, + // AsyncSnapshot + // snapshot, + // ) { + // CommitStatus stateOfCommit = + // CommitStatus.notLoaded; + // + // if (snapshot.connectionState == + // ConnectionState + // .done && + // snapshot.hasData) { + // stateOfCommit = + // snapshot.data!; + // } + // return Column( + // mainAxisSize: + // MainAxisSize.min, + // crossAxisAlignment: + // CrossAxisAlignment + // .start, + // children: [ + // Text( + // "Monero Build Commit", + // style: STextStyles + // .desktopTextExtraExtraSmall( + // context, + // ).copyWith( + // color: Theme.of( + // context, + // ) + // .extension< + // StackColors>()! + // .textDark, + // ), + // ), + // const SizedBox( + // height: 2, + // ), + // SelectableText( + // GitStatus.moneroCommit, + // style: GitStatus + // .styleForStatus( + // stateOfCommit, + // context, + // ), + // ), + // ], + // ); + // }, + // ), ], ), const SizedBox(height: 35), diff --git a/lib/providers/churning/churning_service_provider.dart b/lib/providers/churning/churning_service_provider.dart new file mode 100644 index 000000000..642d1103f --- /dev/null +++ b/lib/providers/churning/churning_service_provider.dart @@ -0,0 +1,12 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../services/churning_service.dart'; +import '../../wallets/wallet/intermediate/lib_monero_wallet.dart'; +import '../global/wallets_provider.dart'; + +final pChurningService = ChangeNotifierProvider.family( + (ref, walletId) { + final wallet = ref.watch(pWallets.select((s) => s.getWallet(walletId))); + return ChurningService(wallet: wallet as LibMoneroWallet); + }, +); diff --git a/lib/route_generator.dart b/lib/route_generator.dart index b449525b6..52454fb29 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -23,6 +23,7 @@ import 'models/isar/models/contact_entry.dart'; import 'models/isar/models/isar_models.dart'; import 'models/isar/ordinal.dart'; import 'models/keys/key_data_interface.dart'; +import 'models/keys/view_only_wallet_data.dart'; import 'models/paynym/paynym_account_lite.dart'; import 'models/send_view_auto_fill_data.dart'; import 'pages/add_wallet_views/add_token_view/add_custom_token_view.dart'; @@ -37,6 +38,7 @@ import 'pages/add_wallet_views/new_wallet_options/new_wallet_options_view.dart'; import 'pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart'; import 'pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart'; import 'pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart'; +import 'pages/add_wallet_views/restore_wallet_view/restore_view_only_wallet_view.dart'; import 'pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart'; import 'pages/add_wallet_views/select_wallet_for_token_view.dart'; import 'pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart'; @@ -52,6 +54,8 @@ import 'pages/buy_view/buy_quote_preview.dart'; import 'pages/buy_view/buy_view.dart'; import 'pages/cashfusion/cashfusion_view.dart'; import 'pages/cashfusion/fusion_progress_view.dart'; +import 'pages/churning/churning_progress_view.dart'; +import 'pages/churning/churning_view.dart'; import 'pages/coin_control/coin_control_view.dart'; import 'pages/coin_control/utxo_details_view.dart'; import 'pages/exchange_view/choose_from_stack_view.dart'; @@ -128,6 +132,7 @@ import 'pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_bac import 'pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart'; import 'pages/settings_views/wallet_settings_view/wallet_settings_view.dart'; import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart'; +import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_view_only_wallet_keys_view.dart'; import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_recovery_phrase_view.dart'; import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_warning_view.dart'; import 'pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/lelantus_settings_view.dart'; @@ -155,6 +160,7 @@ import 'pages/wallets_view/wallets_view.dart'; import 'pages_desktop_specific/address_book_view/desktop_address_book.dart'; import 'pages_desktop_specific/addresses/desktop_wallet_addresses_view.dart'; import 'pages_desktop_specific/cashfusion/desktop_cashfusion_view.dart'; +import 'pages_desktop_specific/churning/desktop_churning_view.dart'; import 'pages_desktop_specific/coin_control/desktop_coin_control_view.dart'; // import 'package:stackwallet/pages_desktop_specific/desktop_exchange/desktop_all_buys_view.dart'; import 'pages_desktop_specific/desktop_buy/desktop_buy_view.dart'; @@ -779,6 +785,34 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case ChurningView.routeName: + if (args is String) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => ChurningView( + walletId: args, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + + case ChurningProgressView.routeName: + if (args is String) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => ChurningProgressView( + walletId: args, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + case DesktopCashFusionView.routeName: if (args is String) { return getRoute( @@ -793,6 +827,20 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case DesktopChurningView.routeName: + if (args is String) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => DesktopChurningView( + walletId: args, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + case GlobalSettingsView.routeName: return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, @@ -1456,7 +1504,7 @@ class RouteGenerator { case RestoreWalletView.routeName: if (args - is Tuple6) { + is Tuple6) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, builder: (_) => RestoreWalletView( @@ -1474,6 +1522,28 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case RestoreViewOnlyWalletView.routeName: + if (args is ({ + String walletName, + CryptoCurrency coin, + DateTime? restoreFromDate, + bool enableLelantusScanning, + })) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => RestoreViewOnlyWalletView( + walletName: args.walletName, + coin: args.coin, + restoreFromDate: args.restoreFromDate, + enableLelantusScanning: args.enableLelantusScanning, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + case NewWalletRecoveryPhraseView.routeName: if (args is Tuple2>) { return getRoute( @@ -1882,6 +1952,21 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case DeleteViewOnlyWalletKeysView.routeName: + if (args is ({String walletId, ViewOnlyWalletData data})) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => DeleteViewOnlyWalletKeysView( + data: args.data, + walletId: args.walletId, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + // exchange steps case Step1View.routeName: diff --git a/lib/services/churning_service.dart b/lib/services/churning_service.dart new file mode 100644 index 000000000..0aecefe08 --- /dev/null +++ b/lib/services/churning_service.dart @@ -0,0 +1,207 @@ +import 'dart:async'; +import 'dart:math'; + +import 'package:cs_monero/cs_monero.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:mutex/mutex.dart'; + +import '../wallets/wallet/intermediate/lib_monero_wallet.dart'; + +enum ChurnStatus { + waiting, + running, + failed, + success; +} + +class ChurningService extends ChangeNotifier { + // stack only uses account 0 at this point in time + static const kAccount = 0; + + ChurningService({required this.wallet}); + + final LibMoneroWallet wallet; + Wallet get csWallet => wallet.libMoneroWallet!; + + int rounds = 1; // default + bool ignoreErrors = false; // default + + bool _running = false; + + ChurnStatus waitingForUnlockedBalance = ChurnStatus.waiting; + ChurnStatus makingChurnTransaction = ChurnStatus.waiting; + ChurnStatus completedStatus = ChurnStatus.waiting; + int roundsCompleted = 0; + bool done = false; + Object? lastSeenError; + + bool _canChurn() { + if (csWallet.getUnlockedBalance(accountIndex: kAccount) > BigInt.zero) { + return true; + } else { + return false; + } + } + + String? confirmsInfo; + Future _updateConfirmsInfo() async { + final currentHeight = wallet.currentKnownChainHeight; + if (currentHeight < 1) { + return; + } + + final outputs = await csWallet.getOutputs(refresh: true); + final required = wallet.cryptoCurrency.minConfirms; + + int lowestNumberOfConfirms = required; + + for (final output in outputs.where((e) => !e.isFrozen && !e.spent)) { + final confirms = currentHeight - output.height; + + lowestNumberOfConfirms = min(lowestNumberOfConfirms, confirms); + } + + final bool shouldNotify; + if (lowestNumberOfConfirms == required) { + shouldNotify = confirmsInfo != null; + confirmsInfo = null; + } else { + final prev = confirmsInfo; + confirmsInfo = "($lowestNumberOfConfirms/$required)"; + shouldNotify = confirmsInfo != prev; + } + + if (_running && _timerRunning && shouldNotify) { + notifyListeners(); + } + } + + Timer? _confirmsTimer; + bool _timerRunning = false; + void _stopConfirmsTimer() { + _timerRunning = false; + _confirmsTimer?.cancel(); + confirmsInfo = null; + _confirmsTimer = null; + } + + void _startConfirmsTimer() { + _confirmsTimer?.cancel(); + _confirmsTimer = Timer.periodic( + const Duration(seconds: 5), + (_) => _updateConfirmsInfo(), + ); + } + + final _pause = Mutex(); + bool get isPaused => _pause.isLocked; + void unpause() { + if (_pause.isLocked) _pause.release(); + } + + Future churn() async { + if (rounds < 0 || _running) { + // TODO: error? + return; + } + + _running = true; + waitingForUnlockedBalance = ChurnStatus.running; + makingChurnTransaction = ChurnStatus.waiting; + completedStatus = ChurnStatus.waiting; + roundsCompleted = 0; + done = false; + lastSeenError = null; + notifyListeners(); + + final roundsToDo = rounds; + final continuous = rounds == 0; + + bool complete() => !continuous && roundsCompleted >= roundsToDo; + + while (!complete() && _running) { + if (_canChurn()) { + waitingForUnlockedBalance = ChurnStatus.success; + makingChurnTransaction = ChurnStatus.running; + notifyListeners(); + + try { + _stopConfirmsTimer(); + Logging.log?.i("Doing churn #${roundsCompleted + 1}"); + await _churnTxSimple(); + waitingForUnlockedBalance = ChurnStatus.success; + makingChurnTransaction = ChurnStatus.success; + roundsCompleted++; + notifyListeners(); + } catch (e, s) { + Logging.log?.e( + "Churning round #${roundsCompleted + 1} failed", + error: e, + stackTrace: s, + ); + lastSeenError = e; + makingChurnTransaction = ChurnStatus.failed; + notifyListeners(); + if (!ignoreErrors) { + await _pause.acquire(); + await _pause.protect(() async {}); + + if (!_running) { + completedStatus = ChurnStatus.failed; + // exit if stop option chosen on error + return; + } + } + } + } else { + Logging.log?.i("Can't churn yet, waiting..."); + } + + if (!complete() && _running) { + _startConfirmsTimer(); + waitingForUnlockedBalance = ChurnStatus.running; + makingChurnTransaction = ChurnStatus.waiting; + completedStatus = ChurnStatus.waiting; + notifyListeners(); + // sleep + await Future.delayed(const Duration(seconds: 30)); + } + } + + waitingForUnlockedBalance = ChurnStatus.success; + makingChurnTransaction = ChurnStatus.success; + completedStatus = ChurnStatus.success; + done = true; + _running = false; + notifyListeners(); + Logging.log?.i("Churning complete"); + } + + void stopChurning() { + done = true; + _running = false; + notifyListeners(); + unpause(); + } + + Future _churnTxSimple({ + final TransactionPriority priority = TransactionPriority.normal, + }) async { + final address = csWallet.getAddress( + accountIndex: kAccount, + addressIndex: 0, + ); + + final pending = await csWallet.createTx( + output: Recipient( + address: address.value, + amount: BigInt.zero, // Doesn't matter if `sweep` is true + ), + priority: priority, + accountIndex: kAccount, + sweep: true, + ); + + await csWallet.commitTx(pending); + } +} diff --git a/lib/services/ethereum/ethereum_api.dart b/lib/services/ethereum/ethereum_api.dart index a74bca89e..6f5f2313e 100644 --- a/lib/services/ethereum/ethereum_api.dart +++ b/lib/services/ethereum/ethereum_api.dart @@ -612,9 +612,21 @@ abstract class EthereumAPI { ); } + static Future _addContractInfoToServer(String contractAddress) async { + await client.get( + url: Uri.parse( + "$stackBaseServer/names?terms=$contractAddress&autoname=$contractAddress&all", + ), + proxyInfo: Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, + ); + } + static Future> getTokenContractInfoByAddress( - String contractAddress, - ) async { + String contractAddress, { + bool autoNameOnEmpty = true, + }) async { try { final response = await client.get( url: Uri.parse( @@ -630,7 +642,28 @@ abstract class EthereumAPI { final json = jsonDecode(response.body) as Map; if (json["data"] is List) { if ((json["data"] as List).isEmpty) { - throw EthApiException("Unknown token"); + if (autoNameOnEmpty) { + Logging.instance.log( + "getTokenByContractAddress(): Adding token data to server", + level: LogLevel.Debug, + ); + // this will add the missing data to server + await _addContractInfoToServer(contractAddress); + + Logging.instance.log( + "getTokenByContractAddress(): Adding to server threw so now" + "we try a normal fetch again", + level: LogLevel.Debug, + ); + + // now try again + return await getTokenContractInfoByAddress( + contractAddress, + autoNameOnEmpty: false, // prevent possible infinite loop + ); + } else { + throw EthApiException("Unknown token"); + } } final map = Map.from(json["data"].first as Map); @@ -653,7 +686,7 @@ abstract class EthereumAPI { ); } else { throw EthApiException( - "Unsupported token type found: ${map["type"]}", + "Unsupported token type found: ${map["type"]} in $map", ); } diff --git a/lib/services/event_bus/events/global/updated_in_background_event.dart b/lib/services/event_bus/events/global/updated_in_background_event.dart index 582978357..36a23360f 100644 --- a/lib/services/event_bus/events/global/updated_in_background_event.dart +++ b/lib/services/event_bus/events/global/updated_in_background_event.dart @@ -8,6 +8,8 @@ * */ +import 'package:flutter/foundation.dart'; + import '../../../../utilities/logger.dart'; class UpdatedInBackgroundEvent { @@ -15,9 +17,11 @@ class UpdatedInBackgroundEvent { String walletId; UpdatedInBackgroundEvent(this.message, this.walletId) { - Logging.instance.log( - "UpdatedInBackgroundEvent fired with message: $message", - level: LogLevel.Info, - ); + if (kDebugMode) { + Logging.instance.log( + "UpdatedInBackgroundEvent fired with message: $message", + level: LogLevel.Info, + ); + } } } diff --git a/lib/services/exchange/exchange_data_loading_service.dart b/lib/services/exchange/exchange_data_loading_service.dart index cfb01e150..811949d6e 100644 --- a/lib/services/exchange/exchange_data_loading_service.dart +++ b/lib/services/exchange/exchange_data_loading_service.dart @@ -12,6 +12,7 @@ import 'package:flutter/foundation.dart'; import 'package:isar/isar.dart'; import 'package:tuple/tuple.dart'; +import '../../app_config.dart'; import '../../db/hive/db.dart'; import '../../models/exchange/active_pair.dart'; import '../../models/exchange/aggregate_currency.dart'; @@ -79,7 +80,7 @@ class ExchangeDataLoadingService { if (await isar.currencies.count() > 0) { pair?.setSend( await getAggregateCurrency( - "BTC", + AppConfig.swapDefaults.from, rateType, null, ), @@ -88,7 +89,7 @@ class ExchangeDataLoadingService { pair?.setReceive( await getAggregateCurrency( - "XMR", + AppConfig.swapDefaults.to, rateType, null, ), diff --git a/lib/services/frost.dart b/lib/services/frost.dart index 632299e37..a3e2e030f 100644 --- a/lib/services/frost.dart +++ b/lib/services/frost.dart @@ -80,6 +80,7 @@ abstract class Frost { int feePerWeight, List inputs, }) extractDataFromSignConfig({ + required String serializedKeys, required String signConfig, required CryptoCurrency coin, }) { @@ -89,6 +90,7 @@ abstract class Frost { final signConfigPointer = decodedSignConfig( encodedConfig: signConfig, network: network, + serializedKeys: serializedKeys, ); // get various data from config @@ -126,6 +128,7 @@ abstract class Frost { final List outputs = []; for (int i = 0; i < count; i++) { final output = signInput( + thresholdKeysWrapperPointer: deserializeKeys(keys: serializedKeys), signConfig: signConfig, index: i, network: network, @@ -283,14 +286,22 @@ abstract class Frost { //=================== transaction creation =================================== static String createSignConfig({ + required String serializedKeys, required int network, - required List<({UTXO utxo, Uint8List scriptPubKey})> inputs, + required List< + ({ + UTXO utxo, + Uint8List scriptPubKey, + AddressDerivationData addressDerivationData + })> + inputs, required List<({String address, Amount amount, bool isChange})> outputs, required String changeAddress, required int feePerWeight, }) { try { final signConfig = newSignConfig( + thresholdKeysWrapperPointer: deserializeKeys(keys: serializedKeys), network: network, outputs: inputs .map( @@ -299,6 +310,7 @@ abstract class Frost { vout: e.utxo.vout, value: e.utxo.value, scriptPubKey: e.scriptPubKey, + addressDerivationData: e.addressDerivationData, ), ) .toList(), @@ -395,12 +407,16 @@ abstract class Frost { } static Pointer decodedSignConfig({ + required String serializedKeys, required String encodedConfig, required int network, }) { try { - final configPtr = - decodeSignConfig(encodedSignConfig: encodedConfig, network: network); + final configPtr = decodeSignConfig( + thresholdKeysWrapperPointer: deserializeKeys(keys: serializedKeys), + encodedSignConfig: encodedConfig, + network: network, + ); return configPtr; } catch (e, s) { Logging.instance.log( diff --git a/lib/services/node_service.dart b/lib/services/node_service.dart index f2e640a67..e55e2218b 100644 --- a/lib/services/node_service.dart +++ b/lib/services/node_service.dart @@ -12,6 +12,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart'; + import '../app_config.dart'; import '../db/hive/db.dart'; import '../models/node_model.dart'; @@ -58,6 +59,8 @@ class NodeService extends ChangeNotifier { enabled: savedNode.enabled, isFailover: savedNode.isFailover, trusted: savedNode.trusted, + torEnabled: savedNode.torEnabled, + clearnetEnabled: savedNode.clearnetEnabled, ), ); } @@ -74,6 +77,8 @@ class NodeService extends ChangeNotifier { enabled: primaryNode.enabled, isFailover: primaryNode.isFailover, trusted: primaryNode.trusted, + torEnabled: primaryNode.torEnabled, + clearnetEnabled: primaryNode.clearnetEnabled, ), ); } @@ -253,7 +258,9 @@ class NodeService extends ChangeNotifier { enabled: true, coinName: coin.identifier, isFailover: true, + torEnabled: nodeMap["torEnabled"] == "true", isDown: nodeMap["isDown"] == "true", + clearnetEnabled: nodeMap["plainEnabled"] == "true", ); final currentNode = getNodeById(id: nodeMap["id"] as String); if (currentNode != null) { diff --git a/lib/services/notifications_api.dart b/lib/services/notifications_api.dart index a5a62c188..13dec90d7 100644 --- a/lib/services/notifications_api.dart +++ b/lib/services/notifications_api.dart @@ -8,13 +8,16 @@ * */ +import 'dart:async'; + import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import '../models/notification_model.dart'; import '../utilities/prefs.dart'; import 'notifications_service.dart'; -class NotificationApi { +abstract final class NotificationApi { + static Completer? _initCalledCompleter; static final _notifications = FlutterLocalNotificationsPlugin(); // static final onNotifications = BehaviorSubject(); @@ -33,6 +36,16 @@ class NotificationApi { } static Future init({bool initScheduled = false}) async { + if (_initCalledCompleter == null) { + _initCalledCompleter = Completer(); + } else { + if (_initCalledCompleter!.isCompleted) { + return; + } else { + return await _initCalledCompleter!.future; + } + } + const android = AndroidInitializationSettings('app_icon_alpha'); const iOS = DarwinInitializationSettings(); const linux = LinuxInitializationSettings( @@ -54,12 +67,18 @@ class NotificationApi { // onNotifications.add(payload.payload); // }, ); + _initCalledCompleter!.complete(); } - static Future clearNotifications() async => _notifications.cancelAll(); + static Future clearNotifications() async { + await init(); + await _notifications.cancelAll(); + } - static Future clearNotification(int id) async => - _notifications.cancel(id); + static Future clearNotification(int id) async { + await init(); + await _notifications.cancel(id); + } //=================================== static late Prefs prefs; @@ -79,6 +98,7 @@ class NotificationApi { String? changeNowId, String? payload, }) async { + await init(); await prefs.incrementCurrentNotificationIndex(); final id = prefs.currentNotificationId; diff --git a/lib/services/notifications_service.dart b/lib/services/notifications_service.dart index 5df8cad4d..065285257 100644 --- a/lib/services/notifications_service.dart +++ b/lib/services/notifications_service.dart @@ -11,23 +11,23 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; + import '../app_config.dart'; import '../db/hive/db.dart'; import '../electrumx_rpc/electrumx_client.dart'; import '../exceptions/electrumx/no_such_transaction.dart'; import '../models/exchange/response_objects/trade.dart'; import '../models/notification_model.dart'; +import '../utilities/logger.dart'; +import '../utilities/prefs.dart'; +import '../wallets/crypto_currency/crypto_currency.dart'; +import '../wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart'; +import 'exchange/exchange.dart'; import 'exchange/exchange_response.dart'; import 'node_service.dart'; import 'notifications_api.dart'; import 'trade_service.dart'; import 'wallets.dart'; -import '../utilities/logger.dart'; -import '../utilities/prefs.dart'; -import '../wallets/crypto_currency/crypto_currency.dart'; -import '../wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart'; - -import 'exchange/exchange.dart'; class NotificationsService extends ChangeNotifier { late NodeService nodeService; @@ -136,12 +136,26 @@ class NotificationsService extends ChangeNotifier { final node = nodeService.getPrimaryNodeFor(currency: coin); if (node != null) { if (wallet is ElectrumXInterface) { + if (prefs.useTor) { + if (node.clearnetEnabled && !node.torEnabled) { + // just ignore I guess?? + return; + } + } else { + if (node.torEnabled && !node.clearnetEnabled) { + // just ignore I guess?? + return; + } + } + final eNode = ElectrumXNode( address: node.host, port: node.port, name: node.name, id: node.id, useSSL: node.useSSL, + torEnabled: node.torEnabled, + clearnetEnabled: node.clearnetEnabled, ); final failovers = nodeService .failoverNodesFor(currency: coin) @@ -152,6 +166,8 @@ class NotificationsService extends ChangeNotifier { name: e.name, id: e.id, useSSL: e.useSSL, + torEnabled: node.torEnabled, + clearnetEnabled: node.clearnetEnabled, ), ) .toList(); diff --git a/lib/services/price.dart b/lib/services/price.dart index dc4f3d8d8..5a97e43cb 100644 --- a/lib/services/price.dart +++ b/lib/services/price.dart @@ -30,6 +30,7 @@ class PriceAPI { BitcoinFrost: "bitcoin", Litecoin: "litecoin", Bitcoincash: "bitcoin-cash", + Cardano: "cardano", Dash: "dash", Dogecoin: "dogecoin", Epiccash: "epic-cash", diff --git a/lib/services/wallets.dart b/lib/services/wallets.dart index c4e75bde8..c606a75cf 100644 --- a/lib/services/wallets.dart +++ b/lib/services/wallets.dart @@ -10,8 +10,7 @@ import 'dart:async'; -import 'package:flutter_libmonero/monero/monero.dart' as monero; -import 'package:flutter_libmonero/wownero/wownero.dart' as wownero; +import 'package:compat/compat.dart' as lib_monero_compat; import 'package:isar/isar.dart'; import '../app_config.dart'; @@ -21,11 +20,12 @@ import '../utilities/enums/sync_type_enum.dart'; import '../utilities/flutter_secure_storage_interface.dart'; import '../utilities/logger.dart'; import '../utilities/prefs.dart'; +import '../utilities/stack_file_system.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; import '../wallets/isar/models/wallet_info.dart'; import '../wallets/wallet/impl/epiccash_wallet.dart'; +import '../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../wallets/wallet/wallet.dart'; -import '../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; import 'event_bus/events/wallet_added_event.dart'; import 'event_bus/global_event_bus.dart'; import 'node_service.dart'; @@ -84,17 +84,24 @@ class Wallets { key: Wallet.mnemonicPassphraseKey(walletId: walletId), ); await secureStorage.delete(key: Wallet.privateKeyKey(walletId: walletId)); + await secureStorage.delete( + key: Wallet.getViewOnlyWalletDataSecStoreKey(walletId: walletId), + ); if (info.coin is Wownero) { - final wowService = wownero.wownero - .createWowneroWalletService(DB.instance.moneroWalletInfoBox); - await wowService.remove(walletId); + await lib_monero_compat.deleteWalletFiles( + name: walletId, + type: lib_monero_compat.WalletType.wownero, + appRoot: await StackFileSystem.applicationRootDirectory(), + ); Logging.instance .log("monero wallet: $walletId deleted", level: LogLevel.Info); } else if (info.coin is Monero) { - final xmrService = monero.monero - .createMoneroWalletService(DB.instance.moneroWalletInfoBox); - await xmrService.remove(walletId); + await lib_monero_compat.deleteWalletFiles( + name: walletId, + type: lib_monero_compat.WalletType.monero, + appRoot: await StackFileSystem.applicationRootDirectory(), + ); Logging.instance .log("monero wallet: $walletId deleted", level: LogLevel.Info); } else if (info.coin is Epiccash) { @@ -220,7 +227,7 @@ class Wallets { final shouldSetAutoSync = shouldAutoSyncAll || walletIdsToEnableAutoSync.contains(walletInfo.walletId); - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { // walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); } else { walletInitFutures.add( @@ -327,7 +334,7 @@ class Wallets { nodeService: nodeService, prefs: prefs, ).then((wallet) { - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { // walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); walletIdCompleter.complete("dummy_ignore"); @@ -457,7 +464,7 @@ class Wallets { nodeService: nodeService, prefs: prefs, ).then((wallet) { - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { // walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); walletIdCompleter.complete("dummy_ignore"); @@ -577,7 +584,7 @@ class Wallets { walletIdsToEnableAutoSync.contains(wallet.walletId); if (isDesktop) { - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { // walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); } else { walletInitFutures.add( diff --git a/lib/themes/theme_service.dart b/lib/themes/theme_service.dart index 2d2ae212b..19050029c 100644 --- a/lib/themes/theme_service.dart +++ b/lib/themes/theme_service.dart @@ -31,7 +31,9 @@ final pThemeService = Provider((ref) { }); class ThemeService { - static const _currentDefaultThemeVersion = 15; + // dumb quick conditional based on name. Should really be done better + static const _currentDefaultThemeVersion = + AppConfig.appName == "Campfire" ? 17 : 16; ThemeService._(); static ThemeService? _instance; static ThemeService get instance => _instance ??= ThemeService._(); @@ -118,70 +120,42 @@ class ThemeService { Future checkDefaultThemesOnStartup() async { // install default themes if (!(await ThemeService.instance.verifyInstalled(themeId: "light"))) { - Logging.instance.log( - "Installing default light theme...", - level: LogLevel.Info, - ); - final lightZip = await rootBundle.load("assets/default_themes/light.zip"); - await ThemeService.instance - .install(themeArchiveData: lightZip.buffer.asUint8List()); - Logging.instance.log( - "Installing default light theme... finished", - level: LogLevel.Info, - ); + await _updateDefaultTheme("light"); } else { // check installed version final theme = ThemeService.instance.getTheme(themeId: "light"); if ((theme?.version ?? 1) < _currentDefaultThemeVersion) { - Logging.instance.log( - "Updating default light theme...", - level: LogLevel.Info, - ); - final lightZip = - await rootBundle.load("assets/default_themes/light.zip"); - await ThemeService.instance - .install(themeArchiveData: lightZip.buffer.asUint8List()); - Logging.instance.log( - "Updating default light theme... finished", - level: LogLevel.Info, - ); + await _updateDefaultTheme("light"); } } if (AppConfig.hasFeature(AppFeature.themeSelection)) { if (!(await ThemeService.instance.verifyInstalled(themeId: "dark"))) { - Logging.instance.log( - "Installing default dark theme... ", - level: LogLevel.Info, - ); - final darkZip = await rootBundle.load("assets/default_themes/dark.zip"); - await ThemeService.instance - .install(themeArchiveData: darkZip.buffer.asUint8List()); - Logging.instance.log( - "Installing default dark theme... finished", - level: LogLevel.Info, - ); + await _updateDefaultTheme("dark"); } else { // check installed version - // final theme = ThemeService.instance.getTheme(themeId: "dark"); - // Force update theme to add missing icons for now - // TODO: uncomment if statement in future when themes are version 4 or above - // if ((theme?.version ?? 1) < _currentDefaultThemeVersion) { - Logging.instance.log( - "Updating default dark theme...", - level: LogLevel.Info, - ); - final darkZip = await rootBundle.load("assets/default_themes/dark.zip"); - await ThemeService.instance - .install(themeArchiveData: darkZip.buffer.asUint8List()); - Logging.instance.log( - "Updating default dark theme... finished", - level: LogLevel.Info, - ); - // } + final theme = ThemeService.instance.getTheme(themeId: "dark"); + if ((theme?.version ?? 1) < _currentDefaultThemeVersion) { + await _updateDefaultTheme("dark"); + } } } } + Future _updateDefaultTheme(String name) async { + Logging.instance.log( + "Updating default $name theme...", + level: LogLevel.Info, + ); + final zip = await rootBundle.load("assets/default_themes/$name.zip"); + await ThemeService.instance.install( + themeArchiveData: zip.buffer.asUint8List(), + ); + Logging.instance.log( + "Updating default $name theme... finished", + level: LogLevel.Info, + ); + } + // TODO more thorough check/verification of theme Future verifyInstalled({required String themeId}) async { final theme = diff --git a/lib/utilities/address_utils.dart b/lib/utilities/address_utils.dart index 97f2ec965..2e6b241b8 100644 --- a/lib/utilities/address_utils.dart +++ b/lib/utilities/address_utils.dart @@ -10,202 +10,180 @@ import 'dart:convert'; -import 'logger.dart'; +import '../app_config.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; +import 'logger.dart'; class AddressUtils { + static final Set recognizedParams = { + 'amount', + 'label', + 'message', + 'tx_amount', // For Monero/Wownero. + 'tx_payment_id', + 'recipient_name', + 'tx_description', + // TODO [prio=med]: Add more recognized params for other coins. + }; + static String condenseAddress(String address) { return '${address.substring(0, 5)}...${address.substring(address.length - 5)}'; } - // static bool validateAddress(String address, Coin coin) { - // //This calls the validate address for each crypto coin, validateAddress is - // //only used in 2 places, so I just replaced the old functionality here - // switch (coin) { - // case Coin.bitcoin: - // return Bitcoin(CryptoCurrencyNetwork.main).validateAddress(address); - // case Coin.bitcoinFrost: - // return BitcoinFrost(CryptoCurrencyNetwork.main) - // .validateAddress(address); - // case Coin.litecoin: - // return Litecoin(CryptoCurrencyNetwork.main).validateAddress(address); - // case Coin.bitcoincash: - // return Bitcoincash(CryptoCurrencyNetwork.main).validateAddress(address); - // case Coin.dogecoin: - // return Dogecoin(CryptoCurrencyNetwork.main).validateAddress(address); - // case Coin.epicCash: - // return Epiccash(CryptoCurrencyNetwork.main).validateAddress(address); - // case Coin.ethereum: - // return Ethereum(CryptoCurrencyNetwork.main).validateAddress(address); - // case Coin.firo: - // return Firo(CryptoCurrencyNetwork.main).validateAddress(address); - // case Coin.eCash: - // return Ecash(CryptoCurrencyNetwork.main).validateAddress(address); - // case Coin.monero: - // return Monero(CryptoCurrencyNetwork.main).validateAddress(address); - // case Coin.wownero: - // return Wownero(CryptoCurrencyNetwork.main).validateAddress(address); - // case Coin.namecoin: - // return Namecoin(CryptoCurrencyNetwork.main).validateAddress(address); - // case Coin.particl: - // return Particl(CryptoCurrencyNetwork.main).validateAddress(address); - // case Coin.peercoin: - // return Peercoin(CryptoCurrencyNetwork.main).validateAddress(address); - // case Coin.solana: - // return Solana(CryptoCurrencyNetwork.main).validateAddress(address); - // case Coin.stellar: - // return Stellar(CryptoCurrencyNetwork.main).validateAddress(address); - // case Coin.nano: - // return Nano(CryptoCurrencyNetwork.main).validateAddress(address); - // case Coin.banano: - // return Banano(CryptoCurrencyNetwork.main).validateAddress(address); - // case Coin.tezos: - // return Tezos(CryptoCurrencyNetwork.main).validateAddress(address); - // case Coin.bitcoinTestNet: - // return Bitcoin(CryptoCurrencyNetwork.test).validateAddress(address); - // case Coin.bitcoinFrostTestNet: - // return BitcoinFrost(CryptoCurrencyNetwork.test) - // .validateAddress(address); - // case Coin.litecoinTestNet: - // return Litecoin(CryptoCurrencyNetwork.test).validateAddress(address); - // case Coin.bitcoincashTestnet: - // return Bitcoincash(CryptoCurrencyNetwork.test).validateAddress(address); - // case Coin.firoTestNet: - // return Firo(CryptoCurrencyNetwork.test).validateAddress(address); - // case Coin.dogecoinTestNet: - // return Dogecoin(CryptoCurrencyNetwork.test).validateAddress(address); - // case Coin.peercoinTestNet: - // return Peercoin(CryptoCurrencyNetwork.test).validateAddress(address); - // case Coin.stellarTestnet: - // return Stellar(CryptoCurrencyNetwork.test).validateAddress(address); - // } - // // throw Exception("moved"); - // // switch (coin) { - // // case Coin.bitcoin: - // // return Address.validateAddress(address, bitcoin); - // // case Coin.litecoin: - // // return Address.validateAddress(address, litecoin); - // // case Coin.bitcoincash: - // // try { - // // // 0 for bitcoincash: address scheme, 1 for legacy address - // // final format = bitbox.Address.detectFormat(address); - // // - // // if (coin == Coin.bitcoincashTestnet) { - // // return true; - // // } - // // - // // if (format == bitbox.Address.formatCashAddr) { - // // String addr = address; - // // if (addr.contains(":")) { - // // addr = addr.split(":").last; - // // } - // // - // // return addr.startsWith("q"); - // // } else { - // // return address.startsWith("1"); - // // } - // // } catch (e) { - // // return false; - // // } - // // case Coin.dogecoin: - // // return Address.validateAddress(address, dogecoin); - // // case Coin.epicCash: - // // return validateSendAddress(address) == "1"; - // // case Coin.ethereum: - // // return true; //TODO - validate ETH address - // // case Coin.firo: - // // return Address.validateAddress(address, firoNetwork); - // // case Coin.eCash: - // // return Address.validateAddress(address, eCashNetwork); - // // case Coin.monero: - // // return RegExp("[a-zA-Z0-9]{95}").hasMatch(address) || - // // RegExp("[a-zA-Z0-9]{106}").hasMatch(address); - // // case Coin.wownero: - // // return RegExp("[a-zA-Z0-9]{95}").hasMatch(address) || - // // RegExp("[a-zA-Z0-9]{106}").hasMatch(address); - // // case Coin.namecoin: - // // return Address.validateAddress(address, namecoin, namecoin.bech32!); - // // case Coin.particl: - // // return Address.validateAddress(address, particl); - // // case Coin.stellar: - // // return RegExp(r"^[G][A-Z0-9]{55}$").hasMatch(address); - // // case Coin.nano: - // // return NanoAccounts.isValid(NanoAccountType.NANO, address); - // // case Coin.banano: - // // return NanoAccounts.isValid(NanoAccountType.BANANO, address); - // // case Coin.tezos: - // // return RegExp(r"^tz[1-9A-HJ-NP-Za-km-z]{34}$").hasMatch(address); - // // case Coin.bitcoinTestNet: - // // return Address.validateAddress(address, testnet); - // // case Coin.litecoinTestNet: - // // return Address.validateAddress(address, litecointestnet); - // // case Coin.bitcoincashTestnet: - // // try { - // // // 0 for bitcoincash: address scheme, 1 for legacy address - // // final format = bitbox.Address.detectFormat(address); - // // - // // if (coin == Coin.bitcoincashTestnet) { - // // return true; - // // } - // // - // // if (format == bitbox.Address.formatCashAddr) { - // // String addr = address; - // // if (addr.contains(":")) { - // // addr = addr.split(":").last; - // // } - // // - // // return addr.startsWith("q"); - // // } else { - // // return address.startsWith("1"); - // // } - // // } catch (e) { - // // return false; - // // } - // // case Coin.firoTestNet: - // // return Address.validateAddress(address, firoTestNetwork); - // // case Coin.dogecoinTestNet: - // // return Address.validateAddress(address, dogecointestnet); - // // case Coin.stellarTestnet: - // // return RegExp(r"^[G][A-Z0-9]{55}$").hasMatch(address); - // // } - // } + /// Return only recognized parameters. + static Map _filterParams(Map params) { + return Map.fromEntries( + params.entries + .where((entry) => recognizedParams.contains(entry.key.toLowerCase())), + ); + } - /// parse an address uri - /// returns an empty map if the input string does not begin with "firo:" - static Map parseUri(String uri) { + /// Parses a URI string and returns a map with parsed components. + static Map _parseUri(String uri) { final Map result = {}; try { final u = Uri.parse(uri); if (u.hasScheme) { result["scheme"] = u.scheme.toLowerCase(); - result["address"] = u.path; - result.addAll(u.queryParameters); + + // Handle different URI formats. + if (result["scheme"] == "bitcoin" || + result["scheme"] == "bitcoincash") { + result["address"] = u.path; + } else if (result["scheme"] == "monero") { + // Monero addresses can contain '?' which Uri.parse interprets as query start. + final addressEnd = + uri.indexOf('?', 7); // 7 is the length of "monero:". + if (addressEnd != -1) { + result["address"] = uri.substring(7, addressEnd); + } else { + result["address"] = uri.substring(7); + } + } else { + // Default case, treat path as address. + result["address"] = u.path; + } + + // Parse query parameters. + result.addAll(_parseQueryParameters(u.queryParameters)); + + // Handle Monero-specific fragment (tx_description). + if (u.fragment.isNotEmpty && result["scheme"] == "monero") { + result["tx_description"] = Uri.decodeComponent(u.fragment); + } } } catch (e) { - Logging.instance - .log("Exception caught in parseUri($uri): $e", level: LogLevel.Error); + print("Exception caught in parseUri($uri): $e"); } return result; } - /// builds a uri string with the given address and query parameters if any + /// Helper method to parse and normalize query parameters. + static Map _parseQueryParameters(Map params) { + final Map result = {}; + params.forEach((key, value) { + final lowerKey = key.toLowerCase(); + if (recognizedParams.contains(lowerKey)) { + switch (lowerKey) { + case 'amount': + case 'tx_amount': + result['amount'] = _normalizeAmount(value); + break; + case 'label': + case 'recipient_name': + result['label'] = Uri.decodeComponent(value); + break; + case 'message': + case 'tx_description': + result['message'] = Uri.decodeComponent(value); + break; + case 'tx_payment_id': + result['tx_payment_id'] = Uri.decodeComponent(value); + break; + default: + result[lowerKey] = Uri.decodeComponent(value); + } + } else { + // Include unrecognized parameters as-is. + result[key] = Uri.decodeComponent(value); + } + }); + return result; + } + + /// Normalizes amount value to a standard format. + static String _normalizeAmount(String amount) { + // Remove any non-numeric characters except for '.' + final sanitized = amount.replaceAll(RegExp(r'[^\d.]'), ''); + // Ensure only one decimal point + final parts = sanitized.split('.'); + if (parts.length > 2) { + return '${parts[0]}.${parts.sublist(1).join()}'; + } + return sanitized; + } + + /// Centralized method to handle various cryptocurrency URIs and return a common object. + /// + /// Returns null on failure to parse + static PaymentUriData? parsePaymentUri( + String uri, { + Logging? logging, + }) { + try { + final Map parsedData = _parseUri(uri); + + // Normalize the URI scheme. + final String scheme = parsedData['scheme'] ?? ''; + parsedData.remove('scheme'); + + // Filter out unrecognized parameters. + final filteredParams = _filterParams(parsedData); + + return PaymentUriData( + scheme: scheme, + address: parsedData['address']!.trim(), + amount: filteredParams['amount'] ?? filteredParams['tx_amount'], + label: filteredParams['label'] ?? filteredParams['recipient_name'], + message: filteredParams['message'] ?? filteredParams['tx_description'], + paymentId: filteredParams['tx_payment_id'], + // Specific to Monero + additionalParams: filteredParams, + ); + } catch (e, s) { + logging?.log("$e\n$s", level: LogLevel.Error); + return null; + } + } + + /// Builds a uri string with the given address and query parameters (if any) static String buildUriString( - CryptoCurrency coin, + String scheme, String address, Map params, ) { - // TODO: other sanitation as well ? - String sanitizedAddress = address; - if (coin is Bitcoincash || coin is Ecash) { - final prefix = "${coin.uriScheme}:"; - if (address.startsWith(prefix)) { - sanitizedAddress = address.replaceFirst(prefix, ""); + // Filter unrecognized parameters. + final filteredParams = _filterParams(params); + String uriString = "$scheme:$address"; + + if (scheme.toLowerCase() == "monero") { + // Handle Monero-specific formatting. + if (filteredParams.containsKey("tx_description")) { + final description = filteredParams.remove("tx_description")!; + if (filteredParams.isNotEmpty) { + uriString += Uri(queryParameters: filteredParams).toString(); + } + uriString += "#${Uri.encodeComponent(description)}"; + } else if (filteredParams.isNotEmpty) { + uriString += Uri(queryParameters: filteredParams).toString(); + } + } else { + // General case for other cryptocurrencies. + if (filteredParams.isNotEmpty) { + uriString += Uri(queryParameters: filteredParams).toString(); } } - String uriString = "${coin.uriScheme}:$sanitizedAddress"; - if (params.isNotEmpty) { - uriString += Uri(queryParameters: params).toString(); - } + return uriString; } @@ -215,10 +193,7 @@ class AddressUtils { try { result = Map.from(jsonDecode(data) as Map); } catch (e) { - Logging.instance.log( - "Exception caught in parseQRSeedData($data): $e", - level: LogLevel.Error, - ); + print("Exception caught in parseQRSeedData($data): $e"); } return result; } @@ -227,4 +202,70 @@ class AddressUtils { static String encodeQRSeedData(List words) { return jsonEncode({"mnemonic": words}); } + + /// Method to get CryptoCurrency based on URI scheme. + static CryptoCurrency? _getCryptoCurrencyByScheme(String scheme) { + if (AppConfig.coins.map((e) => e.uriScheme).toSet().contains(scheme)) { + return AppConfig.coins.firstWhere((e) => e.uriScheme == scheme); + } else { + return null; + // throw UnsupportedError('Unsupported URI scheme: $scheme'); + } + } + + /// Formats an address string to remove any unnecessary prefixes or suffixes. + String formatAddress(String epicAddress) { + // strip http:// or https:// prefixes if the address contains an @ symbol (and is thus an epicbox address) + if ((epicAddress.startsWith("http://") || + epicAddress.startsWith("https://")) && + epicAddress.contains("@")) { + epicAddress = epicAddress.replaceAll("http://", ""); + epicAddress = epicAddress.replaceAll("https://", ""); + } + // strip mailto: prefix + if (epicAddress.startsWith("mailto:")) { + epicAddress = epicAddress.replaceAll("mailto:", ""); + } + // strip / suffix if the address contains an @ symbol (and is thus an epicbox address) + if (epicAddress.endsWith("/") && epicAddress.contains("@")) { + epicAddress = epicAddress.substring(0, epicAddress.length - 1); + } + return epicAddress; + } +} + +class PaymentUriData { + final String address; + final String? scheme; + final String? amount; + final String? label; + final String? message; + final String? paymentId; // Specific to Monero. + final Map additionalParams; + + CryptoCurrency? get coin => AddressUtils._getCryptoCurrencyByScheme( + scheme ?? "", // empty will just return null + ); + + PaymentUriData({ + required this.address, + this.scheme, + this.amount, + this.label, + this.message, + this.paymentId, + required this.additionalParams, + }); + + @override + String toString() => "PaymentUriData { " + "coin: $coin, " + "address: $address, " + "amount: $amount, " + "scheme: $scheme, " + "label: $label, " + "message: $message, " + "paymentId: $paymentId, " + "additionalParams: $additionalParams" + " }"; } diff --git a/lib/utilities/assets.dart b/lib/utilities/assets.dart index c8bfaf1fb..73c520410 100644 --- a/lib/utilities/assets.dart +++ b/lib/utilities/assets.dart @@ -205,6 +205,7 @@ class _SVG { String get robotHead => "assets/svg/robot-head.svg"; String get whirlPool => "assets/svg/whirlpool.svg"; String get cashFusion => "assets/svg/cashfusion-icon.svg"; + String get churn => "assets/svg/churn.svg"; String get fingerprint => "assets/svg/fingerprint.svg"; String get faceId => "assets/svg/faceid.svg"; String get tokens => "assets/svg/tokens.svg"; @@ -278,4 +279,5 @@ class _GIF { const _GIF(); String get stacyOnion => "assets/gif/stacy_onion.gif"; + String get moneroChanDance => "assets/gif/monero-chan-dance.gif"; } diff --git a/lib/utilities/biometrics.dart b/lib/utilities/biometrics.dart index 18e27cbfb..cf9d8c27d 100644 --- a/lib/utilities/biometrics.dart +++ b/lib/utilities/biometrics.dart @@ -10,8 +10,8 @@ import 'dart:io'; -import 'package:local_auth/auth_strings.dart'; import 'package:local_auth/local_auth.dart'; +import 'package:local_auth_android/local_auth_android.dart'; import 'logger.dart'; @@ -46,80 +46,43 @@ class Biometrics { final canCheckBiometrics = await localAuth.canCheckBiometrics; final isDeviceSupported = await localAuth.isDeviceSupported(); - //todo: check if print needed // debugPrint("canCheckBiometrics: $canCheckBiometrics"); // debugPrint("isDeviceSupported: $isDeviceSupported"); if (canCheckBiometrics && isDeviceSupported) { - final List availableSystems = + List availableSystems = await localAuth.getAvailableBiometrics(); - //todo: check if print needed - // debugPrint("availableSystems: $availableSystems"); + Logging.instance.log( + "Bio availableSystems: $availableSystems", + level: LogLevel.Info, + ); //TODO properly handle caught exceptions - if (Platform.isIOS) { - if (availableSystems.contains(BiometricType.face)) { - try { - final bool didAuthenticate = await localAuth.authenticate( - biometricOnly: true, - localizedReason: localizedReason, - stickyAuth: true, - iOSAuthStrings: const IOSAuthMessages(), - ); + try { + final bool didAuthenticate = await localAuth.authenticate( + localizedReason: localizedReason, + options: const AuthenticationOptions( + stickyAuth: true, + biometricOnly: true, + ), + authMessages: [ + AndroidAuthMessages( + biometricHint: "", + cancelButton: cancelButtonText, + signInTitle: title, + ), + ], + ); - if (didAuthenticate) { - return true; - } - } catch (e) { - Logging.instance.log( - "local_auth exception caught in Biometrics.authenticate(), e: $e", - level: LogLevel.Error, - ); - } - } else if (availableSystems.contains(BiometricType.fingerprint)) { - try { - final bool didAuthenticate = await localAuth.authenticate( - biometricOnly: true, - localizedReason: localizedReason, - stickyAuth: true, - iOSAuthStrings: const IOSAuthMessages(), - ); - - if (didAuthenticate) { - return true; - } - } catch (e) { - Logging.instance.log( - "local_auth exception caught in Biometrics.authenticate(), e: $e", - level: LogLevel.Error, - ); - } - } - } else if (Platform.isAndroid) { - if (availableSystems.contains(BiometricType.fingerprint)) { - try { - final bool didAuthenticate = await localAuth.authenticate( - biometricOnly: true, - localizedReason: localizedReason, - stickyAuth: true, - androidAuthStrings: AndroidAuthMessages( - biometricHint: "", - cancelButton: cancelButtonText, - signInTitle: title, - ), - ); - - if (didAuthenticate) { - return true; - } - } catch (e) { - Logging.instance.log( - "local_auth exception caught in Biometrics.authenticate(), e: $e", - level: LogLevel.Error, - ); - } + if (didAuthenticate) { + return true; } + } catch (e) { + Logging.instance.log( + "local_auth exception caught in Biometrics.authenticate(), e: $e", + level: LogLevel.Error, + ); } } diff --git a/lib/utilities/enums/derive_path_type_enum.dart b/lib/utilities/enums/derive_path_type_enum.dart index 8981cf1c3..3d64fb45a 100644 --- a/lib/utilities/enums/derive_path_type_enum.dart +++ b/lib/utilities/enums/derive_path_type_enum.dart @@ -18,7 +18,8 @@ enum DerivePathType { eth, eCash44, solana, - bip86; + bip86, + cardanoShelley; AddressType getAddressType() { switch (this) { @@ -41,6 +42,9 @@ enum DerivePathType { case DerivePathType.bip86: return AddressType.p2tr; + + case DerivePathType.cardanoShelley: + return AddressType.cardanoShelley; } } } diff --git a/lib/utilities/git_status.dart b/lib/utilities/git_status.dart index 818660427..093bc39af 100644 --- a/lib/utilities/git_status.dart +++ b/lib/utilities/git_status.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_libepiccash/git_versions.dart' as epic_versions; -import 'package:flutter_libmonero/git_versions.dart' as monero_versions; +// import 'package:flutter_libmonero/git_versions.dart' as monero_versions; import 'package:http/http.dart'; import 'package:lelantus/git_versions.dart' as firo_versions; @@ -20,7 +20,7 @@ enum CommitStatus { isHead, isOldCommit, notACommit, notLoaded } abstract class GitStatus { static String get firoCommit => firo_versions.getPluginVersion(); static String get epicCashCommit => epic_versions.getPluginVersion(); - static String get moneroCommit => monero_versions.getPluginVersion(); + // static String get moneroCommit => monero_versions.getPluginVersion(); static String get appCommitHash => AppConfig.commitHash; @@ -78,31 +78,31 @@ abstract class GitStatus { return _cachedEpicStatus!; } - - static CommitStatus? _cachedMoneroStatus; - static Future getMoneroCommitStatus() async { - if (_cachedMoneroStatus != null) { - return _cachedMoneroStatus!; - } - - final List results = await Future.wait([ - _doesCommitExist("cypherstack", "flutter_libmonero", moneroCommit), - _isHeadCommit("cypherstack", "flutter_libmonero", "main", moneroCommit), - ]); - - final commitExists = results[0]; - final commitIsHead = results[1]; - - if (commitExists && commitIsHead) { - _cachedMoneroStatus = CommitStatus.isHead; - } else if (commitExists) { - _cachedMoneroStatus = CommitStatus.isOldCommit; - } else { - _cachedMoneroStatus = CommitStatus.notACommit; - } - - return _cachedMoneroStatus!; - } + // + // static CommitStatus? _cachedMoneroStatus; + // static Future getMoneroCommitStatus() async { + // if (_cachedMoneroStatus != null) { + // return _cachedMoneroStatus!; + // } + // + // final List results = await Future.wait([ + // _doesCommitExist("cypherstack", "flutter_libmonero", moneroCommit), + // _isHeadCommit("cypherstack", "flutter_libmonero", "main", moneroCommit), + // ]); + // + // final commitExists = results[0]; + // final commitIsHead = results[1]; + // + // if (commitExists && commitIsHead) { + // _cachedMoneroStatus = CommitStatus.isHead; + // } else if (commitExists) { + // _cachedMoneroStatus = CommitStatus.isOldCommit; + // } else { + // _cachedMoneroStatus = CommitStatus.notACommit; + // } + // + // return _cachedMoneroStatus!; + // } static TextStyle styleForStatus(CommitStatus status, BuildContext context) { final Color color; diff --git a/lib/utilities/paynym_is_api.dart b/lib/utilities/paynym_is_api.dart index 8b42e6143..f81fc855a 100644 --- a/lib/utilities/paynym_is_api.dart +++ b/lib/utilities/paynym_is_api.dart @@ -25,7 +25,7 @@ import 'prefs.dart'; // todo: better error message parsing (from response itself?) class PaynymIsApi { - static const String baseURL = "https://paynym.is/api"; + static const String baseURL = "https://paynym.rs"; static const String version = "/v1"; HTTP client = HTTP(); @@ -35,9 +35,8 @@ class PaynymIsApi { Map body, [ Map additionalHeaders = const {}, ]) async { - final String url = baseURL + - version + - (endpoint.startsWith("/") ? endpoint : "/$endpoint"); + final String url = + "$baseURL/api$version${endpoint.startsWith("/") ? endpoint : "/$endpoint"}"; final uri = Uri.parse(url); // Calculate the body length. diff --git a/lib/utilities/prefs.dart b/lib/utilities/prefs.dart index 78a461ec7..5ffc7a059 100644 --- a/lib/utilities/prefs.dart +++ b/lib/utilities/prefs.dart @@ -71,6 +71,7 @@ class Prefs extends ChangeNotifier { _useTor = await _getUseTor(); _fusionServerInfo = await _getFusionServerInfo(); _autoPin = await _getAutoPin(); + _enableExchange = await _getEnableExchange(); _initialized = true; } @@ -1131,4 +1132,30 @@ class Prefs extends ChangeNotifier { ) as bool? ?? false; } + + // Show or hide exchange (buy & swap) features. + + bool _enableExchange = true; + + bool get enableExchange => _enableExchange; + + set enableExchange(bool showExchange) { + if (_enableExchange != showExchange) { + DB.instance.put( + boxName: DB.boxNamePrefs, + key: "showExchange", + value: showExchange, + ); + _enableExchange = showExchange; + notifyListeners(); + } + } + + Future _getEnableExchange() async { + return await DB.instance.get( + boxName: DB.boxNamePrefs, + key: "showExchange", + ) as bool? ?? + true; + } } diff --git a/lib/utilities/show_node_tor_settings_mismatch.dart b/lib/utilities/show_node_tor_settings_mismatch.dart new file mode 100644 index 000000000..6c2490b51 --- /dev/null +++ b/lib/utilities/show_node_tor_settings_mismatch.dart @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart'; + +import '../services/node_service.dart'; +import '../wallets/crypto_currency/crypto_currency.dart'; +import '../widgets/conditional_parent.dart'; +import '../widgets/desktop/desktop_dialog.dart'; +import '../widgets/desktop/primary_button.dart'; +import '../widgets/desktop/secondary_button.dart'; +import '../widgets/stack_dialog.dart'; +import 'prefs.dart'; +import 'text_styles.dart'; +import 'util.dart'; + +Future checkShowNodeTorSettingsMismatch({ + required BuildContext context, + required CryptoCurrency currency, + required Prefs prefs, + required NodeService nodeService, + required bool allowCancel, + bool rootNavigator = false, +}) async { + final node = + nodeService.getPrimaryNodeFor(currency: currency) ?? currency.defaultNode; + if (prefs.useTor) { + if (node.torEnabled) { + return true; + } + } else { + if (node.clearnetEnabled) { + return true; + } + } + + final result = await showDialog( + context: context, + barrierDismissible: false, + builder: (context) => ConditionalParent( + condition: Util.isDesktop, + builder: (child) => DesktopDialog( + maxHeight: double.infinity, + child: Padding( + padding: const EdgeInsets.all(32), + child: child, + ), + ), + child: ConditionalParent( + condition: !Util.isDesktop, + builder: (child) => StackDialogBase( + child: child, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Attention! Node connection issue detected. " + "The current node will not sync due to its connectivity settings. " + "Please adjust the node settings or enable/disable TOR.", + style: STextStyles.w600_16(context), + ), + SizedBox( + height: Util.isDesktop ? 32 : 24, + ), + Row( + children: [ + allowCancel + ? Expanded( + child: SecondaryButton( + buttonHeight: Util.isDesktop ? ButtonHeight.l : null, + label: "Cancel", + onPressed: () { + Navigator.of(context).pop(false); + }, + ), + ) + : const Spacer(), + SizedBox( + width: Util.isDesktop ? 24 : 16, + ), + Expanded( + child: PrimaryButton( + buttonHeight: Util.isDesktop ? ButtonHeight.l : null, + label: "Continue", + onPressed: () { + Navigator.of(context).pop(true); + }, + ), + ), + ], + ), + ], + ), + ), + ), + ); + + return result ?? true; +} diff --git a/lib/utilities/test_epic_box_connection.dart b/lib/utilities/test_epic_box_connection.dart index 039fa7ec8..7fff18369 100644 --- a/lib/utilities/test_epic_box_connection.dart +++ b/lib/utilities/test_epic_box_connection.dart @@ -15,6 +15,7 @@ import '../pages/settings_views/global_settings_view/manage_nodes_views/add_edit import '../services/tor_service.dart'; import 'logger.dart'; import 'prefs.dart'; +import 'tor_plain_net_option_enum.dart'; Future _testEpicBoxNodeConnection(Uri uri) async { final HTTP client = HTTP(); @@ -50,6 +51,17 @@ Future testEpicNodeConnection(NodeFormData data) async { if (data.host == null || data.port == null || data.useSSL == null) { return null; } + + if (Prefs.instance.useTor) { + if (data.netOption == TorPlainNetworkOption.clear) { + return null; + } + } else { + if (data.netOption == TorPlainNetworkOption.tor) { + return null; + } + } + const String path_postfix = "/v1/version"; if (data.host!.startsWith("https://")) { diff --git a/lib/utilities/test_monero_node_connection.dart b/lib/utilities/test_monero_node_connection.dart index 7af89f6d8..aef26ba9a 100644 --- a/lib/utilities/test_monero_node_connection.dart +++ b/lib/utilities/test_monero_node_connection.dart @@ -38,34 +38,93 @@ Future testMoneroNodeConnection( int port, })? proxyInfo, }) async { - final httpClient = HttpClient(); - MoneroNodeConnectionResponse? badCertResponse; - - try { - if (proxyInfo != null) { - SocksTCPClient.assignToHttpClient(httpClient, [ - ProxySettings( - proxyInfo.host, - proxyInfo.port, - ), - ]); + if (uri.host.endsWith(".onion")) { + if (proxyInfo == null) { + // If the host ends in .onion, we can't access it without Tor. + return MoneroNodeConnectionResponse(null, null, null, false); } - httpClient.badCertificateCallback = (cert, url, port) { - if (allowBadX509Certificate) { - return true; + SOCKSSocket? socket; + try { + // An HttpClient cannot be used for onion nodes. + // + // The SOCKSSocket class from the tor_ffi_plugin package can be used to + // connect to .onion addresses. We'll do the same things as above but + // with SOCKSSocket instead of httpClient. + socket = await SOCKSSocket.create( + proxyHost: proxyInfo.host.address, + proxyPort: proxyInfo.port, + sslEnabled: false, + ); + await socket.connect(); + await socket.connectTo(uri.host, uri.port); + + final body = jsonEncode({ + "jsonrpc": "2.0", + "id": "0", + "method": "get_info", + }); + + final request = 'POST /json_rpc HTTP/1.1\r\n' + 'Host: ${uri.host}\r\n' + 'Content-Type: application/json\r\n' + 'Content-Length: ${body.length}\r\n' + '\r\n' + '$body'; + + socket.write(request); + print("Request sent: $request"); + + final buffer = StringBuffer(); + await for (var response in socket.inputStream) { + buffer.write(utf8.decode(response)); + if (buffer.toString().contains("\r\n\r\n")) { + break; + } } - if (badCertResponse == null) { - badCertResponse = MoneroNodeConnectionResponse(cert, url, port, false); - } else { + final result = buffer.toString(); + print("Response received: $result"); + + // Check if the response contains "results" and does not contain "error" + final success = + result.contains('"result":') && !result.contains('"error"'); + + return MoneroNodeConnectionResponse(null, null, null, success); + } catch (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Warning); + return MoneroNodeConnectionResponse(null, null, null, false); + } finally { + await socket?.close(); + } + } else { + final httpClient = HttpClient(); + MoneroNodeConnectionResponse? badCertResponse; + try { + if (proxyInfo != null) { + SocksTCPClient.assignToHttpClient(httpClient, [ + ProxySettings( + proxyInfo.host, + proxyInfo.port, + ), + ]); + } + + httpClient.badCertificateCallback = (cert, url, port) { + if (allowBadX509Certificate) { + return true; + } + + if (badCertResponse == null) { + badCertResponse = + MoneroNodeConnectionResponse(cert, url, port, false); + } else { + return false; + } + return false; - } + }; - return false; - }; - - if (!uri.host.endsWith('.onion')) { final request = await httpClient.postUrl(uri); final body = utf8.encode( @@ -91,62 +150,22 @@ Future testMoneroNodeConnection( final response = await request.close(); final result = await response.transform(utf8.decoder).join(); - // TODO: json decoded without error so assume connection exists? - // or we can check for certain values in the response to decide - return MoneroNodeConnectionResponse(null, null, null, true); - } else { - // If the URL ends in .onion, we can't use an httpClient to connect to it. - // - // The SOCKSSocket class from the tor_ffi_plugin package can be used to - // connect to .onion addresses. We'll do the same things as above but - // with SOCKSSocket instead of httpClient. - final socket = await SOCKSSocket.create( - proxyHost: proxyInfo!.host.address, - proxyPort: proxyInfo.port, - sslEnabled: false, - ); - await socket.connect(); - await socket.connectTo(uri.host, uri.port); + // print("HTTP Response: $result"); - final body = utf8.encode( - jsonEncode({ - "jsonrpc": "2.0", - "id": "0", - "method": "get_info", - }), - ); + final success = + result.contains('"result":') && !result.contains('"error"'); - // Write the request body to the socket. - socket.write(body); - - // Read the response. - final response = await socket.inputStream.first; - final result = utf8.decode(response); - - // Close the socket. - await socket.close(); - return MoneroNodeConnectionResponse(null, null, null, true); - - // Parse the response. - // - // This is commented because any issues should throw. - // final Map jsonResponse = jsonDecode(result); - // print(jsonResponse); - // if (jsonResponse.containsKey('result')) { - // return MoneroNodeConnectionResponse(null, null, null, true); - // } else { - // return MoneroNodeConnectionResponse(null, null, null, false); - // } + return MoneroNodeConnectionResponse(null, null, null, success); + } catch (e, s) { + if (badCertResponse != null) { + return badCertResponse!; + } else { + Logging.instance.log("$e\n$s", level: LogLevel.Warning); + return MoneroNodeConnectionResponse(null, null, null, false); + } + } finally { + httpClient.close(force: true); } - } catch (e, s) { - if (badCertResponse != null) { - return badCertResponse!; - } else { - Logging.instance.log("$e\n$s", level: LogLevel.Warning); - return MoneroNodeConnectionResponse(null, null, null, false); - } - } finally { - httpClient.close(force: true); } } diff --git a/lib/utilities/test_node_connection.dart b/lib/utilities/test_node_connection.dart index 2160671db..127702b14 100644 --- a/lib/utilities/test_node_connection.dart +++ b/lib/utilities/test_node_connection.dart @@ -4,11 +4,14 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:on_chain/ada/ada.dart'; +import 'package:socks5_proxy/socks.dart'; import '../networking/http.dart'; import '../pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart'; import '../providers/global/prefs_provider.dart'; import '../services/tor_service.dart'; +import '../wallets/api/cardano/blockfrost_http_provider.dart'; import '../wallets/api/tezos/tezos_rpc_api.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; import '../wallets/crypto_currency/interfaces/electrumx_currency_interface.dart'; @@ -21,6 +24,7 @@ import 'test_epic_box_connection.dart'; import 'test_eth_node_connection.dart'; import 'test_monero_node_connection.dart'; import 'test_stellar_node_connection.dart'; +import 'tor_plain_net_option_enum.dart'; Future _xmrHelper( NodeFormData nodeFormData, @@ -41,11 +45,15 @@ Future _xmrHelper( final uriString = "${uri.scheme}://${uri.host}:${port ?? 0}$path"; + if (proxyInfo == null && uri.host.endsWith(".onion")) { + return false; + } + final response = await testMoneroNodeConnection( Uri.parse(uriString), false, proxyInfo: proxyInfo, - ); + ).timeout(Duration(seconds: proxyInfo != null ? 30 : 10)); if (response.cert != null) { if (context.mounted) { @@ -84,6 +92,24 @@ Future testNodeConnection({ }) async { final formData = nodeFormData; + if (ref.read(prefsChangeNotifierProvider).useTor) { + if (formData.netOption! == TorPlainNetworkOption.clear) { + Logging.instance.log( + "This node is configured for non-TOR only but TOR is enabled", + level: LogLevel.Warning, + ); + return false; + } + } else { + if (formData.netOption! == TorPlainNetworkOption.tor) { + Logging.instance.log( + "This node is configured for TOR only but TOR is disabled", + level: LogLevel.Warning, + ); + return false; + } + } + bool testPassed = false; switch (cryptoCurrency) { @@ -109,7 +135,7 @@ Future testNodeConnection({ final url = formData.host!; final uri = Uri.tryParse(url); if (uri != null) { - if (!uri.hasScheme) { + if (!uri.hasScheme && !uri.host.endsWith(".onion")) { // try https first testPassed = await _xmrHelper( formData @@ -228,6 +254,39 @@ Future testNodeConnection({ testPassed = false; } break; + + case Cardano(): + try { + final client = HttpClient(); + if (ref.read(prefsChangeNotifierProvider).useTor) { + final proxyInfo = TorService.sharedInstance.getProxyInfo(); + final proxySettings = ProxySettings( + proxyInfo.host, + proxyInfo.port, + ); + SocksTCPClient.assignToHttpClient(client, [proxySettings]); + } + final blockfrostProvider = BlockforestProvider( + BlockfrostHttpProvider( + url: "${formData.host!}:${formData.port!}/api/v0", + client: client, + ), + ); + + final health = await blockfrostProvider.request( + BlockfrostRequestBackendHealthStatus(), + ); + + Logging.instance.log( + "Cardano testNodeConnection \"health=$health\"", + level: LogLevel.Info, + ); + + return health; + } catch (_) { + testPassed = false; + } + break; } return testPassed; diff --git a/lib/utilities/tor_plain_net_option_enum.dart b/lib/utilities/tor_plain_net_option_enum.dart new file mode 100644 index 000000000..26b5604d9 --- /dev/null +++ b/lib/utilities/tor_plain_net_option_enum.dart @@ -0,0 +1,23 @@ +enum TorPlainNetworkOption { + tor, + clear, + both; + + bool allowsTor() => this == tor || this == both; + bool allowsClear() => this == clear || this == both; + + static TorPlainNetworkOption fromNodeData( + bool torEnabled, + bool clearEnabled, + ) { + if (clearEnabled && torEnabled) { + return TorPlainNetworkOption.both; + } else if (torEnabled) { + return TorPlainNetworkOption.tor; + } else if (clearEnabled) { + return TorPlainNetworkOption.clear; + } else { + return TorPlainNetworkOption.both; + } + } +} diff --git a/lib/wallets/api/cardano/blockfrost_http_provider.dart b/lib/wallets/api/cardano/blockfrost_http_provider.dart new file mode 100644 index 000000000..bf00691a9 --- /dev/null +++ b/lib/wallets/api/cardano/blockfrost_http_provider.dart @@ -0,0 +1,53 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:cbor/simple.dart'; +import 'package:on_chain/ada/src/provider/blockfrost/core/core.dart'; +import 'package:on_chain/ada/src/provider/service/service.dart'; + +import '../../../utilities/logger.dart'; + +class BlockfrostHttpProvider implements BlockfrostServiceProvider { + BlockfrostHttpProvider({ + required this.url, + this.version = "v0", + this.projectId, + HttpClient? client, + this.defaultRequestTimeout = const Duration(seconds: 30), + }) : client = client ?? HttpClient(); + @override + final String url; + final String version; + final String? projectId; + final HttpClient client; + final Duration defaultRequestTimeout; + + @override + Future get(BlockforestRequestDetails params, + [Duration? timeout,]) async { + final response = await client.getUrl(Uri.parse(params.url(url, "api/$version"))).timeout(timeout ?? defaultRequestTimeout); + response.headers.add("Content-Type", "application/json"); + response.headers.add("Accept", "application/json"); + if (projectId != null) { + response.headers.add("project_id", projectId!); + } + final responseStream = await response.close(); + final data = json.decode(await responseStream.transform(utf8.decoder).join()); + return data; + } + + @override + Future post(BlockforestRequestDetails params, + [Duration? timeout,]) async { + final request = await client.postUrl(Uri.parse(params.url(url, "api/$version"))).timeout(timeout ?? defaultRequestTimeout); + // Need to change this for other operations than submitting transactions + request.headers.add("Content-Type", "application/cbor"); + request.headers.add("Accept", "application/json"); + if (projectId != null) { + request.headers.add("project_id", projectId!); + } + request.add(params.body as List); + final response = await request.close(); + final data = json.decode(await response.transform(utf8.decoder).join()); + return data; + } +} diff --git a/lib/wallets/crypto_currency/coins/banano.dart b/lib/wallets/crypto_currency/coins/banano.dart index 4bb5184d2..8b1e1114b 100644 --- a/lib/wallets/crypto_currency/coins/banano.dart +++ b/lib/wallets/crypto_currency/coins/banano.dart @@ -77,6 +77,8 @@ class Banano extends NanoCurrency { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); default: diff --git a/lib/wallets/crypto_currency/coins/bitcoin.dart b/lib/wallets/crypto_currency/coins/bitcoin.dart index 3b75e2186..35a9cf8f0 100644 --- a/lib/wallets/crypto_currency/coins/bitcoin.dart +++ b/lib/wallets/crypto_currency/coins/bitcoin.dart @@ -239,6 +239,8 @@ class Bitcoin extends Bip39HDCurrency coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); case CryptoCurrencyNetwork.test: @@ -252,6 +254,8 @@ class Bitcoin extends Bip39HDCurrency coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); case CryptoCurrencyNetwork.test4: @@ -265,6 +269,8 @@ class Bitcoin extends Bip39HDCurrency coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); default: diff --git a/lib/wallets/crypto_currency/coins/bitcoin_frost.dart b/lib/wallets/crypto_currency/coins/bitcoin_frost.dart index f09d1865e..d1f22227e 100644 --- a/lib/wallets/crypto_currency/coins/bitcoin_frost.dart +++ b/lib/wallets/crypto_currency/coins/bitcoin_frost.dart @@ -73,6 +73,8 @@ class BitcoinFrost extends FrostCurrency { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); case CryptoCurrencyNetwork.test: @@ -86,6 +88,8 @@ class BitcoinFrost extends FrostCurrency { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); case CryptoCurrencyNetwork.test4: @@ -99,6 +103,8 @@ class BitcoinFrost extends FrostCurrency { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); default: @@ -127,9 +133,21 @@ class BitcoinFrost extends FrostCurrency { ); @override - String pubKeyToScriptHash({required Uint8List pubKey}) { + Uint8List addressToPubkey({required String address}) { try { - return Bip39HDCurrency.convertBytesToScriptHash(pubKey); + final addr = coinlib.Address.fromString(address, networkParams); + return addr.program.script.compiled; + } catch (e) { + rethrow; + } + } + + @override + String addressToScriptHash({required String address}) { + try { + return Bip39HDCurrency.convertBytesToScriptHash( + addressToPubkey(address: address), + ); } catch (e) { rethrow; } diff --git a/lib/wallets/crypto_currency/coins/bitcoincash.dart b/lib/wallets/crypto_currency/coins/bitcoincash.dart index 9d9a291a8..6f6d5e4b5 100644 --- a/lib/wallets/crypto_currency/coins/bitcoincash.dart +++ b/lib/wallets/crypto_currency/coins/bitcoincash.dart @@ -298,6 +298,8 @@ class Bitcoincash extends Bip39HDCurrency with ElectrumXCurrencyInterface { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); case CryptoCurrencyNetwork.test: @@ -311,6 +313,8 @@ class Bitcoincash extends Bip39HDCurrency with ElectrumXCurrencyInterface { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); default: diff --git a/lib/wallets/crypto_currency/coins/cardano.dart b/lib/wallets/crypto_currency/coins/cardano.dart new file mode 100644 index 000000000..71ea2c372 --- /dev/null +++ b/lib/wallets/crypto_currency/coins/cardano.dart @@ -0,0 +1,129 @@ +import '../../../models/isar/models/blockchain_data/address.dart'; +import '../../../models/node_model.dart'; +import '../../../utilities/default_nodes.dart'; +import '../../../utilities/enums/derive_path_type_enum.dart'; +import '../crypto_currency.dart'; +import '../intermediate/bip39_currency.dart'; + +class Cardano extends Bip39Currency { + Cardano(super.network) { + _idMain = "cardano"; + _uriScheme = "cardano"; + switch (network) { + case CryptoCurrencyNetwork.main: + _id = _idMain; + _name = "Cardano"; + _ticker = "ADA"; + default: + throw Exception("Unsupported network: $network"); + } + } + + late final String _id; + + @override + String get identifier => _id; + + late final String _idMain; + + @override + String get mainNetId => _idMain; + + late final String _name; + + @override + String get prettyName => _name; + + late final String _uriScheme; + + @override + String get uriScheme => _uriScheme; + + late final String _ticker; + + @override + String get ticker => _ticker; + + @override + AddressType get defaultAddressType => AddressType.cardanoShelley; + + @override + Uri defaultBlockExplorer(String txid) { + switch (network) { + case CryptoCurrencyNetwork.main: + return Uri.parse( + "https://explorer.cardano.org/en/transaction?id=$txid"); + default: + throw Exception( + "Unsupported network for defaultBlockExplorer(): $network", + ); + } + } + + @override + DerivePathType get defaultDerivePathType => DerivePathType.cardanoShelley; + + @override + NodeModel get defaultNode { + switch (network) { + case CryptoCurrencyNetwork.main: + return NodeModel( + host: "https://cardano.stackwallet.com", + port: 443, + name: DefaultNodes.defaultName, + id: DefaultNodes.buildId(this), + useSSL: true, + enabled: true, + coinName: identifier, + isFailover: true, + isDown: false, + torEnabled: true, + clearnetEnabled: true, + ); + + default: + throw Exception("Unsupported network: $network"); + } + } + + @override + int get defaultSeedPhraseLength => 15; + + @override + int get fractionDigits => 6; + + @override + String get genesisHash => + "f0f7892b5c333cffc4b3c4344de48af4cc63f55e44936196f365a9ef2244134f"; + + @override + bool get hasBuySupport => false; + + @override + bool get hasMnemonicPassphraseSupport => false; + + @override + int get minConfirms => 2; + + @override + List get possibleMnemonicLengths => [defaultSeedPhraseLength]; + + @override + BigInt get satsPerCoin => BigInt.from(1000000); + + @override + int get targetBlockTimeSeconds => 20; + + @override + bool get torSupport => true; + + @override + bool validateAddress(String address) { + switch (network) { + case CryptoCurrencyNetwork.main: + return RegExp(r"^addr1[0-9a-zA-Z]{98}$").hasMatch(address); + default: + throw Exception("Unsupported network: $network"); + } + } +} diff --git a/lib/wallets/crypto_currency/coins/dash.dart b/lib/wallets/crypto_currency/coins/dash.dart index 366bbbcc8..1a63f1811 100644 --- a/lib/wallets/crypto_currency/coins/dash.dart +++ b/lib/wallets/crypto_currency/coins/dash.dart @@ -189,6 +189,8 @@ class Dash extends Bip39HDCurrency with ElectrumXCurrencyInterface { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); default: diff --git a/lib/wallets/crypto_currency/coins/dogecoin.dart b/lib/wallets/crypto_currency/coins/dogecoin.dart index 1de949edc..ce10bc0d7 100644 --- a/lib/wallets/crypto_currency/coins/dogecoin.dart +++ b/lib/wallets/crypto_currency/coins/dogecoin.dart @@ -189,6 +189,8 @@ class Dogecoin extends Bip39HDCurrency with ElectrumXCurrencyInterface { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); case CryptoCurrencyNetwork.test: @@ -202,6 +204,8 @@ class Dogecoin extends Bip39HDCurrency with ElectrumXCurrencyInterface { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); default: diff --git a/lib/wallets/crypto_currency/coins/ecash.dart b/lib/wallets/crypto_currency/coins/ecash.dart index ad1ec4f6c..4074b249d 100644 --- a/lib/wallets/crypto_currency/coins/ecash.dart +++ b/lib/wallets/crypto_currency/coins/ecash.dart @@ -289,6 +289,8 @@ class Ecash extends Bip39HDCurrency with ElectrumXCurrencyInterface { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); default: diff --git a/lib/wallets/crypto_currency/coins/epiccash.dart b/lib/wallets/crypto_currency/coins/epiccash.dart index 6e47eb411..25fdd6c9c 100644 --- a/lib/wallets/crypto_currency/coins/epiccash.dart +++ b/lib/wallets/crypto_currency/coins/epiccash.dart @@ -80,6 +80,8 @@ class Epiccash extends Bip39Currency { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); default: diff --git a/lib/wallets/crypto_currency/coins/ethereum.dart b/lib/wallets/crypto_currency/coins/ethereum.dart index 5c0f5b06d..1d7bb53ed 100644 --- a/lib/wallets/crypto_currency/coins/ethereum.dart +++ b/lib/wallets/crypto_currency/coins/ethereum.dart @@ -57,6 +57,8 @@ class Ethereum extends Bip39Currency { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); @override diff --git a/lib/wallets/crypto_currency/coins/firo.dart b/lib/wallets/crypto_currency/coins/firo.dart index 9e4a3bddf..26957600d 100644 --- a/lib/wallets/crypto_currency/coins/firo.dart +++ b/lib/wallets/crypto_currency/coins/firo.dart @@ -54,6 +54,9 @@ class Firo extends Bip39HDCurrency with ElectrumXCurrencyInterface { @override int get minConfirms => 1; + @override + int get minCoinbaseConfirms => 100; + @override bool get torSupport => true; @@ -228,6 +231,8 @@ class Firo extends Bip39HDCurrency with ElectrumXCurrencyInterface { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); case CryptoCurrencyNetwork.test: @@ -254,6 +259,8 @@ class Firo extends Bip39HDCurrency with ElectrumXCurrencyInterface { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); default: diff --git a/lib/wallets/crypto_currency/coins/litecoin.dart b/lib/wallets/crypto_currency/coins/litecoin.dart index 1c09a0c2a..91b444f73 100644 --- a/lib/wallets/crypto_currency/coins/litecoin.dart +++ b/lib/wallets/crypto_currency/coins/litecoin.dart @@ -220,6 +220,8 @@ class Litecoin extends Bip39HDCurrency with ElectrumXCurrencyInterface { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); case CryptoCurrencyNetwork.test: @@ -233,6 +235,8 @@ class Litecoin extends Bip39HDCurrency with ElectrumXCurrencyInterface { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); default: diff --git a/lib/wallets/crypto_currency/coins/monero.dart b/lib/wallets/crypto_currency/coins/monero.dart index edd389ec3..7e0347775 100644 --- a/lib/wallets/crypto_currency/coins/monero.dart +++ b/lib/wallets/crypto_currency/coins/monero.dart @@ -1,4 +1,5 @@ -import 'package:monero/monero.dart' as monero; +import 'package:cs_monero/src/ffi_bindings/monero_wallet_bindings.dart' + as xmr_wallet_ffi; import '../../../models/node_model.dart'; import '../../../utilities/default_nodes.dart'; @@ -50,7 +51,7 @@ class Monero extends CryptonoteCurrency { bool validateAddress(String address) { switch (network) { case CryptoCurrencyNetwork.main: - return monero.Wallet_addressValid(address, 0); + return xmr_wallet_ffi.validateAddress(address, 0); default: throw Exception("Unsupported network: $network"); } @@ -71,6 +72,8 @@ class Monero extends CryptonoteCurrency { isFailover: true, isDown: false, trusted: true, + torEnabled: true, + clearnetEnabled: true, ); default: diff --git a/lib/wallets/crypto_currency/coins/namecoin.dart b/lib/wallets/crypto_currency/coins/namecoin.dart index 9f0134b9a..e390d599e 100644 --- a/lib/wallets/crypto_currency/coins/namecoin.dart +++ b/lib/wallets/crypto_currency/coins/namecoin.dart @@ -102,6 +102,8 @@ class Namecoin extends Bip39HDCurrency with ElectrumXCurrencyInterface { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); // case CryptoCurrencyNetwork.test: // TODO: [prio=low] Add testnet support. diff --git a/lib/wallets/crypto_currency/coins/nano.dart b/lib/wallets/crypto_currency/coins/nano.dart index 32f49cbec..0909b8aa8 100644 --- a/lib/wallets/crypto_currency/coins/nano.dart +++ b/lib/wallets/crypto_currency/coins/nano.dart @@ -77,6 +77,8 @@ class Nano extends NanoCurrency { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); default: diff --git a/lib/wallets/crypto_currency/coins/particl.dart b/lib/wallets/crypto_currency/coins/particl.dart index e83eaf4ed..067aae72d 100644 --- a/lib/wallets/crypto_currency/coins/particl.dart +++ b/lib/wallets/crypto_currency/coins/particl.dart @@ -97,6 +97,8 @@ class Particl extends Bip39HDCurrency with ElectrumXCurrencyInterface { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); // case CryptoCurrencyNetwork.test: // TODO: [prio=low] Add testnet. diff --git a/lib/wallets/crypto_currency/coins/peercoin.dart b/lib/wallets/crypto_currency/coins/peercoin.dart index ba291cc14..b67b0e1ab 100644 --- a/lib/wallets/crypto_currency/coins/peercoin.dart +++ b/lib/wallets/crypto_currency/coins/peercoin.dart @@ -103,6 +103,8 @@ class Peercoin extends Bip39HDCurrency with ElectrumXCurrencyInterface { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); case CryptoCurrencyNetwork.test: @@ -116,6 +118,8 @@ class Peercoin extends Bip39HDCurrency with ElectrumXCurrencyInterface { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); default: diff --git a/lib/wallets/crypto_currency/coins/solana.dart b/lib/wallets/crypto_currency/coins/solana.dart index 6cfc185e1..1db985c76 100644 --- a/lib/wallets/crypto_currency/coins/solana.dart +++ b/lib/wallets/crypto_currency/coins/solana.dart @@ -55,6 +55,8 @@ class Solana extends Bip39Currency { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); default: throw Exception("Unsupported network: $network"); diff --git a/lib/wallets/crypto_currency/coins/stellar.dart b/lib/wallets/crypto_currency/coins/stellar.dart index f0606f1f0..d799de31b 100644 --- a/lib/wallets/crypto_currency/coins/stellar.dart +++ b/lib/wallets/crypto_currency/coins/stellar.dart @@ -68,6 +68,8 @@ class Stellar extends Bip39Currency { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); case CryptoCurrencyNetwork.test: @@ -81,6 +83,8 @@ class Stellar extends Bip39Currency { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); default: diff --git a/lib/wallets/crypto_currency/coins/tezos.dart b/lib/wallets/crypto_currency/coins/tezos.dart index e99831ca5..0cb8cca0c 100644 --- a/lib/wallets/crypto_currency/coins/tezos.dart +++ b/lib/wallets/crypto_currency/coins/tezos.dart @@ -118,6 +118,8 @@ class Tezos extends Bip39Currency { coinName: identifier, isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); default: diff --git a/lib/wallets/crypto_currency/coins/wownero.dart b/lib/wallets/crypto_currency/coins/wownero.dart index 8624ecb75..e043fdd7b 100644 --- a/lib/wallets/crypto_currency/coins/wownero.dart +++ b/lib/wallets/crypto_currency/coins/wownero.dart @@ -1,4 +1,5 @@ -import 'package:cw_wownero/api/wallet.dart' as wownero_wallet; +import 'package:cs_monero/src/ffi_bindings/wownero_wallet_bindings.dart' + as wow_wallet_ffi; import '../../../models/node_model.dart'; import '../../../utilities/default_nodes.dart'; @@ -48,7 +49,12 @@ class Wownero extends CryptonoteCurrency { @override bool validateAddress(String address) { - return wownero_wallet.addressValid(address); + switch (network) { + case CryptoCurrencyNetwork.main: + return wow_wallet_ffi.validateAddress(address, 0); + default: + throw Exception("Unsupported network: $network"); + } } @override @@ -66,6 +72,8 @@ class Wownero extends CryptonoteCurrency { isFailover: true, isDown: false, trusted: true, + torEnabled: true, + clearnetEnabled: true, ); default: @@ -86,7 +94,7 @@ class Wownero extends CryptonoteCurrency { bool get hasMnemonicPassphraseSupport => false; @override - List get possibleMnemonicLengths => [defaultSeedPhraseLength, 25]; + List get possibleMnemonicLengths => [defaultSeedPhraseLength, 16, 25]; @override BigInt get satsPerCoin => BigInt.from(100000000000); diff --git a/lib/wallets/crypto_currency/crypto_currency.dart b/lib/wallets/crypto_currency/crypto_currency.dart index 788c87b4f..066675d28 100644 --- a/lib/wallets/crypto_currency/crypto_currency.dart +++ b/lib/wallets/crypto_currency/crypto_currency.dart @@ -6,6 +6,7 @@ export 'coins/banano.dart'; export 'coins/bitcoin.dart'; export 'coins/bitcoin_frost.dart'; export 'coins/bitcoincash.dart'; +export 'coins/cardano.dart'; export 'coins/dash.dart'; export 'coins/dogecoin.dart'; export 'coins/ecash.dart'; @@ -59,6 +60,7 @@ abstract class CryptoCurrency { bool get torSupport => false; int get minConfirms; + int get minCoinbaseConfirms => minConfirms; // TODO: [prio=low] could be handled differently as (at least) epiccash does not use this String get genesisHash; diff --git a/lib/wallets/crypto_currency/interfaces/view_only_option_currency_interface.dart b/lib/wallets/crypto_currency/interfaces/view_only_option_currency_interface.dart new file mode 100644 index 000000000..dec5c016b --- /dev/null +++ b/lib/wallets/crypto_currency/interfaces/view_only_option_currency_interface.dart @@ -0,0 +1,5 @@ +import '../crypto_currency.dart'; + +mixin ViewOnlyOptionCurrencyInterface on CryptoCurrency { + // +} diff --git a/lib/wallets/crypto_currency/intermediate/bip39_hd_currency.dart b/lib/wallets/crypto_currency/intermediate/bip39_hd_currency.dart index bb97b6754..44b2f79e2 100644 --- a/lib/wallets/crypto_currency/intermediate/bip39_hd_currency.dart +++ b/lib/wallets/crypto_currency/intermediate/bip39_hd_currency.dart @@ -5,9 +5,11 @@ import 'package:flutter/foundation.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/enums/derive_path_type_enum.dart'; +import '../interfaces/view_only_option_currency_interface.dart'; import 'bip39_currency.dart'; -abstract class Bip39HDCurrency extends Bip39Currency { +abstract class Bip39HDCurrency extends Bip39Currency + implements ViewOnlyOptionCurrencyInterface { Bip39HDCurrency(super.network); coinlib.Network get networkParams; @@ -39,6 +41,25 @@ abstract class Bip39HDCurrency extends Bip39Currency { } } + List get supportedHardenedDerivationPaths { + final paths = supportedDerivationPathTypes.map( + (e) => ( + path: e, + addressType: e.getAddressType(), + ), + ); + + return paths.map((e) { + final path = constructDerivePath( + derivePathType: e.path, + chain: 0, + index: 0, + ); + // trim unhardened + return path.substring(0, path.lastIndexOf("'") + 1); + }).toList(); + } + static String convertBytesToScriptHash(Uint8List bytes) { final hash = sha256.convert(bytes.toList(growable: false)).toString(); diff --git a/lib/wallets/crypto_currency/intermediate/cryptonote_currency.dart b/lib/wallets/crypto_currency/intermediate/cryptonote_currency.dart index 496336235..319a501ac 100644 --- a/lib/wallets/crypto_currency/intermediate/cryptonote_currency.dart +++ b/lib/wallets/crypto_currency/intermediate/cryptonote_currency.dart @@ -1,7 +1,9 @@ import '../../../models/isar/models/blockchain_data/address.dart'; import '../crypto_currency.dart'; +import '../interfaces/view_only_option_currency_interface.dart'; -abstract class CryptonoteCurrency extends CryptoCurrency { +abstract class CryptonoteCurrency extends CryptoCurrency + with ViewOnlyOptionCurrencyInterface { CryptonoteCurrency(super.network); @override diff --git a/lib/wallets/crypto_currency/intermediate/frost_currency.dart b/lib/wallets/crypto_currency/intermediate/frost_currency.dart index fd452bb62..c39e5cbce 100644 --- a/lib/wallets/crypto_currency/intermediate/frost_currency.dart +++ b/lib/wallets/crypto_currency/intermediate/frost_currency.dart @@ -6,7 +6,11 @@ import '../crypto_currency.dart'; abstract class FrostCurrency extends CryptoCurrency { FrostCurrency(super.network); - String pubKeyToScriptHash({required Uint8List pubKey}); + // String pubKeyToScriptHash({required Uint8List pubKey}); + + String addressToScriptHash({required String address}); + + Uint8List addressToPubkey({required String address}); Amount get dustLimit; } diff --git a/lib/wallets/isar/models/frost_wallet_info.g.dart b/lib/wallets/isar/models/frost_wallet_info.g.dart index 6c80125e2..a2c7534d0 100644 --- a/lib/wallets/isar/models/frost_wallet_info.g.dart +++ b/lib/wallets/isar/models/frost_wallet_info.g.dart @@ -7,7 +7,7 @@ part of 'frost_wallet_info.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetFrostWalletInfoCollection on Isar { IsarCollection get frostWalletInfo => this.collection(); @@ -68,7 +68,7 @@ const FrostWalletInfoSchema = CollectionSchema( getId: _frostWalletInfoGetId, getLinks: _frostWalletInfoGetLinks, attach: _frostWalletInfoAttach, - version: '3.0.5', + version: '3.1.8', ); int _frostWalletInfoEstimateSize( diff --git a/lib/wallets/isar/models/spark_coin.g.dart b/lib/wallets/isar/models/spark_coin.g.dart index 75cd92b62..717d1a2c6 100644 --- a/lib/wallets/isar/models/spark_coin.g.dart +++ b/lib/wallets/isar/models/spark_coin.g.dart @@ -7,7 +7,7 @@ part of 'spark_coin.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetSparkCoinCollection on Isar { IsarCollection get sparkCoins => this.collection(); @@ -139,7 +139,7 @@ const SparkCoinSchema = CollectionSchema( getId: _sparkCoinGetId, getLinks: _sparkCoinGetLinks, attach: _sparkCoinAttach, - version: '3.0.5', + version: '3.1.8', ); int _sparkCoinEstimateSize( diff --git a/lib/wallets/isar/models/token_wallet_info.g.dart b/lib/wallets/isar/models/token_wallet_info.g.dart index bdf6a060a..f5d8555ab 100644 --- a/lib/wallets/isar/models/token_wallet_info.g.dart +++ b/lib/wallets/isar/models/token_wallet_info.g.dart @@ -7,7 +7,7 @@ part of 'token_wallet_info.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetTokenWalletInfoCollection on Isar { IsarCollection get tokenWalletInfo => this.collection(); @@ -68,7 +68,7 @@ const TokenWalletInfoSchema = CollectionSchema( getId: _tokenWalletInfoGetId, getLinks: _tokenWalletInfoGetLinks, attach: _tokenWalletInfoAttach, - version: '3.0.5', + version: '3.1.8', ); int _tokenWalletInfoEstimateSize( diff --git a/lib/wallets/isar/models/wallet_info.dart b/lib/wallets/isar/models/wallet_info.dart index 6a7a9b54c..eb6a4e9ea 100644 --- a/lib/wallets/isar/models/wallet_info.dart +++ b/lib/wallets/isar/models/wallet_info.dart @@ -6,6 +6,7 @@ import 'package:uuid/uuid.dart'; import '../../../app_config.dart'; import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; +import '../../../models/keys/view_only_wallet_data.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../isar_id_interface.dart'; import 'wallet_info_meta.dart'; @@ -117,6 +118,17 @@ class WalletInfo implements IsarId { ? {} : Map.from(jsonDecode(otherDataJsonString!) as Map); + @ignore + bool get isViewOnly => + otherData[WalletInfoKeys.isViewOnlyKey] as bool? ?? false; + + @ignore + ViewOnlyWalletType? get viewOnlyWalletType { + final index = otherData[WalletInfoKeys.viewOnlyTypeIndexKey] as int?; + if (index == null) return null; + return ViewOnlyWalletType.values[index]; + } + Future isMnemonicVerified(Isar isar) async => (await isar.walletInfoMeta.where().walletIdEqualTo(walletId).findFirst()) ?.isMnemonicVerified == @@ -512,4 +524,6 @@ abstract class WalletInfoKeys { "firoSparkCacheSetTimestampCacheKey"; static const String enableOptInRbf = "enableOptInRbfKey"; static const String reuseAddress = "reuseAddressKey"; + static const String isViewOnlyKey = "isViewOnlyKey"; + static const String viewOnlyTypeIndexKey = "viewOnlyTypeIndexKey"; } diff --git a/lib/wallets/isar/models/wallet_info.g.dart b/lib/wallets/isar/models/wallet_info.g.dart index bc0a5828e..89c5511fa 100644 --- a/lib/wallets/isar/models/wallet_info.g.dart +++ b/lib/wallets/isar/models/wallet_info.g.dart @@ -7,7 +7,7 @@ part of 'wallet_info.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetWalletInfoCollection on Isar { IsarCollection get walletInfo => this.collection(); @@ -114,7 +114,7 @@ const WalletInfoSchema = CollectionSchema( getId: _walletInfoGetId, getLinks: _walletInfoGetLinks, attach: _walletInfoAttach, - version: '3.0.5', + version: '3.1.8', ); int _walletInfoEstimateSize( diff --git a/lib/wallets/isar/models/wallet_info_meta.dart b/lib/wallets/isar/models/wallet_info_meta.dart index 62d37b7d4..ce0820a1a 100644 --- a/lib/wallets/isar/models/wallet_info_meta.dart +++ b/lib/wallets/isar/models/wallet_info_meta.dart @@ -1,4 +1,5 @@ import 'package:isar/isar.dart'; + import '../isar_id_interface.dart'; part 'wallet_info_meta.g.dart'; diff --git a/lib/wallets/isar/models/wallet_info_meta.g.dart b/lib/wallets/isar/models/wallet_info_meta.g.dart index 619bc1ac5..d8f578929 100644 --- a/lib/wallets/isar/models/wallet_info_meta.g.dart +++ b/lib/wallets/isar/models/wallet_info_meta.g.dart @@ -7,7 +7,7 @@ part of 'wallet_info_meta.dart'; // ************************************************************************** // coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types extension GetWalletInfoMetaCollection on Isar { IsarCollection get walletInfoMeta => this.collection(); @@ -53,7 +53,7 @@ const WalletInfoMetaSchema = CollectionSchema( getId: _walletInfoMetaGetId, getLinks: _walletInfoMetaGetLinks, attach: _walletInfoMetaAttach, - version: '3.0.5', + version: '3.1.8', ); int _walletInfoMetaEstimateSize( diff --git a/lib/wallets/models/tx_data.dart b/lib/wallets/models/tx_data.dart index 99a28a138..652a5605f 100644 --- a/lib/wallets/models/tx_data.dart +++ b/lib/wallets/models/tx_data.dart @@ -1,5 +1,4 @@ -import 'package:cw_monero/pending_monero_transaction.dart'; -import 'package:cw_wownero/pending_wownero_transaction.dart'; +import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:tezart/tezart.dart' as tezart; import 'package:web3dart/web3dart.dart' as web3dart; @@ -49,11 +48,8 @@ class TxData { final BigInt? chainId; final BigInt? feeInWei; - // wownero specific - final PendingWowneroTransaction? pendingWowneroTransaction; - - // monero specific - final PendingMoneroTransaction? pendingMoneroTransaction; + // wownero and monero specific + final lib_monero.PendingTransaction? pendingTransaction; // firo lelantus specific final int? jMintValue; @@ -104,8 +100,7 @@ class TxData { this.nonce, this.chainId, this.feeInWei, - this.pendingWowneroTransaction, - this.pendingMoneroTransaction, + this.pendingTransaction, this.jMintValue, this.spendCoinIndexes, this.height, @@ -120,11 +115,27 @@ class TxData { this.ignoreCachedBalanceChecks = false, }); - Amount? get amount => recipients != null && recipients!.isNotEmpty - ? recipients! + Amount? get amount { + if (recipients != null && recipients!.isNotEmpty) { + final sum = recipients! .map((e) => e.amount) - .reduce((total, amount) => total += amount) - : null; + .reduce((total, amount) => total += amount); + + // special xmr/wow check + if (pendingTransaction?.amount != null && fee != null) { + if (pendingTransaction!.amount + fee!.raw == sum.raw) { + return Amount( + rawValue: pendingTransaction!.amount, + fractionDigits: recipients!.first.amount.fractionDigits, + ); + } + } + + return sum; + } + + return null; + } Amount? get amountSpark => sparkRecipients != null && sparkRecipients!.isNotEmpty @@ -141,10 +152,22 @@ class TxData { fractionDigits: recipients!.first.amount.fractionDigits, ); } else { - return recipients! + final sum = recipients! .where((e) => !e.isChange) .map((e) => e.amount) .reduce((total, amount) => total += amount); + + // special xmr/wow check + if (pendingTransaction?.amount != null && fee != null) { + if (pendingTransaction!.amount + fee!.raw == sum.raw) { + return Amount( + rawValue: pendingTransaction!.amount, + fractionDigits: recipients!.first.amount.fractionDigits, + ); + } + } + + return sum; } } else { return null; @@ -196,8 +219,7 @@ class TxData { int? nonce, BigInt? chainId, BigInt? feeInWei, - PendingWowneroTransaction? pendingWowneroTransaction, - PendingMoneroTransaction? pendingMoneroTransaction, + lib_monero.PendingTransaction? pendingTransaction, int? jMintValue, List? spendCoinIndexes, int? height, @@ -241,10 +263,7 @@ class TxData { nonce: nonce ?? this.nonce, chainId: chainId ?? this.chainId, feeInWei: feeInWei ?? this.feeInWei, - pendingWowneroTransaction: - pendingWowneroTransaction ?? this.pendingWowneroTransaction, - pendingMoneroTransaction: - pendingMoneroTransaction ?? this.pendingMoneroTransaction, + pendingTransaction: pendingTransaction ?? this.pendingTransaction, jMintValue: jMintValue ?? this.jMintValue, spendCoinIndexes: spendCoinIndexes ?? this.spendCoinIndexes, height: height ?? this.height, @@ -284,8 +303,7 @@ class TxData { 'nonce: $nonce, ' 'chainId: $chainId, ' 'feeInWei: $feeInWei, ' - 'pendingWowneroTransaction: $pendingWowneroTransaction, ' - 'pendingMoneroTransaction: $pendingMoneroTransaction, ' + 'pendingTransaction: $pendingTransaction, ' 'jMintValue: $jMintValue, ' 'spendCoinIndexes: $spendCoinIndexes, ' 'height: $height, ' diff --git a/lib/wallets/wallet/impl/bitcoin_frost_wallet.dart b/lib/wallets/wallet/impl/bitcoin_frost_wallet.dart index d41d3d4c2..313f1afb6 100644 --- a/lib/wallets/wallet/impl/bitcoin_frost_wallet.dart +++ b/lib/wallets/wallet/impl/bitcoin_frost_wallet.dart @@ -1,10 +1,13 @@ import 'dart:async'; import 'dart:ffi'; +import 'dart:math'; import 'package:flutter/foundation.dart'; import 'package:frostdart/frostdart.dart' as frost; import 'package:frostdart/frostdart_bindings_generated.dart'; +import 'package:frostdart/util.dart'; import 'package:isar/isar.dart'; + import '../../../electrumx_rpc/cached_electrumx_client.dart'; import '../../../electrumx_rpc/electrumx_client.dart'; import '../../../models/balance.dart'; @@ -24,10 +27,15 @@ import '../../../utilities/logger.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../crypto_currency/intermediate/frost_currency.dart'; import '../../isar/models/frost_wallet_info.dart'; +import '../../isar/models/wallet_info.dart'; import '../../models/tx_data.dart'; import '../wallet.dart'; +import '../wallet_mixin_interfaces/multi_address_interface.dart'; -class BitcoinFrostWallet extends Wallet { +const kFrostSecureStartingIndex = 1; + +class BitcoinFrostWallet extends Wallet + with MultiAddressInterface { BitcoinFrostWallet(CryptoCurrencyNetwork network) : super(BitcoinFrost(network) as T); @@ -77,25 +85,11 @@ class BitcoinFrostWallet extends Wallet { await mainDB.isar.frostWalletInfo.put(frostWalletInfo); }); - final keys = frost.deserializeKeys(keys: serializedKeys); - - final addressString = frost.addressForKeys( - network: cryptoCurrency.network == CryptoCurrencyNetwork.main - ? Network.Mainnet - : Network.Testnet, - keys: keys, - ); - - final publicKey = frost.scriptPubKeyForKeys(keys: keys); - - final address = Address( - walletId: info.walletId, - value: addressString, - publicKey: publicKey.toUint8ListFromHex, - derivationIndex: 0, - derivationPath: null, - subType: AddressSubType.receiving, - type: AddressType.unknown, + final address = await _generateAddress( + change: 0, + index: kFrostSecureStartingIndex, + serializedKeys: serializedKeys, + secure: true, ); await mainDB.putAddresses([address]); @@ -110,7 +104,6 @@ class BitcoinFrostWallet extends Wallet { Future frostCreateSignConfig({ required TxData txData, - required String changeAddress, required int feePerWeight, }) async { try { @@ -136,6 +129,7 @@ class BitcoinFrostWallet extends Wallet { (e) => !e.isConfirmed( currentHeight, cryptoCurrency.minConfirms, + cryptoCurrency.minCoinbaseConfirms, ), ); if (utxos.isEmpty) { @@ -152,44 +146,87 @@ class BitcoinFrostWallet extends Wallet { fractionDigits: cryptoCurrency.fractionDigits, ); final Set utxosToUse = {}; - for (final utxo in utxos) { + final Set utxosRemaining = {}; + for (int i = 0; i < utxos.length; i++) { + final utxo = utxos[i]; sum += Amount( rawValue: BigInt.from(utxo.value), fractionDigits: cryptoCurrency.fractionDigits, ); utxosToUse.add(utxo); if (sum > total) { + if (i + 1 < utxos.length) { + utxosRemaining.addAll(utxos.sublist(i)); + } break; } } - final serializedKeys = await getSerializedKeys(); - final keys = frost.deserializeKeys(keys: serializedKeys!); - final int network = cryptoCurrency.network == CryptoCurrencyNetwork.main ? Network.Mainnet : Network.Testnet; - final publicKey = frost - .scriptPubKeyForKeys( - keys: keys, - ) - .toUint8ListFromHex; + final List< + ({ + UTXO utxo, + Uint8List scriptPubKey, + ({int account, int index, bool change}) addressDerivationData + })> inputs = []; - final config = Frost.createSignConfig( - network: network, - inputs: utxosToUse - .map( - (e) => ( - utxo: e, + for (final utxo in utxosToUse) { + final dData = await getDerivationData( + utxo.address, + ); + final publicKey = cryptoCurrency.addressToPubkey( + address: utxo.address!, + ); + + inputs.add( + ( + utxo: utxo, + scriptPubKey: publicKey, + addressDerivationData: dData, + ), + ); + } + await checkChangeAddressForTransactions(); + final changeAddress = await getCurrentChangeAddress(); + + String? config; + + while (config == null) { + try { + config = Frost.createSignConfig( + network: network, + inputs: inputs, + outputs: txData.recipients!, + changeAddress: changeAddress!.value, + feePerWeight: feePerWeight, + serializedKeys: (await getSerializedKeys())!, + ); + } on FrostdartException catch (e) { + if (e.errorCode == NOT_ENOUGH_FUNDS_ERROR && + utxosRemaining.isNotEmpty) { + // add extra utxo + final utxo = utxosRemaining.take(1).first; + final dData = await getDerivationData( + utxo.address, + ); + final publicKey = cryptoCurrency.addressToPubkey( + address: utxo.address!, + ); + inputs.add( + ( + utxo: utxo, scriptPubKey: publicKey, + addressDerivationData: dData, ), - ) - .toList(), - outputs: txData.recipients!, - changeAddress: (await getCurrentReceivingAddress())!.value, - feePerWeight: feePerWeight, - ); + ); + } else { + rethrow; + } + } + } return txData.copyWith(frostMSConfig: config, utxos: utxosToUse); } catch (_) { @@ -197,6 +234,44 @@ class BitcoinFrostWallet extends Wallet { } } + Future<({int account, int index, bool change})> getDerivationData( + String? address, + ) async { + if (address == null) { + throw Exception("Missing address required for FROST signing"); + } + + final addr = await mainDB.getAddress(walletId, address); + if (addr == null) { + throw Exception("Missing address in DB required for FROST signing"); + } + + final dPath = addr.derivationPath?.value ?? "0/0/0"; + + try { + final components = dPath.split("/").map((e) => int.parse(e)).toList(); + + if (components.length != 3) { + throw Exception( + "Unexpected derivation data `$components` for FROST signing", + ); + } + if (components[1] != 0 && components[1] != 1) { + throw Exception( + "${components[1]} must be 1 or 0 for change", + ); + } + + return ( + account: components[0], + change: components[1] == 1, + index: components[2], + ); + } catch (_) { + rethrow; + } + } + Future< ({ Pointer machinePtr, @@ -256,7 +331,11 @@ class BitcoinFrostWallet extends Wallet { final height = await chainHeight; for (final output in (await mainDB.getUTXOs(walletId).findAll())) { if (!output.isBlocked && - output.isConfirmed(height, cryptoCurrency.minConfirms)) { + output.isConfirmed( + height, + cryptoCurrency.minConfirms, + cryptoCurrency.minCoinbaseConfirms, + )) { available += output.value; inputCount++; } @@ -305,6 +384,14 @@ class BitcoinFrostWallet extends Wallet { property: r"subType", value: AddressSubType.change, ), + const FilterCondition.equalTo( + property: r"zSafeFrost", + value: true, + ), + const FilterCondition.greaterThan( + property: r"derivationIndex", + value: 0, + ), ], ); @@ -319,21 +406,41 @@ class BitcoinFrostWallet extends Wallet { property: r"subType", value: AddressSubType.receiving, ), + const FilterCondition.equalTo( + property: r"zSafeFrost", + value: true, + ), + const FilterCondition.greaterThan( + property: r"derivationIndex", + value: 0, + ), ], ); @override Future updateTransactions() async { - final myAddress = (await getCurrentReceivingAddress())!; + // Get all addresses. + final List

allAddressesOld = + await _fetchAddressesForElectrumXScan(); - final scriptHash = cryptoCurrency.pubKeyToScriptHash( - pubKey: Uint8List.fromList(myAddress.publicKey), - ); - final allTxHashes = - (await electrumXClient.getHistory(scripthash: scriptHash)).toSet(); + // Separate receiving and change addresses. + final Set receivingAddresses = allAddressesOld + .where((e) => e.subType == AddressSubType.receiving) + .map((e) => e.value) + .toSet(); + final Set changeAddresses = allAddressesOld + .where((e) => e.subType == AddressSubType.change) + .map((e) => e.value) + .toSet(); + + // Remove duplicates. + final allAddressesSet = {...receivingAddresses, ...changeAddresses}; final currentHeight = await chainHeight; - final coin = info.coin; + + // Fetch history from ElectrumX. + final List> allTxHashes = + await _fetchHistory(allAddressesSet); final List> allTransactions = []; @@ -346,11 +453,15 @@ class BitcoinFrostWallet extends Wallet { .findFirst(); if (storedTx == null || - !storedTx.isConfirmed(currentHeight, cryptoCurrency.minConfirms)) { + !storedTx.isConfirmed( + currentHeight, + cryptoCurrency.minConfirms, + cryptoCurrency.minCoinbaseConfirms, + )) { final tx = await electrumXCachedClient.getTransaction( txHash: txHash["tx_hash"] as String, verbose: true, - cryptoCurrency: coin, + cryptoCurrency: cryptoCurrency, ); if (!_duplicateTxCheck(allTransactions, tx["txid"] as String)) { @@ -371,6 +482,7 @@ class BitcoinFrostWallet extends Wallet { // Parse inputs. BigInt amountReceivedInThisWallet = BigInt.zero; + BigInt changeAmountReceivedInThisWallet = BigInt.zero; final List inputs = []; for (final jsonInput in txData["vin"] as List) { final map = Map.from(jsonInput as Map); @@ -421,7 +533,7 @@ class BitcoinFrostWallet extends Wallet { ); // Check if input was from this wallet. - if (input.addresses.contains(myAddress.value)) { + if (allAddressesSet.intersection(input.addresses.toSet()).isNotEmpty) { wasSentFromThisWallet = true; input = input.copyWith(walletOwns: true); } @@ -441,10 +553,18 @@ class BitcoinFrostWallet extends Wallet { ); // If output was to my wallet, add value to amount received. - if (output.addresses.contains(myAddress.value)) { + if (receivingAddresses + .intersection(output.addresses.toSet()) + .isNotEmpty) { wasReceivedInThisWallet = true; amountReceivedInThisWallet += output.value; output = output.copyWith(walletOwns: true); + } else if (changeAddresses + .intersection(output.addresses.toSet()) + .isNotEmpty) { + wasReceivedInThisWallet = true; + changeAmountReceivedInThisWallet += output.value; + output = output.copyWith(walletOwns: true); } outputs.add(output); @@ -478,7 +598,8 @@ class BitcoinFrostWallet extends Wallet { type = TransactionType.outgoing; if (wasReceivedInThisWallet) { - if (amountReceivedInThisWallet == totalOut) { + if (changeAmountReceivedInThisWallet + amountReceivedInThisWallet == + totalOut) { // Definitely sent all to self. type = TransactionType.sentToSelf; } else if (amountReceivedInThisWallet == BigInt.zero) { @@ -488,6 +609,8 @@ class BitcoinFrostWallet extends Wallet { } else if (wasReceivedInThisWallet) { // Only found outputs owned by this wallet. type = TransactionType.incoming; + + // TODO: [prio=none] Check for special Bitcoin outputs like ordinals. } else { Logging.instance.log( "Unexpected tx found (ignoring it): $txData", @@ -524,28 +647,30 @@ class BitcoinFrostWallet extends Wallet { if (address == null) { final serializedKeys = await getSerializedKeys(); if (serializedKeys != null) { - final keys = frost.deserializeKeys(keys: serializedKeys); + int index = kFrostSecureStartingIndex; + const someSaneMaximum = 200; + Address? address; + while (index < someSaneMaximum) { + try { + address = await _generateAddress( + change: 0, + index: index, + serializedKeys: serializedKeys, + secure: true, + ); - final addressString = frost.addressForKeys( - network: cryptoCurrency.network == CryptoCurrencyNetwork.main - ? Network.Mainnet - : Network.Testnet, - keys: keys, - ); + await mainDB.updateOrPutAddresses([address]); + } catch (_) {} + if (address != null) { + break; + } + index++; + } - final publicKey = frost.scriptPubKeyForKeys(keys: keys); - - final address = Address( - walletId: walletId, - value: addressString, - publicKey: publicKey.toUint8ListFromHex, - derivationIndex: 0, - derivationPath: null, - subType: AddressSubType.receiving, - type: AddressType.frostMS, - ); - - await mainDB.updateOrPutAddresses([address]); + if (index >= someSaneMaximum) { + throw Exception( + "index < kFrostSecureStartingIndex hit someSaneMaximum"); + } } else { Logging.instance.log( "$runtimeType.checkSaveInitialReceivingAddress() failed due" @@ -729,30 +854,83 @@ class BitcoinFrostWallet extends Wallet { await mainDB.deleteWalletBlockchainData(walletId); } - final keys = frost.deserializeKeys(keys: serializedKeys!); await _saveSerializedKeys(serializedKeys!); await _saveMultisigConfig(multisigConfig!); - final addressString = frost.addressForKeys( - network: cryptoCurrency.network == CryptoCurrencyNetwork.main - ? Network.Mainnet - : Network.Testnet, - keys: keys, + const receiveChain = 0; + const changeChain = 1; + final List addresses})>> + receiveFutures = [ + _checkGapsLinearly( + serializedKeys, + receiveChain, + secure: true, + ), + ]; + final List addresses})>> + changeFutures = [ + _checkGapsLinearly( + serializedKeys, + changeChain, + secure: true, + ), + ]; + + // io limitations may require running these linearly instead + final futuresResult = await Future.wait([ + Future.wait(receiveFutures), + Future.wait(changeFutures), + ]); + + final receiveResults = futuresResult[0]; + final changeResults = futuresResult[1]; + + final List
addressesToStore = []; + + int highestReceivingIndexWithHistory = 0; + + for (final tuple in receiveResults) { + if (tuple.addresses.isEmpty) { + await checkReceivingAddressForTransactions(); + } else { + highestReceivingIndexWithHistory = max( + tuple.index, + highestReceivingIndexWithHistory, + ); + addressesToStore.addAll(tuple.addresses); + } + } + + int highestChangeIndexWithHistory = 0; + // If restoring a wallet that never sent any funds with change, then set changeArray + // manually. If we didn't do this, it'd store an empty array. + for (final tuple in changeResults) { + if (tuple.addresses.isEmpty) { + await checkChangeAddressForTransactions(); + } else { + highestChangeIndexWithHistory = max( + tuple.index, + highestChangeIndexWithHistory, + ); + addressesToStore.addAll(tuple.addresses); + } + } + + // remove extra addresses to help minimize risk of creating a large gap + addressesToStore.removeWhere( + (e) => + e.subType == AddressSubType.change && + e.derivationIndex > highestChangeIndexWithHistory, + ); + addressesToStore.removeWhere( + (e) => + e.subType == AddressSubType.receiving && + e.derivationIndex > highestReceivingIndexWithHistory, ); - final publicKey = frost.scriptPubKeyForKeys(keys: keys); + await mainDB.updateOrPutAddresses(addressesToStore); - final address = Address( - walletId: walletId, - value: addressString, - publicKey: publicKey.toUint8ListFromHex, - derivationIndex: 0, - derivationPath: null, - subType: AddressSubType.receiving, - type: AddressType.frostMS, - ); - - await mainDB.updateOrPutAddresses([address]); + await _legacyInsecureScan(serializedKeys); }); GlobalEventBus.instance.fire( @@ -780,6 +958,80 @@ class BitcoinFrostWallet extends Wallet { } } + // for legacy support secure is set to false to see + // funds received on insecure addresses + Future _legacyInsecureScan(String serializedKeys) async { + const receiveChain = 0; + const changeChain = 1; + + final List addresses})>> receiveFutures = + [ + _checkGapsLinearly( + serializedKeys, + receiveChain, + secure: false, + ), + ]; + final List addresses})>> changeFutures = [ + // for legacy support secure is set to false to see + // funds received on insecure addresses + _checkGapsLinearly( + serializedKeys, + changeChain, + secure: false, + ), + ]; + + // io limitations may require running these linearly instead + final futuresResult = await Future.wait([ + Future.wait(receiveFutures), + Future.wait(changeFutures), + ]); + + final receiveResults = futuresResult[0]; + final changeResults = futuresResult[1]; + + final List
addressesToStore = []; + + int highestReceivingIndexWithHistory = 0; + for (final tuple in receiveResults) { + if (tuple.addresses.isNotEmpty) { + highestReceivingIndexWithHistory = max( + tuple.index, + highestReceivingIndexWithHistory, + ); + addressesToStore.addAll(tuple.addresses); + } + } + + int highestChangeIndexWithHistory = 0; + for (final tuple in changeResults) { + if (tuple.addresses.isNotEmpty) { + highestChangeIndexWithHistory = max( + tuple.index, + highestChangeIndexWithHistory, + ); + addressesToStore.addAll(tuple.addresses); + } + } + + // remove extra addresses to help minimize risk of creating a large gap + addressesToStore.removeWhere( + (e) => + e.subType == AddressSubType.change && + e.derivationIndex > highestChangeIndexWithHistory, + ); + addressesToStore.removeWhere( + (e) => + e.subType == AddressSubType.receiving && + e.derivationIndex > highestReceivingIndexWithHistory, + ); + + if (addressesToStore.isNotEmpty) { + await mainDB.updateOrPutAddresses(addressesToStore); + } + } + @override Future updateBalance() async { final utxos = await mainDB.getUTXOs(walletId).findAll(); @@ -817,6 +1069,7 @@ class BitcoinFrostWallet extends Wallet { if (utxo.isConfirmed( currentChainHeight, cryptoCurrency.minConfirms, + cryptoCurrency.minCoinbaseConfirms, )) { satoshiBalanceSpendable += utxoAmount; } else { @@ -868,23 +1121,31 @@ class BitcoinFrostWallet extends Wallet { @override Future updateUTXOs() async { - final address = await getCurrentReceivingAddress(); + final allAddresses = await _fetchAddressesForElectrumXScan(); try { - final scriptHash = cryptoCurrency.pubKeyToScriptHash( - pubKey: Uint8List.fromList(address!.publicKey), - ); + final fetchedUtxoList = >>[]; + for (int i = 0; i < allAddresses.length; i++) { + final scriptHash = cryptoCurrency.addressToScriptHash( + address: allAddresses[i].value, + ); - final utxos = await electrumXClient.getUTXOs(scripthash: scriptHash); + final utxos = await electrumXClient.getUTXOs(scripthash: scriptHash); + if (utxos.isNotEmpty) { + fetchedUtxoList.add(utxos); + } + } final List outputArray = []; - for (int i = 0; i < utxos.length; i++) { - final utxo = await _parseUTXO( - jsonUTXO: utxos[i], - ); + for (int i = 0; i < fetchedUtxoList.length; i++) { + for (int j = 0; j < fetchedUtxoList[i].length; j++) { + final utxo = await _parseUTXO( + jsonUTXO: fetchedUtxoList[i][j], + ); - outputArray.add(utxo); + outputArray.add(utxo); + } } return await mainDB.updateUTXOs(walletId, outputArray); @@ -1077,6 +1338,8 @@ class BitcoinFrostWallet extends Wallet { name: node.name, useSSL: node.useSSL, id: node.id, + torEnabled: node.torEnabled, + clearnetEnabled: node.clearnetEnabled, ); } @@ -1091,6 +1354,8 @@ class BitcoinFrostWallet extends Wallet { name: e.name, id: e.id, useSSL: e.useSSL, + torEnabled: e.torEnabled, + clearnetEnabled: e.clearnetEnabled, ), ) .toList(); @@ -1174,4 +1439,401 @@ class BitcoinFrostWallet extends Wallet { return utxo; } + + @override + Future checkChangeAddressForTransactions() async { + try { + final currentChange = await getCurrentChangeAddress(); + + final bool needsGenerate; + if (currentChange == null) { + // no addresses in db yet for some reason. + // Should not happen at this point... + + needsGenerate = true; + } else { + final txCount = await _fetchTxCount(address: currentChange); + needsGenerate = txCount > 0 || currentChange.derivationIndex < 0; + } + + if (needsGenerate) { + await generateNewChangeAddress(); + + // TODO: get rid of this? Could cause problems (long loading/infinite loop or something) + // keep checking until address with no tx history is set as current + await checkChangeAddressForTransactions(); + } + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from _checkChangeAddressForTransactions" + "($cryptoCurrency): $e\n$s", + level: LogLevel.Error, + ); + rethrow; + } + } + + @override + Future checkReceivingAddressForTransactions() async { + if (info.otherData[WalletInfoKeys.reuseAddress] == true) { + try { + throw Exception(); + } catch (_, s) { + Logging.instance.log( + "checkReceivingAddressForTransactions called but reuse address flag set: $s", + level: LogLevel.Error, + ); + } + } + + try { + final currentReceiving = await getCurrentReceivingAddress(); + + final bool needsGenerate; + if (currentReceiving == null) { + // no addresses in db yet for some reason. + // Should not happen at this point... + + needsGenerate = true; + } else { + final txCount = await _fetchTxCount(address: currentReceiving); + needsGenerate = txCount > 0 || currentReceiving.derivationIndex < 0; + } + + if (needsGenerate) { + await generateNewReceivingAddress(); + + // TODO: [prio=low] Make sure we scan all addresses but only show one. + if (info.otherData[WalletInfoKeys.reuseAddress] != true) { + // TODO: get rid of this? Could cause problems (long loading/infinite loop or something) + // keep checking until address with no tx history is set as current + await checkReceivingAddressForTransactions(); + } + } + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from _checkReceivingAddressForTransactions" + "($cryptoCurrency): $e\n$s", + level: LogLevel.Error, + ); + rethrow; + } + } + + @override + Future generateNewChangeAddress() async { + final current = await getCurrentChangeAddress(); + int index = current == null + ? kFrostSecureStartingIndex + : current.derivationIndex + 1; + const chain = 1; // change address + + final serializedKeys = (await getSerializedKeys())!; + + Address? address; + while (address == null) { + try { + address = await _generateAddress( + change: chain, + index: index, + serializedKeys: serializedKeys, + secure: true, + ); + } on FrostdartException catch (e) { + if (e.errorCode == 72) { + // rust doesn't like the addressDerivationData + index++; + continue; + } else { + rethrow; + } + } + } + + await mainDB.updateOrPutAddresses([address]); + } + + @override + Future generateNewReceivingAddress() async { + final current = await getCurrentReceivingAddress(); + int index = current == null + ? kFrostSecureStartingIndex + : current.derivationIndex + 1; + const chain = 0; // receiving address + + final serializedKeys = (await getSerializedKeys())!; + + Address? address; + while (address == null) { + try { + address = await _generateAddress( + change: chain, + index: index, + serializedKeys: serializedKeys, + secure: true, + ); + } on FrostdartException catch (e) { + if (e.errorCode == 72) { + // rust doesn't like the addressDerivationData + index++; + continue; + } else { + rethrow; + } + } + } + + await mainDB.updateOrPutAddresses([address]); + await info.updateReceivingAddress( + newAddress: address.value, + isar: mainDB.isar, + ); + } + + Future lookAhead() async { + Address? currentReceiving = await getCurrentReceivingAddress(); + if (currentReceiving == null) { + await generateNewReceivingAddress(); + currentReceiving = await getCurrentReceivingAddress(); + } + Address? currentChange = await getCurrentChangeAddress(); + if (currentChange == null) { + await generateNewChangeAddress(); + currentChange = await getCurrentChangeAddress(); + } + + final List
nextReceivingAddresses = []; + final List
nextChangeAddresses = []; + + int receiveIndex = currentReceiving!.derivationIndex; + int changeIndex = currentChange!.derivationIndex; + for (int i = 0; i < 10; i++) { + final receiveAddress = await _generateAddressSafe( + chain: 0, + startingIndex: receiveIndex + 1, + ); + receiveIndex = receiveAddress.derivationIndex; + nextReceivingAddresses.add(receiveAddress); + + final changeAddress = await _generateAddressSafe( + chain: 1, + startingIndex: changeIndex + 1, + ); + changeIndex = changeAddress.derivationIndex; + nextChangeAddresses.add(changeAddress); + } + + int activeReceiveIndex = currentReceiving.derivationIndex; + int activeChangeIndex = currentChange.derivationIndex; + for (final address in nextReceivingAddresses) { + final txCount = await _fetchTxCount(address: address); + if (txCount > 0) { + activeReceiveIndex = max(activeReceiveIndex, address.derivationIndex); + } + } + for (final address in nextChangeAddresses) { + final txCount = await _fetchTxCount(address: address); + if (txCount > 0) { + activeChangeIndex = max(activeChangeIndex, address.derivationIndex); + } + } + + nextReceivingAddresses + .removeWhere((e) => e.derivationIndex > activeReceiveIndex); + if (nextReceivingAddresses.isNotEmpty) { + await mainDB.updateOrPutAddresses(nextReceivingAddresses); + await info.updateReceivingAddress( + newAddress: nextReceivingAddresses.last.value, + isar: mainDB.isar, + ); + } + nextChangeAddresses + .removeWhere((e) => e.derivationIndex > activeChangeIndex); + if (nextChangeAddresses.isNotEmpty) { + await mainDB.updateOrPutAddresses(nextChangeAddresses); + } + } + + Future
_generateAddressSafe({ + required final int chain, + required int startingIndex, + }) async { + final serializedKeys = (await getSerializedKeys())!; + + Address? address; + while (address == null) { + try { + address = await _generateAddress( + change: chain, + index: startingIndex, + serializedKeys: serializedKeys, + secure: true, + ); + } on FrostdartException catch (e) { + if (e.errorCode == 72) { + // rust doesn't like the addressDerivationData + startingIndex++; + continue; + } else { + rethrow; + } + } + } + + return address; + } + + /// Can and will often throw unless [index], [change], and [account] are zero. + /// Caller MUST handle exception! + Future
_generateAddress({ + int account = 0, + required int change, + required int index, + required String serializedKeys, + required bool secure, + }) async { + final addressDerivationData = ( + account: account, + change: change == 1, + index: index, + ); + + final keys = frost.deserializeKeys(keys: serializedKeys); + + final addressString = frost.addressForKeys( + network: cryptoCurrency.network == CryptoCurrencyNetwork.main + ? Network.Mainnet + : Network.Testnet, + keys: keys, + addressDerivationData: addressDerivationData, + secure: secure, + ); + + return Address( + walletId: info.walletId, + value: addressString, + publicKey: cryptoCurrency.addressToPubkey(address: addressString), + derivationIndex: index, + derivationPath: DerivationPath()..value = "$account/$change/$index", + subType: change == 0 + ? AddressSubType.receiving + : change == 1 + ? AddressSubType.change + : AddressSubType.unknown, + type: AddressType.frostMS, + zSafeFrost: secure && index >= kFrostSecureStartingIndex, + ); + } + + Future<({List
addresses, int index})> _checkGapsLinearly( + String serializedKeys, + int chain, { + required bool secure, + }) async { + final List
addressArray = []; + int gapCounter = 0; + int index = secure ? kFrostSecureStartingIndex : 0; + for (; gapCounter < 20; index++) { + Logging.instance.log( + "Frost index: $index, \t GapCounter chain=$chain: $gapCounter", + level: LogLevel.Info, + ); + + Address? address; + while (address == null) { + try { + address = await _generateAddress( + change: chain, + index: index, + serializedKeys: serializedKeys, + secure: secure, + ); + } on FrostdartException catch (e) { + if (e.errorCode == 72) { + // rust doesn't like the addressDerivationData + index++; + continue; + } else { + rethrow; + } + } + } + + // get address tx count + final count = await _fetchTxCount( + address: address, + ); + + // check and add appropriate addresses + if (count > 0) { + // add address to array + addressArray.add(address); + // reset counter + gapCounter = 0; + // add info to derivations + } else { + // increase counter when no tx history found + gapCounter++; + } + } + + return (addresses: addressArray, index: index); + } + + Future _fetchTxCount({required Address address}) async { + final transactions = await electrumXClient.getHistory( + scripthash: cryptoCurrency.addressToScriptHash( + address: address.value, + ), + ); + return transactions.length; + } + + Future> _fetchAddressesForElectrumXScan() async { + final allAddresses = await mainDB + .getAddresses(walletId) + .filter() + .not() + .group( + (q) => q + .typeEqualTo(AddressType.nonWallet) + .or() + .subTypeEqualTo(AddressSubType.nonWallet), + ) + .findAll(); + return allAddresses; + } + + Future>> _fetchHistory( + Iterable allAddresses, + ) async { + try { + final List> allTxHashes = []; + for (int i = 0; i < allAddresses.length; i++) { + final addressString = allAddresses.elementAt(i); + final scriptHash = cryptoCurrency.addressToScriptHash( + address: addressString, + ); + + final response = await electrumXClient.getHistory( + scripthash: scriptHash, + ); + + for (int j = 0; j < response.length; j++) { + response[j]["address"] = addressString; + if (!allTxHashes.contains(response[j])) { + allTxHashes.add(response[j]); + } + } + } + + return allTxHashes; + } catch (e, s) { + Logging.instance.log( + "$runtimeType._fetchHistory: $e\n$s", + level: LogLevel.Error, + ); + rethrow; + } + } } diff --git a/lib/wallets/wallet/impl/cardano_wallet.dart b/lib/wallets/wallet/impl/cardano_wallet.dart new file mode 100644 index 000000000..2b9a8b9bb --- /dev/null +++ b/lib/wallets/wallet/impl/cardano_wallet.dart @@ -0,0 +1,650 @@ +import 'dart:io'; + +import 'package:blockchain_utils/bip/bip/bip44/base/bip44_base.dart'; +import 'package:blockchain_utils/bip/cardano/bip32/cardano_icarus_bip32.dart'; +import 'package:blockchain_utils/bip/cardano/cip1852/cip1852.dart'; +import 'package:blockchain_utils/bip/cardano/cip1852/conf/cip1852_coins.dart'; +import 'package:blockchain_utils/bip/cardano/mnemonic/cardano_icarus_seed_generator.dart'; +import 'package:blockchain_utils/bip/cardano/shelley/cardano_shelley.dart'; +import 'package:isar/isar.dart'; +import 'package:on_chain/ada/ada.dart'; +import 'package:socks5_proxy/socks.dart'; +import 'package:tuple/tuple.dart'; + +import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart'; +import '../../../models/balance.dart'; +import '../../../models/isar/models/blockchain_data/address.dart'; +import '../../../models/isar/models/blockchain_data/transaction.dart' as isar; +import '../../../models/paymint/fee_object_model.dart'; +import '../../../services/tor_service.dart'; +import '../../../utilities/amount/amount.dart'; +import '../../../utilities/logger.dart'; +import '../../../utilities/prefs.dart'; +import '../../../utilities/tor_plain_net_option_enum.dart'; +import '../../api/cardano/blockfrost_http_provider.dart'; +import '../../crypto_currency/crypto_currency.dart'; +import '../../models/tx_data.dart'; +import '../intermediate/bip39_wallet.dart'; + +class CardanoWallet extends Bip39Wallet { + CardanoWallet(CryptoCurrencyNetwork network) : super(Cardano(network)); + + // Source: https://cips.cardano.org/cip/CIP-1852 + static const String _addressDerivationPath = "m/1852'/1815'/0'/0/0"; + + BlockforestProvider? blockfrostProvider; + + @override + FilterOperation? get changeAddressFilterOperation => null; + + @override + FilterOperation? get receivingAddressFilterOperation => null; + + Future
_getAddress() async { + final mnemonic = await getMnemonic(); + final seed = CardanoIcarusSeedGenerator(mnemonic).generate(); + final cip1852 = Cip1852.fromSeed(seed, Cip1852Coins.cardanoIcarus); + final derivationAccount = cip1852.purpose.coin.account(0); + final shelley = CardanoShelley.fromCip1852Object(derivationAccount) + .change(Bip44Changes.chainExt) + .addressIndex(0); + final paymentPublicKey = shelley.bip44.publicKey.compressed; + final stakePublicKey = shelley.bip44Sk.publicKey.compressed; + final addressStr = ADABaseAddress.fromPublicKey( + basePubkeyBytes: paymentPublicKey, + stakePubkeyBytes: stakePublicKey, + ).address; + return Address( + walletId: walletId, + value: addressStr, + publicKey: paymentPublicKey, + derivationIndex: 0, + derivationPath: DerivationPath()..value = _addressDerivationPath, + type: AddressType.cardanoShelley, + subType: AddressSubType.receiving, + ); + } + + @override + Future checkSaveInitialReceivingAddress() async { + try { + final Address? address = await getCurrentReceivingAddress(); + + if (address == null) { + final address = await _getAddress(); + + await mainDB.updateOrPutAddresses([address]); + } + } catch (e, s) { + Logging.instance.log( + "$runtimeType checkSaveInitialReceivingAddress() failed: $e\n$s", + level: LogLevel.Error, + ); + } + } + + @override + Future pingCheck() async { + try { + await updateProvider(); + + final health = await blockfrostProvider!.request( + BlockfrostRequestBackendHealthStatus(), + ); + + return Future.value(health); + } catch (e, s) { + Logging.instance.log( + "Error ping checking in cardano_wallet.dart: $e\n$s", + level: LogLevel.Error, + ); + return Future.value(false); + } + } + + @override + Future estimateFeeFor(Amount amount, int feeRate) async { + await updateProvider(); + + if (info.cachedBalance.spendable.raw == BigInt.zero) { + return Amount( + rawValue: BigInt.zero, + fractionDigits: cryptoCurrency.fractionDigits, + ); + } + + final params = await blockfrostProvider!.request( + BlockfrostRequestLatestEpochProtocolParameters(), + ); + + final fee = params.calculateFee(284); + + return Amount( + rawValue: fee, + fractionDigits: cryptoCurrency.fractionDigits, + ); + } + + @override + Future get fees async { + try { + await updateProvider(); + + final params = await blockfrostProvider!.request( + BlockfrostRequestLatestEpochProtocolParameters(), + ); + + // 284 is the size of a basic transaction with one input and two outputs (change and recipient) + final fee = params.calculateFee(284).toInt(); + + return FeeObject( + numberOfBlocksFast: 2, + numberOfBlocksAverage: 2, + numberOfBlocksSlow: 2, + fast: fee, + medium: fee, + slow: fee, + ); + } catch (e, s) { + Logging.instance.log( + "Error getting fees in cardano_wallet.dart: $e\n$s", + level: LogLevel.Error, + ); + rethrow; + } + } + + @override + Future prepareSend({required TxData txData}) async { + try { + await updateProvider(); + + if (txData.amount!.raw < ADAHelper.toLovelaces("1")) { + throw Exception("By network rules, you can send minimum 1 ADA"); + } + + final utxos = await blockfrostProvider!.request( + BlockfrostRequestAddressUTXOsOfAGivenAsset( + address: ADAAddress.fromAddress( + (await getCurrentReceivingAddress())!.value, + ), + asset: "lovelace", + ), + ); + + var leftAmountForUtxos = txData.amount!.raw; + final listOfUtxosToBeUsed = []; + var totalBalance = BigInt.zero; + + for (final utxo in utxos) { + if (!(leftAmountForUtxos <= BigInt.parse("0"))) { + leftAmountForUtxos -= BigInt.parse(utxo.amount.first.quantity); + listOfUtxosToBeUsed.add(utxo); + } + totalBalance += BigInt.parse(utxo.amount.first.quantity); + } + + if (leftAmountForUtxos > BigInt.parse("0") || + totalBalance < txData.amount!.raw) { + throw Exception("Insufficient balance"); + } + + final bip32 = CardanoIcarusBip32.fromSeed( + CardanoIcarusSeedGenerator(await getMnemonic()).generate()); + final spend = bip32.derivePath("1852'/1815'/0'/0/0"); + final privateKey = AdaPrivateKey.fromBytes(spend.privateKey.raw); + + // Calculate fees with example tx + final exampleFee = ADAHelper.toLovelaces("0.10"); + final change = TransactionOutput( + address: ADABaseAddress((await getCurrentReceivingAddress())!.value), + amount: Value(coin: totalBalance - (txData.amount!.raw))); + final body = TransactionBody( + inputs: listOfUtxosToBeUsed + .map((e) => TransactionInput( + transactionId: TransactionHash.fromHex(e.txHash), + index: e.outputIndex)) + .toList(), + outputs: [ + change, + TransactionOutput( + address: ADABaseAddress(txData.recipients!.first.address), + amount: Value(coin: txData.amount!.raw - exampleFee)) + ], + fee: exampleFee, + ); + final exampleTx = ADATransaction( + body: body, + witnessSet: TransactionWitnessSet( + vKeys: [ + privateKey.createSignatureWitness(body.toHash().data), + ], + ), + ); + final params = await blockfrostProvider! + .request(BlockfrostRequestLatestEpochProtocolParameters()); + final fee = params.calculateFee(exampleTx.size); + + // Check if we are sending all balance, which means no change and only one output for recipient. + if (totalBalance == txData.amount!.raw) { + final List newRecipients = [ + ( + address: txData.recipients!.first.address, + amount: Amount( + rawValue: txData.amount!.raw - fee, + fractionDigits: cryptoCurrency.fractionDigits, + ), + isChange: txData.recipients!.first.isChange, + ), + ]; + return txData.copyWith( + fee: Amount( + rawValue: fee, + fractionDigits: cryptoCurrency.fractionDigits, + ), + recipients: newRecipients, + ); + } else { + if (txData.amount!.raw + fee > totalBalance) { + throw Exception("Insufficient balance for fee"); + } + + // Minimum change in Cardano is 1 ADA and we need to have enough balance for that + if (totalBalance - (txData.amount!.raw + fee) < + ADAHelper.toLovelaces("1")) { + throw Exception( + "Not enough balance for change. By network rules, please either send all balance or leave at least 1 ADA change."); + } + + return txData.copyWith( + fee: Amount( + rawValue: fee, + fractionDigits: cryptoCurrency.fractionDigits, + ), + ); + } + } catch (e, s) { + Logging.instance.log( + "$runtimeType Cardano prepareSend failed: $e\n$s", + level: LogLevel.Error, + ); + rethrow; + } + } + + @override + Future confirmSend({required TxData txData}) async { + try { + await updateProvider(); + + final utxos = await blockfrostProvider!.request( + BlockfrostRequestAddressUTXOsOfAGivenAsset( + address: ADAAddress.fromAddress( + (await getCurrentReceivingAddress())!.value, + ), + asset: "lovelace", + ), + ); + + var leftAmountForUtxos = txData.amount!.raw + txData.fee!.raw; + final listOfUtxosToBeUsed = []; + var totalBalance = BigInt.zero; + + for (final utxo in utxos) { + if (!(leftAmountForUtxos <= BigInt.parse("0"))) { + leftAmountForUtxos -= BigInt.parse(utxo.amount.first.quantity); + listOfUtxosToBeUsed.add(utxo); + } + totalBalance += BigInt.parse(utxo.amount.first.quantity); + } + + var totalUtxoAmount = BigInt.zero; + + for (final utxo in listOfUtxosToBeUsed) { + totalUtxoAmount += BigInt.parse(utxo.amount.first.quantity); + } + + final bip32 = CardanoIcarusBip32.fromSeed( + CardanoIcarusSeedGenerator(await getMnemonic()).generate()); + final spend = bip32.derivePath("1852'/1815'/0'/0/0"); + final privateKey = AdaPrivateKey.fromBytes(spend.privateKey.raw); + + final change = TransactionOutput( + address: ADABaseAddress((await getCurrentReceivingAddress())!.value), + amount: Value( + coin: totalUtxoAmount - (txData.amount!.raw + txData.fee!.raw))); + List outputs = []; + if (totalBalance == (txData.amount!.raw + txData.fee!.raw)) { + outputs = [ + TransactionOutput( + address: ADABaseAddress(txData.recipients!.first.address), + amount: Value(coin: txData.amount!.raw)) + ]; + } else { + outputs = [ + change, + TransactionOutput( + address: ADABaseAddress(txData.recipients!.first.address), + amount: Value(coin: txData.amount!.raw)) + ]; + } + final body = TransactionBody( + inputs: listOfUtxosToBeUsed + .map((e) => TransactionInput( + transactionId: TransactionHash.fromHex(e.txHash), + index: e.outputIndex)) + .toList(), + outputs: outputs, + fee: txData.fee!.raw, + ); + final tx = ADATransaction( + body: body, + witnessSet: TransactionWitnessSet( + vKeys: [ + privateKey.createSignatureWitness(body.toHash().data), + ], + ), + ); + + final sentTx = await blockfrostProvider!.request( + BlockfrostRequestSubmitTransaction( + transactionCborBytes: tx.serialize(), + ), + ); + return txData.copyWith( + txid: sentTx, + ); + } catch (e, s) { + Logging.instance.log( + "$runtimeType Cardano confirmSend failed: $e\n$s", + level: LogLevel.Error, + ); + rethrow; + } + } + + @override + Future recover({required bool isRescan}) async { + await refreshMutex.protect(() async { + final addressStruct = await _getAddress(); + + await mainDB.updateOrPutAddresses([addressStruct]); + + if (info.cachedReceivingAddress != addressStruct.value) { + await info.updateReceivingAddress( + newAddress: addressStruct.value, + isar: mainDB.isar, + ); + } + + await Future.wait([ + updateBalance(), + updateChainHeight(), + updateTransactions(), + ]); + }); + } + + @override + Future updateBalance() async { + try { + await updateProvider(); + + final addressUtxos = await blockfrostProvider!.request( + BlockfrostRequestAddressUTXOsOfAGivenAsset( + address: ADAAddress.fromAddress( + (await getCurrentReceivingAddress())!.value, + ), + asset: "lovelace", + ), + ); + + BigInt totalBalanceInLovelace = BigInt.parse("0"); + for (final utxo in addressUtxos) { + totalBalanceInLovelace += BigInt.parse(utxo.amount.first.quantity); + } + + final balance = Balance( + total: Amount( + rawValue: totalBalanceInLovelace, + fractionDigits: cryptoCurrency.fractionDigits, + ), + spendable: Amount( + rawValue: totalBalanceInLovelace, + fractionDigits: cryptoCurrency.fractionDigits, + ), + blockedTotal: Amount( + rawValue: BigInt.zero, + fractionDigits: cryptoCurrency.fractionDigits, + ), + pendingSpendable: Amount( + rawValue: BigInt.zero, + fractionDigits: cryptoCurrency.fractionDigits, + ), + ); + + await info.updateBalance(newBalance: balance, isar: mainDB.isar); + } catch (e, s) { + Logging.instance.log( + "Error getting balance in cardano_wallet.dart: $e\n$s", + level: LogLevel.Error, + ); + } + } + + @override + Future updateChainHeight() async { + try { + await updateProvider(); + + final latestBlock = await blockfrostProvider!.request( + BlockfrostRequestLatestBlock(), + ); + + await info.updateCachedChainHeight( + newHeight: latestBlock.height == null ? 0 : latestBlock.height!, + isar: mainDB.isar, + ); + } catch (e, s) { + Logging.instance.log( + "Error updating transactions in cardano_wallet.dart: $e\n$s", + level: LogLevel.Error, + ); + } + } + + @override + Future updateNode() async { + await refresh(); + } + + @override + Future updateTransactions() async { + try { + await updateProvider(); + + final currentAddr = (await getCurrentReceivingAddress())!.value; + + final txsList = await blockfrostProvider!.request( + BlockfrostRequestAddressTransactions( + ADAAddress.fromAddress( + currentAddr, + ), + ), + ); + + final parsedTxsList = + List>.empty(growable: true); + + for (final tx in txsList) { + final txInfo = await blockfrostProvider!.request( + BlockfrostRequestSpecificTransaction(tx.txHash), + ); + final utxoInfo = await blockfrostProvider!.request( + BlockfrostRequestTransactionUTXOs(tx.txHash), + ); + var txType = isar.TransactionType.unknown; + + for (final input in utxoInfo.inputs) { + if (input.address == currentAddr) { + txType = isar.TransactionType.outgoing; + } + } + + if (txType == isar.TransactionType.outgoing) { + var isSelfTx = true; + for (final output in utxoInfo.outputs) { + if (output.address != currentAddr) { + isSelfTx = false; + } + } + if (isSelfTx) { + txType = isar.TransactionType.sentToSelf; + } + } + + if (txType == isar.TransactionType.unknown) { + for (final output in utxoInfo.outputs) { + if (output.address == currentAddr) { + txType = isar.TransactionType.incoming; + } + } + } + + var receiverAddr = "Unknown?"; + var amount = 0; + + if (txType == isar.TransactionType.incoming) { + receiverAddr = currentAddr; + for (final output in utxoInfo.outputs) { + if (output.address == currentAddr && + output.amount.first.unit == "lovelace") { + amount += int.parse(output.amount.first.quantity); + } + } + } else if (txType == isar.TransactionType.outgoing) { + for (final output in utxoInfo.outputs) { + if (output.address != currentAddr && + output.amount.first.unit == "lovelace") { + receiverAddr = output.address; + amount += int.parse(output.amount.first.quantity); + } + } + } else if (txType == isar.TransactionType.sentToSelf) { + receiverAddr = currentAddr; + for (final output in utxoInfo.outputs) { + if (output.amount.first.unit == "lovelace") { + amount += int.parse(output.amount.first.quantity); + } + } + } + + final transaction = isar.Transaction( + walletId: walletId, + txid: txInfo.hash, + timestamp: tx.blockTime, + type: txType, + subType: isar.TransactionSubType.none, + amount: amount, + amountString: Amount( + rawValue: BigInt.from(amount), + fractionDigits: cryptoCurrency.fractionDigits, + ).toJsonString(), + fee: int.parse(txInfo.fees), + height: txInfo.blockHeight, + isCancelled: false, + isLelantus: false, + slateId: null, + otherData: null, + inputs: [], + outputs: [], + nonce: null, + numberOfMessages: 0, + ); + + final txAddress = Address( + walletId: walletId, + value: receiverAddr, + publicKey: List.empty(), + derivationIndex: 0, + derivationPath: DerivationPath()..value = _addressDerivationPath, + type: AddressType.cardanoShelley, + subType: txType == isar.TransactionType.outgoing + ? AddressSubType.unknown + : AddressSubType.receiving, + ); + + parsedTxsList.add(Tuple2(transaction, txAddress)); + } + + await mainDB.addNewTransactionData(parsedTxsList, walletId); + } on NodeTorMismatchConfigException { + rethrow; + } catch (e, s) { + Logging.instance.log( + "Error updating transactions in cardano_wallet.dart: $e\n$s", + level: LogLevel.Error, + ); + } + } + + @override + Future updateUTXOs() async { + // TODO: implement updateUTXOs + return false; + } + + Future updateProvider() async { + final currentNode = getCurrentNode(); + + final client = HttpClient(); + if (prefs.useTor) { + final proxyInfo = TorService.sharedInstance.getProxyInfo(); + final proxySettings = ProxySettings( + proxyInfo.host, + proxyInfo.port, + ); + SocksTCPClient.assignToHttpClient(client, [proxySettings]); + } + blockfrostProvider = CustomBlockForestProvider( + BlockfrostHttpProvider( + url: "${currentNode.host}:${currentNode.port}/", + client: client, + ), + prefs, + TorPlainNetworkOption.fromNodeData( + currentNode.torEnabled, + currentNode.clearnetEnabled, + ), + ); + } +} + +class CustomBlockForestProvider extends BlockforestProvider { + CustomBlockForestProvider(super.rpc, this.prefs, this.netOption); + + final Prefs prefs; + final TorPlainNetworkOption netOption; + + @override + Future request( + BlockforestRequestParam request, [ + Duration? timeout, + ]) async { + if (prefs.useTor) { + if (netOption == TorPlainNetworkOption.clear) { + throw NodeTorMismatchConfigException( + message: "TOR enabled but node set to clearnet only", + ); + } + } else { + if (netOption == TorPlainNetworkOption.tor) { + throw NodeTorMismatchConfigException( + message: "TOR off but node set to TOR only", + ); + } + } + + return super.request(request, timeout); + } +} diff --git a/lib/wallets/wallet/impl/epiccash_wallet.dart b/lib/wallets/wallet/impl/epiccash_wallet.dart index 9df6a936a..96d6b112e 100644 --- a/lib/wallets/wallet/impl/epiccash_wallet.dart +++ b/lib/wallets/wallet/impl/epiccash_wallet.dart @@ -11,6 +11,7 @@ import 'package:mutex/mutex.dart'; import 'package:stack_wallet_backup/generate_password.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; +import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart'; import '../../../models/balance.dart'; import '../../../models/epicbox_config_model.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; @@ -32,6 +33,7 @@ import '../../../utilities/flutter_secure_storage_interface.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/stack_file_system.dart'; import '../../../utilities/test_epic_box_connection.dart'; +import '../../../utilities/tor_plain_net_option_enum.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../models/tx_data.dart'; import '../intermediate/bip39_wallet.dart'; @@ -82,6 +84,7 @@ class EpiccashWallet extends Bip39Wallet { /// returns an empty String on success, error message on failure Future cancelPendingTransactionAndPost(String txSlateId) async { try { + _hackedCheckTorNodePrefs(); final String wallet = (await secureStorageInterface.read( key: '${walletId}_wallet', ))!; @@ -173,6 +176,7 @@ class EpiccashWallet extends Bip39Wallet { }) async { final wallet = await secureStorageInterface.read(key: '${walletId}_wallet'); try { + _hackedCheckTorNodePrefs(); final available = info.cachedBalance.spendable.raw.toInt(); final transactionFees = await epiccash.LibEpiccash.getTransactionFees( @@ -198,6 +202,7 @@ class EpiccashWallet extends Bip39Wallet { } Future _startSync() async { + _hackedCheckTorNodePrefs(); Logging.instance.log("request start sync", level: LogLevel.Info); final wallet = await secureStorageInterface.read(key: '${walletId}_wallet'); const int refreshFromNode = 1; @@ -222,6 +227,7 @@ class EpiccashWallet extends Bip39Wallet { double spendable, double total })> _allWalletBalances() async { + _hackedCheckTorNodePrefs(); final wallet = await secureStorageInterface.read(key: '${walletId}_wallet'); const refreshFromNode = 0; return await epiccash.LibEpiccash.getWalletBalances( @@ -232,6 +238,7 @@ class EpiccashWallet extends Bip39Wallet { } Future _testEpicboxServer(EpicBoxConfigModel epicboxConfig) async { + _hackedCheckTorNodePrefs(); final host = epicboxConfig.host; final port = epicboxConfig.port ?? 443; WebSocketChannel? channel; @@ -299,26 +306,19 @@ class EpiccashWallet extends Bip39Wallet { } } + /// Only index 0 is currently used in stack wallet. Future
_generateAndStoreReceivingAddressForIndex( int index, ) async { - Address? address = await getCurrentReceivingAddress(); - - if (address != null) { - final splitted = address.value.split('@'); - //Check if the address is the same as the current epicbox domain - //Since we're only using one epicbpox now this doesn't apply but will be - // useful in the future - final epicboxConfig = await getEpicBoxConfig(); - if (splitted[1] != epicboxConfig.host) { - //Update the address - address = await thisWalletAddress(index, epicboxConfig); - } - } else { - final epicboxConfig = await getEpicBoxConfig(); - address = await thisWalletAddress(index, epicboxConfig); + // Since only 0 is a valid index in stack wallet at this time, lets just + // throw is not zero + if (index != 0) { + throw Exception("Invalid/unexpected address index used"); } + final epicBoxConfig = await getEpicBoxConfig(); + final address = await thisWalletAddress(index, epicBoxConfig); + if (info.cachedReceivingAddress != address.value) { await info.updateReceivingAddress( newAddress: address.value, @@ -583,6 +583,7 @@ class EpiccashWallet extends Bip39Wallet { @override Future confirmSend({required TxData txData}) async { try { + _hackedCheckTorNodePrefs(); final wallet = await secureStorageInterface.read(key: '${walletId}_wallet'); final EpicBoxConfigModel epicboxConfig = await getEpicBoxConfig(); @@ -609,7 +610,7 @@ class EpiccashWallet extends Bip39Wallet { wallet: wallet!, selectionStrategyIsAll: 0, minimumConfirmations: cryptoCurrency.minConfirms, - message: txData.noteOnChain!, + message: txData.noteOnChain ?? "", amount: txData.recipients!.first.amount.raw.toInt(), address: txData.recipients!.first.address, ); @@ -645,6 +646,7 @@ class EpiccashWallet extends Bip39Wallet { @override Future prepareSend({required TxData txData}) async { try { + _hackedCheckTorNodePrefs(); if (txData.recipients?.length != 1) { throw Exception("Epic cash prepare send requires a single recipient!"); } @@ -686,6 +688,7 @@ class EpiccashWallet extends Bip39Wallet { @override Future recover({required bool isRescan}) async { try { + _hackedCheckTorNodePrefs(); await refreshMutex.protect(() async { if (isRescan) { // clear blockchain info @@ -789,6 +792,7 @@ class EpiccashWallet extends Bip39Wallet { cryptoCurrency, ), ); + _hackedCheckTorNodePrefs(); // if (info.epicData?.creationHeight == null) { // await info.updateExtraEpiccashWalletInfo(epicData: inf, isar: isar) @@ -887,6 +891,7 @@ class EpiccashWallet extends Bip39Wallet { @override Future updateBalance() async { try { + _hackedCheckTorNodePrefs(); final balances = await _allWalletBalances(); final balance = Balance( total: Amount.fromDecimal( @@ -922,6 +927,7 @@ class EpiccashWallet extends Bip39Wallet { @override Future updateTransactions() async { try { + _hackedCheckTorNodePrefs(); final wallet = await secureStorageInterface.read(key: '${walletId}_wallet'); const refreshFromNode = 1; @@ -948,8 +954,6 @@ class EpiccashWallet extends Bip39Wallet { final slatesToCommits = info.epicData?.slatesToCommits ?? {}; for (final tx in transactions) { - Logging.instance.log("tx: $tx", level: LogLevel.Info); - final isIncoming = tx.txType == epic_models.TransactionType.TxReceived || tx.txType == epic_models.TransactionType.TxReceivedCancelled; @@ -1092,7 +1096,11 @@ class EpiccashWallet extends Bip39Wallet { NodeFormData() ..host = node!.host ..useSSL = node.useSSL - ..port = node.port, + ..port = node.port + ..netOption = TorPlainNetworkOption.fromNodeData( + node.torEnabled, + node.clearnetEnabled, + ), ) != null; } catch (e, s) { @@ -1103,6 +1111,7 @@ class EpiccashWallet extends Bip39Wallet { @override Future updateChainHeight() async { + _hackedCheckTorNodePrefs(); final config = await _getRealConfig(); final latestHeight = await epiccash.LibEpiccash.getChainHeight(config: config); @@ -1114,6 +1123,7 @@ class EpiccashWallet extends Bip39Wallet { @override Future estimateFeeFor(Amount amount, int feeRate) async { + _hackedCheckTorNodePrefs(); // setting ifErrorEstimateFee doesn't do anything as its not used in the nativeFee function????? final int currentFee = await _nativeFee( amount.raw.toInt(), @@ -1152,6 +1162,28 @@ class EpiccashWallet extends Bip39Wallet { await super.exit(); Logging.instance.log("EpicCash_wallet exit finished", level: LogLevel.Info); } + + void _hackedCheckTorNodePrefs() { + final node = nodeService.getPrimaryNodeFor(currency: cryptoCurrency)!; + final netOption = TorPlainNetworkOption.fromNodeData( + node.torEnabled, + node.clearnetEnabled, + ); + + if (prefs.useTor) { + if (netOption == TorPlainNetworkOption.clear) { + throw NodeTorMismatchConfigException( + message: "TOR enabled but node set to clearnet only", + ); + } + } else { + if (netOption == TorPlainNetworkOption.tor) { + throw NodeTorMismatchConfigException( + message: "TOR off but node set to TOR only", + ); + } + } + } } Future deleteEpicWallet({ diff --git a/lib/wallets/wallet/impl/ethereum_wallet.dart b/lib/wallets/wallet/impl/ethereum_wallet.dart index 736f0fb05..beb377229 100644 --- a/lib/wallets/wallet/impl/ethereum_wallet.dart +++ b/lib/wallets/wallet/impl/ethereum_wallet.dart @@ -84,6 +84,66 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface { _credentials = web3.EthPrivateKey.fromHex(privateKey); } + TxData _prepareTempTx(TxData txData, String myAddress) { + // hack eth tx data into inputs and outputs + final List outputs = []; + final List inputs = []; + + final amount = txData.recipients!.first.amount; + final addressTo = txData.recipients!.first.address; + + final OutputV2 output = OutputV2.isarCantDoRequiredInDefaultConstructor( + scriptPubKeyHex: "00", + valueStringSats: amount.raw.toString(), + addresses: [ + addressTo, + ], + walletOwns: addressTo == myAddress, + ); + final InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor( + scriptSigHex: null, + scriptSigAsm: null, + sequence: null, + outpoint: null, + addresses: [myAddress], + valueStringSats: amount.raw.toString(), + witness: null, + innerRedeemScriptAsm: null, + coinbase: null, + walletOwns: true, + ); + + outputs.add(output); + inputs.add(input); + + final otherData = { + "nonce": txData.nonce, + "isCancelled": false, + "overrideFee": txData.fee!.toJsonString(), + }; + + final txn = TransactionV2( + walletId: walletId, + blockHash: null, + hash: txData.txHash!, + txid: txData.txid!, + timestamp: DateTime.now().millisecondsSinceEpoch ~/ 1000, + height: null, + inputs: List.unmodifiable(inputs), + outputs: List.unmodifiable(outputs), + version: -1, + type: addressTo == myAddress + ? TransactionType.sentToSelf + : TransactionType.outgoing, + subType: TransactionSubType.none, + otherData: jsonEncode(otherData), + ); + + return txData.copyWith( + tempTx: txn, + ); + } + // ==================== Overrides ============================================ @override @@ -447,7 +507,10 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface { } @override - Future confirmSend({required TxData txData}) async { + Future confirmSend({ + required TxData txData, + TxData Function(TxData txData, String myAddress)? prepareTempTx, + }) async { final client = getEthClient(); if (_credentials == null) { await _initCredentials(); @@ -459,10 +522,15 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface { chainId: txData.chainId!.toInt(), ); - return txData.copyWith( - txid: txid, - txHash: txid, + final data = (prepareTempTx ?? _prepareTempTx)( + txData.copyWith( + txid: txid, + txHash: txid, + ), + (await getCurrentReceivingAddress())!.value, ); + + return await updateSentCachedTxData(txData: data); } @override diff --git a/lib/wallets/wallet/impl/firo_wallet.dart b/lib/wallets/wallet/impl/firo_wallet.dart index f9fcb11b5..1ff9999fe 100644 --- a/lib/wallets/wallet/impl/firo_wallet.dart +++ b/lib/wallets/wallet/impl/firo_wallet.dart @@ -592,7 +592,11 @@ class FiroWallet extends Bip39HDWallet ); if (_unconfirmedTxids.contains(tx.txid)) { - if (tx.isConfirmed(await chainHeight, cryptoCurrency.minConfirms)) { + if (tx.isConfirmed( + await chainHeight, + cryptoCurrency.minConfirms, + cryptoCurrency.minCoinbaseConfirms, + )) { txns.add(tx); _unconfirmedTxids.removeWhere((e) => e == tx.txid); } else { @@ -659,6 +663,11 @@ class FiroWallet extends Bip39HDWallet @override Future recover({required bool isRescan}) async { + if (isViewOnly) { + await recoverViewOnly(isRescan: isRescan); + return; + } + // reset last checked values await info.updateOtherData( newEntries: { diff --git a/lib/wallets/wallet/impl/monero_wallet.dart b/lib/wallets/wallet/impl/monero_wallet.dart index 9c4848b8c..bcec63851 100644 --- a/lib/wallets/wallet/impl/monero_wallet.dart +++ b/lib/wallets/wallet/impl/monero_wallet.dart @@ -1,185 +1,51 @@ import 'dart:async'; -import 'dart:io'; -import 'dart:math'; -import 'package:cw_core/monero_transaction_priority.dart'; -import 'package:cw_core/node.dart'; -import 'package:cw_core/pending_transaction.dart'; -import 'package:cw_core/sync_status.dart'; -import 'package:cw_core/transaction_direction.dart'; -import 'package:cw_core/wallet_base.dart'; -import 'package:cw_core/wallet_credentials.dart'; -import 'package:cw_core/wallet_info.dart'; -import 'package:cw_core/wallet_type.dart'; -import 'package:cw_monero/api/exceptions/creation_transaction_exception.dart'; -import 'package:cw_monero/monero_wallet.dart'; -import 'package:cw_monero/pending_monero_transaction.dart'; -import 'package:decimal/decimal.dart'; -import 'package:flutter_libmonero/core/wallet_creation_service.dart'; -import 'package:flutter_libmonero/monero/monero.dart' as xmr_dart; -import 'package:flutter_libmonero/view_model/send/output.dart' as monero_output; -import 'package:isar/isar.dart'; -import 'package:mutex/mutex.dart'; -import 'package:tuple/tuple.dart'; +import 'package:compat/compat.dart' as lib_monero_compat; +import 'package:cs_monero/cs_monero.dart' as lib_monero; -import '../../../db/hive/db.dart'; -import '../../../models/isar/models/blockchain_data/address.dart'; -import '../../../models/isar/models/blockchain_data/transaction.dart'; -import '../../../models/keys/cw_key_data.dart'; -import '../../../services/event_bus/events/global/tor_connection_status_changed_event.dart'; -import '../../../services/event_bus/events/global/tor_status_changed_event.dart'; -import '../../../services/event_bus/global_event_bus.dart'; -import '../../../services/tor_service.dart'; import '../../../utilities/amount/amount.dart'; -import '../../../utilities/enums/fee_rate_type_enum.dart'; -import '../../../utilities/logger.dart'; import '../../crypto_currency/crypto_currency.dart'; -import '../../models/tx_data.dart'; -import '../intermediate/cryptonote_wallet.dart'; -import '../wallet.dart'; -import '../wallet_mixin_interfaces/cw_based_interface.dart'; +import '../intermediate/lib_monero_wallet.dart'; -class MoneroWallet extends CryptonoteWallet with CwBasedInterface { - MoneroWallet(CryptoCurrencyNetwork network) : super(Monero(network)) { - final bus = GlobalEventBus.instance; - - // Listen for tor status changes. - _torStatusListener = bus.on().listen( - (event) async { - switch (event.newStatus) { - case TorConnectionStatus.connecting: - if (!_torConnectingLock.isLocked) { - await _torConnectingLock.acquire(); - } - _requireMutex = true; - break; - - case TorConnectionStatus.connected: - case TorConnectionStatus.disconnected: - if (_torConnectingLock.isLocked) { - _torConnectingLock.release(); - } - _requireMutex = false; - break; - } - }, - ); - - // Listen for tor preference changes. - _torPreferenceListener = bus.on().listen( - (event) async { - await updateNode(); - }, - ); - } - - @override - Address addressFor({required int index, int account = 0}) { - final String address = (CwBasedInterface.cwWalletBase as MoneroWalletBase) - .getTransactionAddress(account, index); - - final newReceivingAddress = Address( - walletId: walletId, - derivationIndex: index, - derivationPath: null, - value: address, - publicKey: [], - type: AddressType.cryptonote, - subType: AddressSubType.receiving, - ); - - return newReceivingAddress; - } - - @override - Future exitCwWallet() async { - (CwBasedInterface.cwWalletBase as MoneroWalletBase?)?.onNewBlock = null; - (CwBasedInterface.cwWalletBase as MoneroWalletBase?)?.onNewTransaction = - null; - (CwBasedInterface.cwWalletBase as MoneroWalletBase?)?.syncStatusChanged = - null; - await (CwBasedInterface.cwWalletBase as MoneroWalletBase?) - ?.save(prioritySave: true); - } - - @override - Future open() async { - // await any previous exit - await CwBasedInterface.exitMutex.protect(() async {}); - - String? password; - try { - password = await cwKeysStorage.getWalletPassword(walletName: walletId); - } catch (e, s) { - throw Exception("Password not found $e, $s"); - } - - CwBasedInterface.cwWalletBase?.close(); - CwBasedInterface.cwWalletBase = (await CwBasedInterface.cwWalletService! - .openWallet(walletId, password)) as MoneroWalletBase; - - (CwBasedInterface.cwWalletBase as MoneroWalletBase?)?.onNewBlock = - onNewBlock; - (CwBasedInterface.cwWalletBase as MoneroWalletBase?)?.onNewTransaction = - onNewTransaction; - (CwBasedInterface.cwWalletBase as MoneroWalletBase?)?.syncStatusChanged = - syncStatusChanged; - - await updateNode(); - - Address? currentAddress = await getCurrentReceivingAddress(); - if (currentAddress == null) { - currentAddress = addressFor(index: 0); - await mainDB.updateOrPutAddresses([currentAddress]); - } - if (info.cachedReceivingAddress != currentAddress.value) { - await info.updateReceivingAddress( - newAddress: currentAddress.value, - isar: mainDB.isar, - ); - } - - await CwBasedInterface.cwWalletBase?.startSync(); - unawaited(refresh()); - autoSaveTimer?.cancel(); - autoSaveTimer = Timer.periodic( - const Duration(seconds: 193), - (_) async => await CwBasedInterface.cwWalletBase?.save(), - ); - } +class MoneroWallet extends LibMoneroWallet { + MoneroWallet(CryptoCurrencyNetwork network) + : super( + Monero(network), + lib_monero_compat.WalletType.monero, + ); @override Future estimateFeeFor(Amount amount, int feeRate) async { - if (CwBasedInterface.cwWalletBase == null || - CwBasedInterface.cwWalletBase?.syncStatus is! SyncedSyncStatus) { + if (libMoneroWallet == null || + syncStatus is! lib_monero_compat.SyncedSyncStatus) { return Amount.zeroWith( fractionDigits: cryptoCurrency.fractionDigits, ); } - MoneroTransactionPriority priority; + lib_monero.TransactionPriority priority; switch (feeRate) { case 1: - priority = MoneroTransactionPriority.regular; + priority = lib_monero.TransactionPriority.low; break; case 2: - priority = MoneroTransactionPriority.medium; + priority = lib_monero.TransactionPriority.medium; break; case 3: - priority = MoneroTransactionPriority.fast; + priority = lib_monero.TransactionPriority.high; break; case 4: - priority = MoneroTransactionPriority.fastest; + priority = lib_monero.TransactionPriority.last; break; case 0: default: - priority = MoneroTransactionPriority.slow; + priority = lib_monero.TransactionPriority.normal; break; } int approximateFee = 0; await estimateFeeMutex.protect(() async { - approximateFee = CwBasedInterface.cwWalletBase!.calculateEstimatedFee( + approximateFee = await libMoneroWallet!.estimateFee( priority, amount.raw.toInt(), ); @@ -192,571 +58,65 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { } @override - Future pingCheck() async { - return await (CwBasedInterface.cwWalletBase as MoneroWalletBase?) - ?.isConnected() ?? - false; - } + bool walletExists(String path) => lib_monero.MoneroWallet.isWalletExist(path); @override - Future updateNode() async { - final node = getCurrentNode(); - - final host = Uri.parse(node.host).host; - ({InternetAddress host, int port})? proxy; - if (prefs.useTor) { - proxy = TorService.sharedInstance.getProxyInfo(); - } - if (_requireMutex) { - await _torConnectingLock.protect(() async { - await CwBasedInterface.cwWalletBase?.connectToNode( - node: Node( - uri: "$host:${node.port}", - type: WalletType.monero, - trusted: node.trusted ?? false, - useSSL: node.useSSL, - ), - socksProxyAddress: - proxy == null ? null : "${proxy.host.address}:${proxy.port}", - ); - }); - } else { - await CwBasedInterface.cwWalletBase?.connectToNode( - node: Node( - uri: "$host:${node.port}", - type: WalletType.monero, - trusted: node.trusted ?? false, - useSSL: node.useSSL, - ), - socksProxyAddress: - proxy == null ? null : "${proxy.host.address}:${proxy.port}", - ); - } - - return; - } - - @override - Future getKeys() async { - final base = (CwBasedInterface.cwWalletBase as MoneroWalletBase?); - - if (base == null || - base.walletInfo.name != walletId || - CwBasedInterface.exitMutex.isLocked) { - return null; - } - - return CWKeyData( - walletId: walletId, - publicViewKey: base.keys.publicViewKey, - privateViewKey: base.keys.privateViewKey, - publicSpendKey: base.keys.publicSpendKey, - privateSpendKey: base.keys.privateSpendKey, + void loadWallet({ + required String path, + required String password, + }) { + libMoneroWallet = lib_monero.MoneroWallet.loadWallet( + path: path, + password: password, ); } @override - Future updateTransactions() async { - final base = (CwBasedInterface.cwWalletBase as MoneroWalletBase?); - - if (base == null || - base.walletInfo.name != walletId || - CwBasedInterface.exitMutex.isLocked) { - return; - } - await base.updateTransactions(); - final transactions = base.transactionHistory?.transactions; - - // final cachedTransactions = - // DB.instance.get(boxName: walletId, key: 'latest_tx_model') - // as TransactionData?; - // int latestTxnBlockHeight = - // DB.instance.get(boxName: walletId, key: "storedTxnDataHeight") - // as int? ?? - // 0; - // - // final txidsList = DB.instance - // .get(boxName: walletId, key: "cachedTxids") as List? ?? - // []; - // - // final Set cachedTxids = Set.from(txidsList); - - // TODO: filter to skip cached + confirmed txn processing in next step - // final unconfirmedCachedTransactions = - // cachedTransactions?.getAllTransactions() ?? {}; - // unconfirmedCachedTransactions - // .removeWhere((key, value) => value.confirmedStatus); - // - // if (cachedTransactions != null) { - // for (final tx in allTxHashes.toList(growable: false)) { - // final txHeight = tx["height"] as int; - // if (txHeight > 0 && - // txHeight < latestTxnBlockHeight - MINIMUM_CONFIRMATIONS) { - // if (unconfirmedCachedTransactions[tx["tx_hash"] as String] == null) { - // allTxHashes.remove(tx); - // } - // } - // } - // } - - final List> txnsData = []; - - if (transactions != null) { - for (final tx in transactions.entries) { - Address? address; - TransactionType type; - if (tx.value.direction == TransactionDirection.incoming) { - final addressInfo = tx.value.additionalInfo; - - final addressString = - (CwBasedInterface.cwWalletBase as MoneroWalletBase?) - ?.getTransactionAddress( - addressInfo!['accountIndex'] as int, - addressInfo['addressIndex'] as int, - ); - - if (addressString != null) { - address = await mainDB - .getAddresses(walletId) - .filter() - .valueEqualTo(addressString) - .findFirst(); - } - - type = TransactionType.incoming; - } else { - // txn.address = ""; - type = TransactionType.outgoing; - } - - final txn = Transaction( - walletId: walletId, - txid: tx.value.id, - timestamp: (tx.value.date.millisecondsSinceEpoch ~/ 1000), - type: type, - subType: TransactionSubType.none, - amount: tx.value.amount ?? 0, - amountString: Amount( - rawValue: BigInt.from(tx.value.amount ?? 0), - fractionDigits: cryptoCurrency.fractionDigits, - ).toJsonString(), - fee: tx.value.fee ?? 0, - height: tx.value.height, - isCancelled: false, - isLelantus: false, - slateId: null, - otherData: null, - nonce: null, - inputs: [], - outputs: [], - numberOfMessages: null, - ); - - txnsData.add(Tuple2(txn, address)); - } - } - - await mainDB.isar.writeTxn(() async { - await mainDB.isar.transactions - .where() - .walletIdEqualTo(walletId) - .deleteAll(); - for (final data in txnsData) { - final tx = data.item1; - - // save transaction - await mainDB.isar.transactions.put(tx); - - if (data.item2 != null) { - final address = await mainDB.getAddress(walletId, data.item2!.value); - - // check if address exists in db and add if it does not - if (address == null) { - await mainDB.isar.addresses.put(data.item2!); - } - - // link and save address - tx.address.value = address ?? data.item2!; - await tx.address.save(); - } - } - }); - } - - @override - Future init({bool? isRestore}) async { - await CwBasedInterface.exitMutex.protect(() async {}); - - CwBasedInterface.cwWalletService = xmr_dart.monero - .createMoneroWalletService(DB.instance.moneroWalletInfoBox); - - if (!(await CwBasedInterface.cwWalletService!.isWalletExit(walletId)) && - isRestore != true) { - WalletInfo walletInfo; - WalletCredentials credentials; - try { - final dirPath = - await pathForWalletDir(name: walletId, type: WalletType.monero); - final path = - await pathForWallet(name: walletId, type: WalletType.monero); - credentials = xmr_dart.monero.createMoneroNewWalletCredentials( - name: walletId, - language: "English", - ); - - walletInfo = WalletInfo.external( - id: WalletBase.idFor(walletId, WalletType.monero), - name: walletId, - type: WalletType.monero, - isRecovery: false, - restoreHeight: credentials.height ?? 0, - date: DateTime.now(), - path: path, - dirPath: dirPath, - address: '', - ); - credentials.walletInfo = walletInfo; - - final _walletCreationService = WalletCreationService( - secureStorage: secureStorageInterface, - walletService: CwBasedInterface.cwWalletService, - keyService: cwKeysStorage, - ); - _walletCreationService.type = WalletType.monero; - // To restore from a seed - final wallet = await _walletCreationService.create(credentials); - - // subtract a couple days to ensure we have a buffer for SWB - final bufferedCreateHeight = xmr_dart.monero.getHeigthByDate( - date: DateTime.now().subtract(const Duration(days: 2)), - ); - - await info.updateRestoreHeight( - newRestoreHeight: bufferedCreateHeight, - isar: mainDB.isar, - ); - - // special case for xmr/wow. Normally mnemonic + passphrase is saved - // before wallet.init() is called - await secureStorageInterface.write( - key: Wallet.mnemonicKey(walletId: walletId), - value: wallet.seed.trim(), - ); - await secureStorageInterface.write( - key: Wallet.mnemonicPassphraseKey(walletId: walletId), - value: "", - ); - - walletInfo.restoreHeight = bufferedCreateHeight; - - walletInfo.address = wallet.walletAddresses.address; - await DB.instance - .add(boxName: WalletInfo.boxName, value: walletInfo); - - wallet.close(); - } catch (e, s) { - Logging.instance.log("$e\n$s", level: LogLevel.Fatal); - CwBasedInterface.cwWalletBase?.close(); - } - await updateNode(); - } - - return super.init(); - } - - @override - Future recover({required bool isRescan}) async { - await CwBasedInterface.exitMutex.protect(() async {}); - - if (isRescan) { - await refreshMutex.protect(() async { - // clear blockchain info - await mainDB.deleteWalletBlockchainData(walletId); - - final restoreHeight = - CwBasedInterface.cwWalletBase?.walletInfo.restoreHeight; - highestPercentCached = 0; - await CwBasedInterface.cwWalletBase?.rescan(height: restoreHeight ?? 0); - }); - unawaited(refresh()); - return; - } - - await refreshMutex.protect(() async { - final mnemonic = await getMnemonic(); - final seedLength = mnemonic.trim().split(" ").length; - - if (seedLength != 25 && seedLength != 16) { - throw Exception("Invalid monero mnemonic length found: $seedLength"); - } - - try { - final height = max(info.restoreHeight, 0); - - await info.updateRestoreHeight( - newRestoreHeight: height, - isar: mainDB.isar, - ); - - CwBasedInterface.cwWalletService = xmr_dart.monero - .createMoneroWalletService(DB.instance.moneroWalletInfoBox); - WalletInfo walletInfo; - WalletCredentials credentials; - final String name = walletId; - final dirPath = - await pathForWalletDir(name: name, type: WalletType.monero); - final path = await pathForWallet(name: name, type: WalletType.monero); - credentials = - xmr_dart.monero.createMoneroRestoreWalletFromSeedCredentials( - name: name, - height: height, - mnemonic: mnemonic.trim(), - ); - try { - walletInfo = WalletInfo.external( - id: WalletBase.idFor(name, WalletType.monero), - name: name, - type: WalletType.monero, - isRecovery: false, - restoreHeight: credentials.height ?? 0, - date: DateTime.now(), - path: path, - dirPath: dirPath, - address: '', - ); - credentials.walletInfo = walletInfo; - - final cwWalletCreationService = WalletCreationService( - secureStorage: secureStorageInterface, - walletService: CwBasedInterface.cwWalletService, - keyService: cwKeysStorage, - ); - cwWalletCreationService.type = WalletType.monero; - // To restore from a seed - final wallet = - await cwWalletCreationService.restoreFromSeed(credentials); - walletInfo.address = wallet.walletAddresses.address; - await DB.instance - .add(boxName: WalletInfo.boxName, value: walletInfo); - if (walletInfo.address != null) { - final newReceivingAddress = await getCurrentReceivingAddress() ?? - Address( - walletId: walletId, - derivationIndex: 0, - derivationPath: null, - value: walletInfo.address!, - publicKey: [], - type: AddressType.cryptonote, - subType: AddressSubType.receiving, - ); - - await mainDB.updateOrPutAddresses([newReceivingAddress]); - await info.updateReceivingAddress( - newAddress: newReceivingAddress.value, - isar: mainDB.isar, - ); - } - CwBasedInterface.cwWalletBase?.close(); - CwBasedInterface.cwWalletBase = wallet as MoneroWalletBase; - } catch (e, s) { - Logging.instance.log("$e\n$s", level: LogLevel.Fatal); - } - await updateNode(); - - await CwBasedInterface.cwWalletBase?.rescan(height: credentials.height); - CwBasedInterface.cwWalletBase?.close(); - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from recoverFromMnemonic(): $e\n$s", - level: LogLevel.Error, - ); - rethrow; - } - }); - } - - @override - Future prepareSend({required TxData txData}) async { - try { - final feeRate = txData.feeRateType; - if (feeRate is FeeRateType) { - MoneroTransactionPriority feePriority; - switch (feeRate) { - case FeeRateType.fast: - feePriority = MoneroTransactionPriority.fast; - break; - case FeeRateType.average: - feePriority = MoneroTransactionPriority.regular; - break; - case FeeRateType.slow: - feePriority = MoneroTransactionPriority.slow; - break; - default: - throw ArgumentError("Invalid use of custom fee"); - } - - Future? awaitPendingTransaction; - try { - // check for send all - bool isSendAll = false; - final balance = await availableBalance; - if (txData.amount! == balance && - txData.recipients!.first.amount == balance) { - isSendAll = true; - } - - final List outputs = []; - for (final recipient in txData.recipients!) { - final output = monero_output.Output(CwBasedInterface.cwWalletBase!); - output.address = recipient.address; - output.sendAll = isSendAll; - final String amountToSend = recipient.amount.decimal.toString(); - output.setCryptoAmount(amountToSend); - outputs.add(output); - } - - final tmp = - xmr_dart.monero.createMoneroTransactionCreationCredentials( - outputs: outputs, - priority: feePriority, - ); - - await prepareSendMutex.protect(() async { - awaitPendingTransaction = - CwBasedInterface.cwWalletBase!.createTransaction(tmp); - }); - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from prepareSend(): $e\n$s", - level: LogLevel.Warning, - ); - } - - final PendingMoneroTransaction pendingMoneroTransaction = - await (awaitPendingTransaction!) as PendingMoneroTransaction; - final realFee = Amount.fromDecimal( - Decimal.parse(pendingMoneroTransaction.feeFormatted), - fractionDigits: cryptoCurrency.fractionDigits, - ); - - return txData.copyWith( - fee: realFee, - pendingMoneroTransaction: pendingMoneroTransaction, - ); - } else { - throw ArgumentError("Invalid fee rate argument provided!"); - } - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from prepare send(): $e\n$s", - level: LogLevel.Info, + Future getCreatedWallet({ + required String path, + required String password, + }) async => + await lib_monero.MoneroWallet.create( + path: path, + password: password, + seedType: lib_monero.MoneroSeedType + .sixteen, // TODO: check we want to actually use 16 here ); - if (e.toString().contains("Incorrect unlocked balance")) { - throw Exception("Insufficient balance!"); - } else if (e is CreationTransactionException) { - throw Exception("Insufficient funds to pay for transaction fee!"); - } else { - throw Exception("Transaction failed with error code $e"); - } - } - } - @override - Future confirmSend({required TxData txData}) async { - try { - try { - await txData.pendingMoneroTransaction!.commit(); - Logging.instance.log( - "transaction ${txData.pendingMoneroTransaction!.id} has been sent", - level: LogLevel.Info, - ); - return txData.copyWith(txid: txData.pendingMoneroTransaction!.id); - } catch (e, s) { - Logging.instance.log( - "${info.name} monero confirmSend: $e\n$s", - level: LogLevel.Error, - ); - rethrow; - } - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from confirmSend(): $e\n$s", - level: LogLevel.Info, + Future getRestoredWallet({ + required String path, + required String password, + required String mnemonic, + int height = 0, + }) async => + await lib_monero.MoneroWallet.restoreWalletFromSeed( + path: path, + password: password, + seed: mnemonic, + restoreHeight: height, ); - rethrow; - } - } @override - Future get availableBalance async { - try { - if (CwBasedInterface.exitMutex.isLocked) { - throw Exception("Exit in progress"); - } - int runningBalance = 0; - for (final entry in (CwBasedInterface.cwWalletBase as MoneroWalletBase?)! - .balance! - .entries) { - runningBalance += entry.value.unlockedBalance; - } - return Amount( - rawValue: BigInt.from(runningBalance), - fractionDigits: cryptoCurrency.fractionDigits, + Future getRestoredFromViewKeyWallet({ + required String path, + required String password, + required String address, + required String privateViewKey, + int height = 0, + }) async => + lib_monero.MoneroWallet.createViewOnlyWallet( + path: path, + password: password, + address: address, + viewKey: privateViewKey, + restoreHeight: height, ); - } catch (_) { - return info.cachedBalance.spendable; - } - } @override - Future get totalBalance async { - try { - if (CwBasedInterface.exitMutex.isLocked) { - throw Exception("Exit in progress"); - } - final balanceEntries = - (CwBasedInterface.cwWalletBase as MoneroWalletBase?) - ?.balance - ?.entries; - if (balanceEntries != null) { - int bal = 0; - for (final element in balanceEntries) { - bal = bal + element.value.fullBalance; - } - return Amount( - rawValue: BigInt.from(bal), - fractionDigits: cryptoCurrency.fractionDigits, - ); - } else { - final transactions = - (CwBasedInterface.cwWalletBase as MoneroWalletBase?)! - .transactionHistory! - .transactions; - int transactionBalance = 0; - for (final tx in transactions!.entries) { - if (tx.value.direction == TransactionDirection.incoming) { - transactionBalance += tx.value.amount!; - } else { - transactionBalance += -tx.value.amount! - tx.value.fee!; - } - } - - return Amount( - rawValue: BigInt.from(transactionBalance), - fractionDigits: cryptoCurrency.fractionDigits, - ); - } - } catch (_) { - return info.cachedBalance.total; + void invalidSeedLengthCheck(int length) { + if (length != 25 && length != 16) { + throw Exception("Invalid monero mnemonic length found: $length"); } } - - // ============== Private ==================================================== - - StreamSubscription? _torStatusListener; - StreamSubscription? _torPreferenceListener; - - final Mutex _torConnectingLock = Mutex(); - bool _requireMutex = false; } diff --git a/lib/wallets/wallet/impl/solana_wallet.dart b/lib/wallets/wallet/impl/solana_wallet.dart index 5cbfda157..8c7f7e43b 100644 --- a/lib/wallets/wallet/impl/solana_wallet.dart +++ b/lib/wallets/wallet/impl/solana_wallet.dart @@ -9,6 +9,7 @@ import 'package:solana/dto.dart'; import 'package:solana/solana.dart'; import 'package:tuple/tuple.dart'; +import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart'; import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/transaction.dart' as isar; import '../../../models/isar/models/isar_models.dart'; @@ -19,6 +20,7 @@ import '../../../services/tor_service.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/prefs.dart'; +import '../../../utilities/tor_plain_net_option_enum.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../models/tx_data.dart'; import '../intermediate/bip39_wallet.dart'; @@ -60,6 +62,7 @@ class SolanaWallet extends Bip39Wallet { } Future _getEstimatedNetworkFee(Amount transferAmount) async { + _checkClient(); final latestBlockhash = await _rpcClient?.getLatestBlockhash(); final pubKey = (await _getKeyPair()).publicKey; @@ -129,11 +132,14 @@ class SolanaWallet extends Bip39Wallet { // Rent exemption of Solana final accInfo = await _rpcClient?.getAccountInfo(address!.value); + if (accInfo!.value == null) { + throw Exception("Account does not appear to exist"); + } + final int minimumRent = - await _rpcClient?.getMinimumBalanceForRentExemption( - accInfo!.value!.data.toString().length, - ) ?? - 0; // TODO revisit null condition. + await _rpcClient!.getMinimumBalanceForRentExemption( + accInfo.value!.data.toString().length, + ); if (minimumRent > ((await _getCurrentBalanceInLamports()) - txData.amount!.raw.toInt() - @@ -254,7 +260,7 @@ class SolanaWallet extends Bip39Wallet { return health != null; } catch (e, s) { Logging.instance.log( - "$runtimeType Solana pingCheck failed \"health=$health\": $e\n$s", + "$runtimeType Solana pingCheck failed \"health response=$health\": $e\n$s", level: LogLevel.Error, ); return Future.value(false); @@ -289,21 +295,22 @@ class SolanaWallet extends Bip39Wallet { @override Future updateBalance() async { + _checkClient(); try { final address = await getCurrentReceivingAddress(); - _checkClient(); final balance = await _rpcClient?.getBalance(address!.value); // Rent exemption of Solana final accInfo = await _rpcClient?.getAccountInfo(address!.value); - // TODO [prio=low]: handle null account info. + if (accInfo!.value == null) { + throw Exception("Account does not appear to exist"); + } + final int minimumRent = - await _rpcClient?.getMinimumBalanceForRentExemption( - accInfo!.value!.data.toString().length, - ) ?? - 0; - // TODO [prio=low]: revisit null condition. + await _rpcClient!.getMinimumBalanceForRentExemption( + accInfo.value!.data.toString().length, + ); final spendableBalance = balance!.value - minimumRent; final newBalance = Balance( @@ -357,16 +364,19 @@ class SolanaWallet extends Bip39Wallet { @override Future updateNode() async { - _solNode = getCurrentNode(); + _solNode = NodeService(secureStorageInterface: secureStorageInterface) + .getPrimaryNodeFor(currency: info.coin) ?? + info.coin.defaultNode; await refresh(); } @override NodeModel getCurrentNode() { - return _solNode ?? - NodeService(secureStorageInterface: secureStorageInterface) + _solNode ??= NodeService(secureStorageInterface: secureStorageInterface) .getPrimaryNodeFor(currency: info.coin) ?? info.coin.defaultNode; + + return _solNode!; } @override @@ -445,6 +455,8 @@ class SolanaWallet extends Bip39Wallet { txsList.add(Tuple2(transaction, txAddress)); } await mainDB.addNewTransactionData(txsList, walletId); + } on NodeTorMismatchConfigException { + rethrow; } catch (e, s) { Logging.instance.log( "Error occurred in solana_wallet.dart while getting" @@ -464,6 +476,28 @@ class SolanaWallet extends Bip39Wallet { /// void _checkClient() { final node = getCurrentNode(); + + final netOption = TorPlainNetworkOption.fromNodeData( + node.torEnabled, + node.clearnetEnabled, + ); + + if (prefs.useTor) { + if (netOption == TorPlainNetworkOption.clear) { + _rpcClient = null; + throw NodeTorMismatchConfigException( + message: "TOR enabled but node set to clearnet only", + ); + } + } else { + if (netOption == TorPlainNetworkOption.tor) { + _rpcClient = null; + throw NodeTorMismatchConfigException( + message: "TOR off but node set to TOR only", + ); + } + } + _rpcClient = createRpcClient( node.host, node.port, diff --git a/lib/wallets/wallet/impl/stellar_wallet.dart b/lib/wallets/wallet/impl/stellar_wallet.dart index 09d5d48fa..8932a30fb 100644 --- a/lib/wallets/wallet/impl/stellar_wallet.dart +++ b/lib/wallets/wallet/impl/stellar_wallet.dart @@ -7,6 +7,7 @@ import 'package:mutex/mutex.dart'; import 'package:socks5_proxy/socks.dart'; import 'package:stellar_flutter_sdk/stellar_flutter_sdk.dart' as stellar; +import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart'; import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/isar/models/blockchain_data/transaction.dart'; @@ -22,6 +23,7 @@ import '../../../utilities/amount/amount.dart'; import '../../../utilities/enums/fee_rate_type_enum.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/test_stellar_node_connection.dart'; +import '../../../utilities/tor_plain_net_option_enum.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../models/tx_data.dart'; import '../intermediate/bip39_wallet.dart'; @@ -61,12 +63,40 @@ class StellarWallet extends Bip39Wallet { ); } + void _hackedCheck() { + final node = getCurrentNode(); + final netOption = TorPlainNetworkOption.fromNodeData( + node.torEnabled, + node.clearnetEnabled, + ); + + if (prefs.useTor) { + if (netOption == TorPlainNetworkOption.clear) { + _stellarSdk?.httpClient.close(); + _stellarSdk = null; + throw NodeTorMismatchConfigException( + message: "TOR enabled but node set to clearnet only", + ); + } + } else { + if (netOption == TorPlainNetworkOption.tor) { + _stellarSdk?.httpClient.close(); + _stellarSdk = null; + throw NodeTorMismatchConfigException( + message: "TOR off but node set to TOR only", + ); + } + } + } + Future get stellarSdk async { if (_requireMutex) { await _torConnectingLock.protect(() async { + _hackedCheck(); _stellarSdk ??= _getFreshSdk(); }); } else { + _hackedCheck(); _stellarSdk ??= _getFreshSdk(); } return _stellarSdk!; diff --git a/lib/wallets/wallet/impl/sub_wallets/eth_token_wallet.dart b/lib/wallets/wallet/impl/sub_wallets/eth_token_wallet.dart index 3be7b8edb..99d831f4b 100644 --- a/lib/wallets/wallet/impl/sub_wallets/eth_token_wallet.dart +++ b/lib/wallets/wallet/impl/sub_wallets/eth_token_wallet.dart @@ -39,7 +39,7 @@ class EthTokenWallet extends Wallet { late web3dart.DeployedContract _deployedContract; late web3dart.ContractFunction _sendFunction; - static const _gasLimit = 200000; + static const _gasLimit = 65000; // =========================================================================== @@ -67,6 +67,67 @@ class EthTokenWallet extends Wallet { String _addressFromTopic(String topic) => checksumEthereumAddress("0x${topic.substring(topic.length - 40)}"); + TxData _prepareTempTx(TxData txData, String myAddress) { + final otherData = { + "nonce": txData.nonce!, + "isCancelled": false, + "overrideFee": txData.fee!.toJsonString(), + "contractAddress": tokenContract.address, + }; + + final amount = txData.recipients!.first.amount; + final addressTo = txData.recipients!.first.address; + + // hack eth tx data into inputs and outputs + final List outputs = []; + final List inputs = []; + + final output = OutputV2.isarCantDoRequiredInDefaultConstructor( + scriptPubKeyHex: "00", + valueStringSats: amount.raw.toString(), + addresses: [ + addressTo, + ], + walletOwns: addressTo == myAddress, + ); + final input = InputV2.isarCantDoRequiredInDefaultConstructor( + scriptSigHex: null, + scriptSigAsm: null, + sequence: null, + outpoint: null, + addresses: [myAddress], + valueStringSats: amount.raw.toString(), + witness: null, + innerRedeemScriptAsm: null, + coinbase: null, + walletOwns: true, + ); + + outputs.add(output); + inputs.add(input); + + final tempTx = TransactionV2( + walletId: walletId, + blockHash: null, + hash: txData.txHash!, + txid: txData.txid!, + timestamp: DateTime.now().millisecondsSinceEpoch ~/ 1000, + height: null, + inputs: List.unmodifiable(inputs), + outputs: List.unmodifiable(outputs), + version: -1, + type: addressTo == myAddress + ? TransactionType.sentToSelf + : TransactionType.outgoing, + subType: TransactionSubType.ethToken, + otherData: jsonEncode(otherData), + ); + + return txData.copyWith( + tempTx: tempTx, + ); + } + // =========================================================================== @override @@ -181,6 +242,19 @@ class EthTokenWallet extends Wallet { final amount = txData.recipients!.first.amount; final address = txData.recipients!.first.address; + await updateBalance(); + final info = await mainDB.isar.tokenWalletInfo + .where() + .walletIdTokenAddressEqualTo(walletId, tokenContract.address) + .findFirst(); + final availableBalance = info?.getCachedBalance().spendable ?? + Amount.zeroWith( + fractionDigits: tokenContract.decimals, + ); + if (amount > availableBalance) { + throw Exception("Insufficient balance"); + } + final tx = web3dart.Transaction.callContract( contract: _deployedContract, function: _sendFunction, @@ -205,7 +279,10 @@ class EthTokenWallet extends Wallet { @override Future confirmSend({required TxData txData}) async { try { - return await ethWallet.confirmSend(txData: txData); + return await ethWallet.confirmSend( + txData: txData, + prepareTempTx: _prepareTempTx, + ); } catch (e) { // rethrow to pass error in alert rethrow; diff --git a/lib/wallets/wallet/impl/tezos_wallet.dart b/lib/wallets/wallet/impl/tezos_wallet.dart index a1728c10a..ab1448319 100644 --- a/lib/wallets/wallet/impl/tezos_wallet.dart +++ b/lib/wallets/wallet/impl/tezos_wallet.dart @@ -4,6 +4,7 @@ import 'package:isar/isar.dart'; import 'package:tezart/tezart.dart' as tezart; import 'package:tuple/tuple.dart'; +import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart'; import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/isar/models/blockchain_data/transaction.dart'; @@ -14,6 +15,7 @@ import '../../../services/tor_service.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/extensions/impl/string.dart'; import '../../../utilities/logger.dart'; +import '../../../utilities/tor_plain_net_option_enum.dart'; import '../../api/tezos/tezos_account.dart'; import '../../api/tezos/tezos_api.dart'; import '../../api/tezos/tezos_rpc_api.dart'; @@ -42,6 +44,7 @@ class TezosWallet extends Bip39Wallet { String passphrase = "", }) async { try { + _hackedCheckTorNodePrefs(); for (final path in Tezos.possibleDerivationPaths) { final ks = await _getKeyStore(path: path.value); @@ -99,6 +102,7 @@ class TezosWallet extends Bip39Wallet { // Amount? customRevealFee, }) async { try { + _hackedCheckTorNodePrefs(); final sourceKeyStore = await _getKeyStore(); final server = (_xtzNode ?? getCurrentNode()).host; // if (kDebugMode) { @@ -178,6 +182,7 @@ class TezosWallet extends Bip39Wallet { @override Future prepareSend({required TxData txData}) async { try { + _hackedCheckTorNodePrefs(); if (txData.recipients == null || txData.recipients!.length != 1) { throw Exception("$runtimeType prepareSend requires 1 recipient"); } @@ -288,6 +293,7 @@ class TezosWallet extends Bip39Wallet { @override Future confirmSend({required TxData txData}) async { + _hackedCheckTorNodePrefs(); await txData.tezosOperationsList!.inject(); await txData.tezosOperationsList!.monitor(); return txData.copyWith( @@ -301,6 +307,7 @@ class TezosWallet extends Bip39Wallet { TezosAccount account, String recipientAddress, ) async { + _hackedCheckTorNodePrefs(); try { final opList = await _buildSendTransaction( amount: Amount( @@ -356,6 +363,7 @@ class TezosWallet extends Bip39Wallet { int feeRate, { String recipientAddress = "tz1MXvDCyXSqBqXPNDcsdmVZKfoxL9FTHmp2", }) async { + _hackedCheckTorNodePrefs(); if (info.cachedBalance.spendable.raw == BigInt.zero) { return Amount( rawValue: BigInt.zero, @@ -402,6 +410,7 @@ class TezosWallet extends Bip39Wallet { @override Future pingCheck() async { + _hackedCheckTorNodePrefs(); final currentNode = getCurrentNode(); return await TezosRpcAPI.testNetworkConnection( nodeInfo: ( @@ -413,6 +422,7 @@ class TezosWallet extends Bip39Wallet { @override Future recover({required bool isRescan}) async { + _hackedCheckTorNodePrefs(); await refreshMutex.protect(() async { if (isRescan) { await mainDB.deleteWalletBlockchainData(walletId); @@ -463,6 +473,7 @@ class TezosWallet extends Bip39Wallet { @override Future updateBalance() async { try { + _hackedCheckTorNodePrefs(); final currentNode = _xtzNode ?? getCurrentNode(); final balance = await TezosRpcAPI.getBalance( nodeInfo: (host: currentNode.host, port: currentNode.port), @@ -498,6 +509,7 @@ class TezosWallet extends Bip39Wallet { @override Future updateChainHeight() async { try { + _hackedCheckTorNodePrefs(); final currentNode = _xtzNode ?? getCurrentNode(); final height = await TezosRpcAPI.getChainHeight( nodeInfo: ( @@ -530,14 +542,15 @@ class TezosWallet extends Bip39Wallet { @override NodeModel getCurrentNode() { - return _xtzNode ?? + return _xtzNode ??= NodeService(secureStorageInterface: secureStorageInterface) - .getPrimaryNodeFor(currency: info.coin) ?? - info.coin.defaultNode; + .getPrimaryNodeFor(currency: info.coin) ?? + info.coin.defaultNode; } @override Future updateTransactions() async { + _hackedCheckTorNodePrefs(); // TODO: optimize updateTransactions and use V2 final myAddress = (await getCurrentReceivingAddress())!; @@ -615,4 +628,26 @@ class TezosWallet extends Bip39Wallet { // do nothing. Not used in tezos return false; } + + void _hackedCheckTorNodePrefs() { + final node = nodeService.getPrimaryNodeFor(currency: cryptoCurrency)!; + final netOption = TorPlainNetworkOption.fromNodeData( + node.torEnabled, + node.clearnetEnabled, + ); + + if (prefs.useTor) { + if (netOption == TorPlainNetworkOption.clear) { + throw NodeTorMismatchConfigException( + message: "TOR enabled but node set to clearnet only", + ); + } + } else { + if (netOption == TorPlainNetworkOption.tor) { + throw NodeTorMismatchConfigException( + message: "TOR off but node set to TOR only", + ); + } + } + } } diff --git a/lib/wallets/wallet/impl/wownero_wallet.dart b/lib/wallets/wallet/impl/wownero_wallet.dart index a3ef7d04b..a33bd2da7 100644 --- a/lib/wallets/wallet/impl/wownero_wallet.dart +++ b/lib/wallets/wallet/impl/wownero_wallet.dart @@ -1,130 +1,52 @@ import 'dart:async'; -import 'dart:io'; -import 'dart:math'; -import 'package:cw_core/monero_transaction_priority.dart'; -import 'package:cw_core/node.dart'; -import 'package:cw_core/pending_transaction.dart'; -import 'package:cw_core/sync_status.dart'; -import 'package:cw_core/transaction_direction.dart'; -import 'package:cw_core/wallet_base.dart'; -import 'package:cw_core/wallet_credentials.dart'; -import 'package:cw_core/wallet_info.dart'; -import 'package:cw_core/wallet_type.dart'; -import 'package:cw_monero/api/exceptions/creation_transaction_exception.dart'; -import 'package:cw_wownero/api/account_list.dart'; -import 'package:cw_wownero/pending_wownero_transaction.dart'; -import 'package:cw_wownero/wownero_wallet.dart'; -import 'package:decimal/decimal.dart'; -import 'package:flutter_libmonero/core/wallet_creation_service.dart'; -import 'package:flutter_libmonero/view_model/send/output.dart' - as wownero_output; -import 'package:flutter_libmonero/wownero/wownero.dart' as wow_dart; -import 'package:isar/isar.dart'; -import 'package:monero/wownero.dart' as wownerodart; -import 'package:mutex/mutex.dart'; -import 'package:tuple/tuple.dart'; +import 'package:compat/compat.dart' as lib_monero_compat; +import 'package:cs_monero/cs_monero.dart' as lib_monero; -import '../../../db/hive/db.dart'; -import '../../../models/isar/models/blockchain_data/address.dart'; -import '../../../models/isar/models/blockchain_data/transaction.dart'; -import '../../../models/keys/cw_key_data.dart'; -import '../../../services/event_bus/events/global/tor_connection_status_changed_event.dart'; -import '../../../services/event_bus/events/global/tor_status_changed_event.dart'; -import '../../../services/event_bus/global_event_bus.dart'; -import '../../../services/tor_service.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/enums/fee_rate_type_enum.dart'; -import '../../../utilities/logger.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../models/tx_data.dart'; -import '../intermediate/cryptonote_wallet.dart'; -import '../wallet.dart'; -import '../wallet_mixin_interfaces/cw_based_interface.dart'; +import '../intermediate/lib_monero_wallet.dart'; -class WowneroWallet extends CryptonoteWallet with CwBasedInterface { - WowneroWallet(CryptoCurrencyNetwork network) : super(Wownero(network)) { - final bus = GlobalEventBus.instance; - - // Listen for tor status changes. - _torStatusListener = bus.on().listen( - (event) async { - switch (event.newStatus) { - case TorConnectionStatus.connecting: - if (!_torConnectingLock.isLocked) { - await _torConnectingLock.acquire(); - } - _requireMutex = true; - break; - - case TorConnectionStatus.connected: - case TorConnectionStatus.disconnected: - if (_torConnectingLock.isLocked) { - _torConnectingLock.release(); - } - _requireMutex = false; - break; - } - }, - ); - - // Listen for tor preference changes. - _torPreferenceListener = bus.on().listen( - (event) async { - await updateNode(); - }, - ); - } - - @override - Address addressFor({required int index, int account = 0}) { - final String address = (CwBasedInterface.cwWalletBase as WowneroWalletBase) - .getTransactionAddress(account, index); - - final newReceivingAddress = Address( - walletId: walletId, - derivationIndex: index, - derivationPath: null, - value: address, - publicKey: [], - type: AddressType.cryptonote, - subType: AddressSubType.receiving, - ); - - return newReceivingAddress; - } +class WowneroWallet extends LibMoneroWallet { + WowneroWallet(CryptoCurrencyNetwork network) + : super( + Wownero(network), + lib_monero_compat.WalletType.wownero, + ); @override Future estimateFeeFor(Amount amount, int feeRate) async { - if (CwBasedInterface.cwWalletBase == null || - CwBasedInterface.cwWalletBase?.syncStatus is! SyncedSyncStatus) { + if (libMoneroWallet == null || + syncStatus is! lib_monero_compat.SyncedSyncStatus) { return Amount.zeroWith( fractionDigits: cryptoCurrency.fractionDigits, ); } - MoneroTransactionPriority priority; + lib_monero.TransactionPriority priority; FeeRateType feeRateType = FeeRateType.slow; switch (feeRate) { case 1: - priority = MoneroTransactionPriority.regular; + priority = lib_monero.TransactionPriority.low; feeRateType = FeeRateType.average; break; case 2: - priority = MoneroTransactionPriority.medium; + priority = lib_monero.TransactionPriority.medium; feeRateType = FeeRateType.average; break; case 3: - priority = MoneroTransactionPriority.fast; + priority = lib_monero.TransactionPriority.high; feeRateType = FeeRateType.fast; break; case 4: - priority = MoneroTransactionPriority.fastest; + priority = lib_monero.TransactionPriority.last; feeRateType = FeeRateType.fast; break; case 0: default: - priority = MoneroTransactionPriority.slow; + priority = lib_monero.TransactionPriority.normal; feeRateType = FeeRateType.slow; break; } @@ -152,7 +74,7 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { // unsure why this delay? await Future.delayed(const Duration(milliseconds: 500)); } catch (e) { - approximateFee = CwBasedInterface.cwWalletBase!.calculateEstimatedFee( + approximateFee = libMoneroWallet!.estimateFee( priority, amount.raw.toInt(), ); @@ -171,639 +93,67 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { } @override - Future pingCheck() async { - return await (CwBasedInterface.cwWalletBase as WowneroWalletBase?) - ?.isConnected() ?? - false; - } + bool walletExists(String path) => + lib_monero.WowneroWallet.isWalletExist(path); @override - Future updateNode() async { - final node = getCurrentNode(); - - final host = Uri.parse(node.host).host; - ({InternetAddress host, int port})? proxy; - if (prefs.useTor) { - proxy = TorService.sharedInstance.getProxyInfo(); - } - if (_requireMutex) { - await _torConnectingLock.protect(() async { - await CwBasedInterface.cwWalletBase?.connectToNode( - node: Node( - uri: "$host:${node.port}", - type: WalletType.wownero, - trusted: node.trusted ?? false, - useSSL: node.useSSL, - ), - socksProxyAddress: - proxy == null ? null : "${proxy.host.address}:${proxy.port}", - ); - }); - } else { - await CwBasedInterface.cwWalletBase?.connectToNode( - node: Node( - uri: "$host:${node.port}", - type: WalletType.wownero, - trusted: node.trusted ?? false, - useSSL: node.useSSL, - ), - socksProxyAddress: - proxy == null ? null : "${proxy.host.address}:${proxy.port}", - ); - } - - return; - } - - @override - Future getKeys() async { - final base = (CwBasedInterface.cwWalletBase as WowneroWalletBase?); - - if (base == null || - base.walletInfo.name != walletId || - CwBasedInterface.exitMutex.isLocked) { - return null; - } - - return CWKeyData( - walletId: walletId, - publicViewKey: base.keys.publicViewKey, - privateViewKey: base.keys.privateViewKey, - publicSpendKey: base.keys.publicSpendKey, - privateSpendKey: base.keys.privateSpendKey, + void loadWallet({ + required String path, + required String password, + }) { + libMoneroWallet = lib_monero.WowneroWallet.loadWallet( + path: path, + password: password, ); } @override - Future updateTransactions() async { - final base = (CwBasedInterface.cwWalletBase as WowneroWalletBase?); - - if (base == null || - base.walletInfo.name != walletId || - CwBasedInterface.exitMutex.isLocked) { - return; - } - await base.updateTransactions(); - final transactions = base.transactionHistory?.transactions; - - // final cachedTransactions = - // DB.instance.get(boxName: walletId, key: 'latest_tx_model') - // as TransactionData?; - // int latestTxnBlockHeight = - // DB.instance.get(boxName: walletId, key: "storedTxnDataHeight") - // as int? ?? - // 0; - // - // final txidsList = DB.instance - // .get(boxName: walletId, key: "cachedTxids") as List? ?? - // []; - // - // final Set cachedTxids = Set.from(txidsList); - - // TODO: filter to skip cached + confirmed txn processing in next step - // final unconfirmedCachedTransactions = - // cachedTransactions?.getAllTransactions() ?? {}; - // unconfirmedCachedTransactions - // .removeWhere((key, value) => value.confirmedStatus); - // - // if (cachedTransactions != null) { - // for (final tx in allTxHashes.toList(growable: false)) { - // final txHeight = tx["height"] as int; - // if (txHeight > 0 && - // txHeight < latestTxnBlockHeight - MINIMUM_CONFIRMATIONS) { - // if (unconfirmedCachedTransactions[tx["tx_hash"] as String] == null) { - // allTxHashes.remove(tx); - // } - // } - // } - // } - - final List> txnsData = []; - - if (transactions != null) { - for (final tx in transactions.entries) { - Address? address; - TransactionType type; - if (tx.value.direction == TransactionDirection.incoming) { - final addressInfo = tx.value.additionalInfo; - - final addressString = - (CwBasedInterface.cwWalletBase as WowneroWalletBase?) - ?.getTransactionAddress( - addressInfo!['accountIndex'] as int, - addressInfo['addressIndex'] as int, - ); - - if (addressString != null) { - address = await mainDB - .getAddresses(walletId) - .filter() - .valueEqualTo(addressString) - .findFirst(); - } - - type = TransactionType.incoming; - } else { - // txn.address = ""; - type = TransactionType.outgoing; - } - - final txn = Transaction( - walletId: walletId, - txid: tx.value.id, - timestamp: (tx.value.date.millisecondsSinceEpoch ~/ 1000), - type: type, - subType: TransactionSubType.none, - amount: tx.value.amount ?? 0, - amountString: Amount( - rawValue: BigInt.from(tx.value.amount ?? 0), - fractionDigits: cryptoCurrency.fractionDigits, - ).toJsonString(), - fee: tx.value.fee ?? 0, - height: tx.value.height, - isCancelled: false, - isLelantus: false, - slateId: null, - otherData: null, - nonce: null, - inputs: [], - outputs: [], - numberOfMessages: null, - ); - - txnsData.add(Tuple2(txn, address)); - } - } - - await mainDB.isar.writeTxn(() async { - await mainDB.isar.transactions - .where() - .walletIdEqualTo(walletId) - .deleteAll(); - for (final data in txnsData) { - final tx = data.item1; - - // save transaction - await mainDB.isar.transactions.put(tx); - - if (data.item2 != null) { - final address = await mainDB.getAddress(walletId, data.item2!.value); - - // check if address exists in db and add if it does not - if (address == null) { - await mainDB.isar.addresses.put(data.item2!); - } - - // link and save address - tx.address.value = address ?? data.item2!; - await tx.address.save(); - } - } - }); - } - - @override - Future init({bool? isRestore}) async { - await CwBasedInterface.exitMutex.protect(() async {}); - CwBasedInterface.cwWalletService = wow_dart.wownero - .createWowneroWalletService(DB.instance.moneroWalletInfoBox); - - if (!(await CwBasedInterface.cwWalletService!.isWalletExit(walletId)) && - isRestore != true) { - WalletInfo walletInfo; - WalletCredentials credentials; - try { - final dirPath = - await pathForWalletDir(name: walletId, type: WalletType.wownero); - final path = - await pathForWallet(name: walletId, type: WalletType.wownero); - credentials = wow_dart.wownero.createWowneroNewWalletCredentials( - name: walletId, - language: "English", - seedWordsLength: 14, - ); - - walletInfo = WalletInfo.external( - id: WalletBase.idFor(walletId, WalletType.wownero), - name: walletId, - type: WalletType.wownero, - isRecovery: false, - restoreHeight: credentials.height ?? 0, - date: DateTime.now(), - path: path, - dirPath: dirPath, - address: '', - ); - credentials.walletInfo = walletInfo; - - final _walletCreationService = WalletCreationService( - secureStorage: secureStorageInterface, - walletService: CwBasedInterface.cwWalletService, - keyService: cwKeysStorage, - ); - // _walletCreationService.changeWalletType(); - _walletCreationService.type = WalletType.wownero; - // To restore from a seed - final wallet = await _walletCreationService.create(credentials); - - final height = wownerodart.Wallet_getRefreshFromBlockHeight(wptr!); - - await info.updateRestoreHeight( - newRestoreHeight: height, - isar: mainDB.isar, - ); - - // special case for xmr/wow. Normally mnemonic + passphrase is saved - // before wallet.init() is called - await secureStorageInterface.write( - key: Wallet.mnemonicKey(walletId: walletId), - value: wallet.seed.trim(), - ); - await secureStorageInterface.write( - key: Wallet.mnemonicPassphraseKey(walletId: walletId), - value: "", - ); - - walletInfo.restoreHeight = height; - - walletInfo.address = wallet.walletAddresses.address; - await DB.instance - .add(boxName: WalletInfo.boxName, value: walletInfo); - - wallet.close(); - } catch (e, s) { - Logging.instance.log("$e\n$s", level: LogLevel.Fatal); - CwBasedInterface.cwWalletBase?.close(); - } - await updateNode(); - } - - return super.init(); - } - - @override - Future open() async { - // await any previous exit - await CwBasedInterface.exitMutex.protect(() async {}); - - String? password; - try { - password = await cwKeysStorage.getWalletPassword(walletName: walletId); - } catch (e, s) { - throw Exception("Password not found $e, $s"); - } - - CwBasedInterface.cwWalletBase?.close(); - CwBasedInterface.cwWalletBase = (await CwBasedInterface.cwWalletService! - .openWallet(walletId, password)) as WowneroWalletBase; - - (CwBasedInterface.cwWalletBase as WowneroWalletBase?)?.onNewBlock = - onNewBlock; - (CwBasedInterface.cwWalletBase as WowneroWalletBase?)?.onNewTransaction = - onNewTransaction; - (CwBasedInterface.cwWalletBase as WowneroWalletBase?)?.syncStatusChanged = - syncStatusChanged; - - await updateNode(); - - Address? currentAddress = await getCurrentReceivingAddress(); - if (currentAddress == null) { - currentAddress = addressFor(index: 0); - await mainDB.updateOrPutAddresses([currentAddress]); - } - if (info.cachedReceivingAddress != currentAddress.value) { - await info.updateReceivingAddress( - newAddress: currentAddress.value, - isar: mainDB.isar, - ); - } - - await (CwBasedInterface.cwWalletBase as WowneroWalletBase?)?.startSync(); - unawaited(refresh()); - autoSaveTimer?.cancel(); - autoSaveTimer = Timer.periodic( - const Duration(seconds: 193), - (_) async => await CwBasedInterface.cwWalletBase?.save(), - ); - } - - @override - Future exitCwWallet() async { - (CwBasedInterface.cwWalletBase as WowneroWalletBase?)?.onNewBlock = null; - (CwBasedInterface.cwWalletBase as WowneroWalletBase?)?.onNewTransaction = - null; - (CwBasedInterface.cwWalletBase as WowneroWalletBase?)?.syncStatusChanged = - null; - await (CwBasedInterface.cwWalletBase as WowneroWalletBase?) - ?.save(prioritySave: true); - } - - @override - Future recover({required bool isRescan}) async { - await CwBasedInterface.exitMutex.protect(() async {}); - - if (isRescan) { - await refreshMutex.protect(() async { - // clear blockchain info - await mainDB.deleteWalletBlockchainData(walletId); - - final restoreHeight = - CwBasedInterface.cwWalletBase?.walletInfo.restoreHeight; - highestPercentCached = 0; - await CwBasedInterface.cwWalletBase?.rescan(height: restoreHeight ?? 0); - }); - unawaited(refresh()); - return; - } - - await refreshMutex.protect(() async { - final mnemonic = await getMnemonic(); - final seedLength = mnemonic.trim().split(" ").length; - - if (!(seedLength == 14 || seedLength == 25)) { - throw Exception("Invalid wownero mnemonic length found: $seedLength"); - } - - try { - int height = info.restoreHeight; - - // extract seed height from 14 word seed - if (seedLength == 14) { - height = 0; - } else { - height = max(height, 0); - } - - // TODO: info.updateRestoreHeight - // await DB.instance - // .put(boxName: walletId, key: "restoreHeight", value: height); - - CwBasedInterface.cwWalletService = wow_dart.wownero - .createWowneroWalletService(DB.instance.moneroWalletInfoBox); - WalletInfo walletInfo; - WalletCredentials credentials; - final String name = walletId; - final dirPath = - await pathForWalletDir(name: name, type: WalletType.wownero); - final path = await pathForWallet(name: name, type: WalletType.wownero); - credentials = - wow_dart.wownero.createWowneroRestoreWalletFromSeedCredentials( - name: name, - height: height, - mnemonic: mnemonic.trim(), - ); - try { - walletInfo = WalletInfo.external( - id: WalletBase.idFor(name, WalletType.wownero), - name: name, - type: WalletType.wownero, - isRecovery: false, - restoreHeight: credentials.height ?? 0, - date: DateTime.now(), - path: path, - dirPath: dirPath, - // TODO: find out what to put for address - address: '', - ); - credentials.walletInfo = walletInfo; - - final cwWalletCreationService = WalletCreationService( - secureStorage: secureStorageInterface, - walletService: CwBasedInterface.cwWalletService, - keyService: cwKeysStorage, - ); - cwWalletCreationService.type = WalletType.wownero; - // To restore from a seed - final wallet = await cwWalletCreationService - .restoreFromSeed(credentials) as WowneroWalletBase; - height = wownerodart.Wallet_getRefreshFromBlockHeight(wptr!); - walletInfo.address = wallet.walletAddresses.address; - walletInfo.restoreHeight = height; - await info.updateRestoreHeight( - newRestoreHeight: height, - isar: mainDB.isar, - ); - await DB.instance - .add(boxName: WalletInfo.boxName, value: walletInfo); - CwBasedInterface.cwWalletBase?.close(); - CwBasedInterface.cwWalletBase = wallet; - if (walletInfo.address != null) { - final newReceivingAddress = await getCurrentReceivingAddress() ?? - Address( - walletId: walletId, - derivationIndex: 0, - derivationPath: null, - value: walletInfo.address!, - publicKey: [], - type: AddressType.cryptonote, - subType: AddressSubType.receiving, - ); - - await mainDB.updateOrPutAddresses([newReceivingAddress]); - await info.updateReceivingAddress( - newAddress: newReceivingAddress.value, - isar: mainDB.isar, - ); - } - } catch (e, s) { - Logging.instance.log("$e\n$s", level: LogLevel.Fatal); - } - await updateNode(); - - await CwBasedInterface.cwWalletBase?.rescan(height: credentials.height); - CwBasedInterface.cwWalletBase?.close(); - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from recoverFromMnemonic(): $e\n$s", - level: LogLevel.Error, - ); - rethrow; - } - }); - } - - @override - Future prepareSend({required TxData txData}) async { - try { - final feeRate = txData.feeRateType; - if (feeRate is FeeRateType) { - MoneroTransactionPriority feePriority; - switch (feeRate) { - case FeeRateType.fast: - feePriority = MoneroTransactionPriority.fast; - break; - case FeeRateType.average: - feePriority = MoneroTransactionPriority.regular; - break; - case FeeRateType.slow: - feePriority = MoneroTransactionPriority.slow; - break; - default: - throw ArgumentError("Invalid use of custom fee"); - } - - Future? awaitPendingTransaction; - try { - // check for send all - bool isSendAll = false; - final balance = await availableBalance; - if (txData.amount! == balance && - txData.recipients!.first.amount == balance) { - isSendAll = true; - } - - final List outputs = []; - for (final recipient in txData.recipients!) { - final output = - wownero_output.Output(CwBasedInterface.cwWalletBase!); - output.address = recipient.address; - output.sendAll = isSendAll; - final String amountToSend = recipient.amount.decimal.toString(); - output.setCryptoAmount(amountToSend); - outputs.add(output); - } - - final tmp = - wow_dart.wownero.createWowneroTransactionCreationCredentials( - outputs: outputs, - priority: feePriority, - ); - - await prepareSendMutex.protect(() async { - awaitPendingTransaction = - CwBasedInterface.cwWalletBase!.createTransaction(tmp); - }); - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from prepareSend(): $e\n$s", - level: LogLevel.Warning, - ); - } - - final PendingWowneroTransaction pendingWowneroTransaction = - await (awaitPendingTransaction!) as PendingWowneroTransaction; - final realFee = Amount.fromDecimal( - Decimal.parse(pendingWowneroTransaction.feeFormatted), - fractionDigits: cryptoCurrency.fractionDigits, - ); - - return txData.copyWith( - fee: realFee, - pendingWowneroTransaction: pendingWowneroTransaction, - ); - } else { - throw ArgumentError("Invalid fee rate argument provided!"); - } - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from prepare send(): $e\n$s", - level: LogLevel.Info, + Future getCreatedWallet({ + required String path, + required String password, + }) async => + await lib_monero.WowneroWallet.create( + path: path, + password: password, + seedType: lib_monero.WowneroSeedType + .fourteen, // TODO: check we want to actually use 14 here + overrideDeprecated14WordSeedException: true, ); - if (e.toString().contains("Incorrect unlocked balance")) { - throw Exception("Insufficient balance!"); - } else if (e is CreationTransactionException) { - throw Exception("Insufficient funds to pay for transaction fee!"); - } else { - throw Exception("Transaction failed with error code $e"); - } - } - } - @override - Future confirmSend({required TxData txData}) async { - try { - try { - await txData.pendingWowneroTransaction!.commit(); - Logging.instance.log( - "transaction ${txData.pendingWowneroTransaction!.id} has been sent", - level: LogLevel.Info, - ); - return txData.copyWith(txid: txData.pendingWowneroTransaction!.id); - } catch (e, s) { - Logging.instance.log( - "${info.name} wownero confirmSend: $e\n$s", - level: LogLevel.Error, - ); - rethrow; - } - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from confirmSend(): $e\n$s", - level: LogLevel.Info, + Future getRestoredWallet({ + required String path, + required String password, + required String mnemonic, + int height = 0, + }) async => + await lib_monero.WowneroWallet.restoreWalletFromSeed( + path: path, + password: password, + seed: mnemonic, + restoreHeight: height, ); - rethrow; - } - } @override - Future get availableBalance async { - try { - if (CwBasedInterface.exitMutex.isLocked) { - throw Exception("Exit in progress"); - } - - int runningBalance = 0; - for (final entry in (CwBasedInterface.cwWalletBase as WowneroWalletBase?)! - .balance! - .entries) { - runningBalance += entry.value.unlockedBalance; - } - return Amount( - rawValue: BigInt.from(runningBalance), - fractionDigits: cryptoCurrency.fractionDigits, + Future getRestoredFromViewKeyWallet({ + required String path, + required String password, + required String address, + required String privateViewKey, + int height = 0, + }) async => + lib_monero.WowneroWallet.createViewOnlyWallet( + path: path, + password: password, + address: address, + viewKey: privateViewKey, + restoreHeight: height, ); - } catch (_) { - return info.cachedBalance.spendable; - } - } @override - Future get totalBalance async { - try { - if (CwBasedInterface.exitMutex.isLocked) { - throw Exception("Exit in progress"); - } - final balanceEntries = - (CwBasedInterface.cwWalletBase as WowneroWalletBase?) - ?.balance - ?.entries; - if (balanceEntries != null) { - int bal = 0; - for (final element in balanceEntries) { - bal = bal + element.value.fullBalance; - } - return Amount( - rawValue: BigInt.from(bal), - fractionDigits: cryptoCurrency.fractionDigits, - ); - } else { - final transactions = - CwBasedInterface.cwWalletBase!.transactionHistory!.transactions; - int transactionBalance = 0; - for (final tx in transactions!.entries) { - if (tx.value.direction == TransactionDirection.incoming) { - transactionBalance += tx.value.amount!; - } else { - transactionBalance += -tx.value.amount! - tx.value.fee!; - } - } - - return Amount( - rawValue: BigInt.from(transactionBalance), - fractionDigits: cryptoCurrency.fractionDigits, - ); - } - } catch (_) { - return info.cachedBalance.total; + void invalidSeedLengthCheck(int length) { + if (!(length == 14 || length == 16 || length == 25)) { + throw Exception("Invalid wownero mnemonic length found: $length"); } } - - // ============== Private ==================================================== - - StreamSubscription? _torStatusListener; - StreamSubscription? _torPreferenceListener; - - final Mutex _torConnectingLock = Mutex(); - bool _requireMutex = false; } diff --git a/lib/wallets/wallet/intermediate/bip39_hd_wallet.dart b/lib/wallets/wallet/intermediate/bip39_hd_wallet.dart index 3d603fc03..832ce3e13 100644 --- a/lib/wallets/wallet/intermediate/bip39_hd_wallet.dart +++ b/lib/wallets/wallet/intermediate/bip39_hd_wallet.dart @@ -6,15 +6,17 @@ import 'package:isar/isar.dart'; import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; +import '../../../models/keys/view_only_wallet_data.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/enums/derive_path_type_enum.dart'; import '../../../utilities/extensions/extensions.dart'; import '../../crypto_currency/intermediate/bip39_hd_currency.dart'; import '../wallet_mixin_interfaces/multi_address_interface.dart'; +import '../wallet_mixin_interfaces/view_only_option_interface.dart'; import 'bip39_wallet.dart'; abstract class Bip39HDWallet extends Bip39Wallet - with MultiAddressInterface { + with MultiAddressInterface, ViewOnlyOptionInterface { Bip39HDWallet(super.cryptoCurrency); Set get supportedAddressTypes => @@ -74,6 +76,34 @@ abstract class Bip39HDWallet extends Bip39Wallet return address; } + @override + List get standardReceivingAddressFilters => [ + // view only only have a single derivation path currently + if (!isViewOnly) + FilterCondition.equalTo( + property: r"type", + value: info.mainAddressType, + ), + const FilterCondition.equalTo( + property: r"subType", + value: AddressSubType.receiving, + ), + ]; + + @override + List get standardChangeAddressFilters => [ + // view only only have a single derivation path currently + if (!isViewOnly) + FilterCondition.equalTo( + property: r"type", + value: info.mainAddressType, + ), + const FilterCondition.equalTo( + property: r"subType", + value: AddressSubType.change, + ), + ]; + /// Generates a receiving address. If none /// are in the current wallet db it will generate at index 0, otherwise the /// highest index found in the current wallet db. @@ -83,10 +113,17 @@ abstract class Bip39HDWallet extends Bip39Wallet final index = current == null ? 0 : current.derivationIndex + 1; const chain = 0; // receiving address + final DerivePathType type; + if (isViewOnly) { + type = await _viewOnlyPathHelper(); + } else { + type = _fromAddressType(info.mainAddressType); + } + final address = await _generateAddress( chain: chain, index: index, - derivePathType: _fromAddressType(info.mainAddressType), + derivePathType: type, ); await mainDB.updateOrPutAddresses([address]); @@ -105,10 +142,17 @@ abstract class Bip39HDWallet extends Bip39Wallet final index = current == null ? 0 : current.derivationIndex + 1; const chain = 1; // change address + final DerivePathType type; + if (isViewOnly) { + type = await _viewOnlyPathHelper(); + } else { + type = _fromAddressType(info.mainAddressType); + } + final address = await _generateAddress( chain: chain, index: index, - derivePathType: _fromAddressType(info.mainAddressType), + derivePathType: type, ); await mainDB.updateOrPutAddresses([address]); @@ -116,12 +160,21 @@ abstract class Bip39HDWallet extends Bip39Wallet @override Future checkSaveInitialReceivingAddress() async { - final current = await getCurrentChangeAddress(); + if (isViewOnly && viewOnlyType == ViewOnlyWalletType.addressOnly) return; + + final current = await getCurrentReceivingAddress(); if (current == null) { + final DerivePathType type; + if (isViewOnly) { + type = await _viewOnlyPathHelper(); + } else { + type = _fromAddressType(info.mainAddressType); + } + final address = await _generateAddress( chain: 0, // receiving index: 0, // initial index - derivePathType: _fromAddressType(info.mainAddressType), + derivePathType: type, ); await mainDB.updateOrPutAddresses([address]); @@ -138,6 +191,25 @@ abstract class Bip39HDWallet extends Bip39Wallet // ========== Private ======================================================== + Future _viewOnlyPathHelper() async { + final voData = + await getViewOnlyWalletData() as ExtendedKeysViewOnlyWalletData; + for (final type in cryptoCurrency.supportedDerivationPathTypes) { + final testPath = cryptoCurrency.constructDerivePath( + derivePathType: type, + chain: 0, + index: 0, + ); + if (testPath.startsWith(voData.xPubs.first.path)) { + return type; + } + } + + throw Exception( + "_viewOnlyPathHelper viewOnly failed to match paths", + ); + } + DerivePathType _fromAddressType(AddressType addressType) { switch (addressType) { case AddressType.p2pkh: @@ -174,15 +246,29 @@ abstract class Bip39HDWallet extends Bip39Wallet required int index, required DerivePathType derivePathType, }) async { - final root = await getRootHDNode(); - final derivationPath = cryptoCurrency.constructDerivePath( derivePathType: derivePathType, chain: chain, index: index, ); - final keys = root.derivePath(derivationPath); + final coinlib.HDKey keys; + if (isViewOnly) { + final idx = derivationPath.lastIndexOf("'/"); + final path = derivationPath.substring(idx + 2); + final data = + await getViewOnlyWalletData() as ExtendedKeysViewOnlyWalletData; + + final xPub = data.xPubs.firstWhere( + (e) => derivationPath.startsWith(e.path), + ); + + final node = coinlib.HDPublicKey.decode(xPub.encoded); + keys = node.derivePath(path); + } else { + final root = await getRootHDNode(); + keys = root.derivePath(derivationPath); + } final data = cryptoCurrency.getAddressForPublicKey( publicKey: keys.publicKey, @@ -205,7 +291,8 @@ abstract class Bip39HDWallet extends Bip39Wallet value: convertAddressString(data.address.toString()), publicKey: keys.publicKey.data, derivationIndex: index, - derivationPath: DerivationPath()..value = derivationPath, + derivationPath: + isViewOnly ? null : (DerivationPath()..value = derivationPath), type: data.addressType, subType: subType, ); @@ -250,6 +337,7 @@ abstract class Bip39HDWallet extends Bip39Wallet if (utxo.isConfirmed( currentChainHeight, cryptoCurrency.minConfirms, + cryptoCurrency.minCoinbaseConfirms, )) { satoshiBalanceSpendable += utxoAmount; } else { diff --git a/lib/wallets/wallet/intermediate/cryptonote_wallet.dart b/lib/wallets/wallet/intermediate/cryptonote_wallet.dart index aabee237b..131bf8f04 100644 --- a/lib/wallets/wallet/intermediate/cryptonote_wallet.dart +++ b/lib/wallets/wallet/intermediate/cryptonote_wallet.dart @@ -1,51 +1,9 @@ -import 'dart:async'; - import '../../crypto_currency/intermediate/cryptonote_currency.dart'; -import '../../models/tx_data.dart'; import '../wallet.dart'; +import '../wallet_mixin_interfaces/coin_control_interface.dart'; import '../wallet_mixin_interfaces/mnemonic_interface.dart'; abstract class CryptonoteWallet extends Wallet - with MnemonicInterface { + with MnemonicInterface, CoinControlInterface { CryptonoteWallet(super.currency); - - Completer? walletOpenCompleter; - - void resetWalletOpenCompleter() { - if (walletOpenCompleter == null || walletOpenCompleter!.isCompleted) { - walletOpenCompleter = Completer(); - } - } - - Future waitForWalletOpen() async { - if (walletOpenCompleter != null && !walletOpenCompleter!.isCompleted) { - await walletOpenCompleter!.future; - } - } - - // ========== Overrides ====================================================== - - @override - Future confirmSend({required TxData txData}) { - // TODO: implement confirmSend - throw UnimplementedError(); - } - - @override - Future prepareSend({required TxData txData}) { - // TODO: implement prepareSend - throw UnimplementedError(); - } - - @override - Future recover({required bool isRescan}) { - // TODO: implement recover - throw UnimplementedError(); - } - - @override - Future updateUTXOs() async { - // do nothing for now - return false; - } } diff --git a/lib/wallets/wallet/intermediate/lib_monero_wallet.dart b/lib/wallets/wallet/intermediate/lib_monero_wallet.dart new file mode 100644 index 000000000..597853a62 --- /dev/null +++ b/lib/wallets/wallet/intermediate/lib_monero_wallet.dart @@ -0,0 +1,1453 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; + +import 'package:compat/compat.dart' as lib_monero_compat; +import 'package:cs_monero/cs_monero.dart' as lib_monero; +import 'package:isar/isar.dart'; +import 'package:mutex/mutex.dart'; +import 'package:stack_wallet_backup/generate_password.dart'; + +import '../../../db/hive/db.dart'; +import '../../../models/balance.dart'; +import '../../../models/isar/models/blockchain_data/address.dart'; +import '../../../models/isar/models/blockchain_data/transaction.dart'; +import '../../../models/isar/models/blockchain_data/utxo.dart'; +import '../../../models/isar/models/blockchain_data/v2/input_v2.dart'; +import '../../../models/isar/models/blockchain_data/v2/output_v2.dart'; +import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; +import '../../../models/keys/cw_key_data.dart'; +import '../../../models/keys/view_only_wallet_data.dart'; +import '../../../models/paymint/fee_object_model.dart'; +import '../../../services/event_bus/events/global/blocks_remaining_event.dart'; +import '../../../services/event_bus/events/global/refresh_percent_changed_event.dart'; +import '../../../services/event_bus/events/global/tor_connection_status_changed_event.dart'; +import '../../../services/event_bus/events/global/tor_status_changed_event.dart'; +import '../../../services/event_bus/events/global/updated_in_background_event.dart'; +import '../../../services/event_bus/events/global/wallet_sync_status_changed_event.dart'; +import '../../../services/event_bus/global_event_bus.dart'; +import '../../../services/tor_service.dart'; +import '../../../utilities/amount/amount.dart'; +import '../../../utilities/enums/fee_rate_type_enum.dart'; +import '../../../utilities/logger.dart'; +import '../../../utilities/stack_file_system.dart'; +import '../../crypto_currency/intermediate/cryptonote_currency.dart'; +import '../../isar/models/wallet_info.dart'; +import '../../models/tx_data.dart'; +import '../wallet.dart'; +import '../wallet_mixin_interfaces/multi_address_interface.dart'; +import '../wallet_mixin_interfaces/view_only_option_interface.dart'; +import 'cryptonote_wallet.dart'; + +abstract class LibMoneroWallet + extends CryptonoteWallet + with ViewOnlyOptionInterface + implements MultiAddressInterface { + @override + int get isarTransactionVersion => 2; + + LibMoneroWallet(super.currency, this.compatType) { + final bus = GlobalEventBus.instance; + + // Listen for tor status changes. + _torStatusListener = bus.on().listen( + (event) async { + switch (event.newStatus) { + case TorConnectionStatus.connecting: + if (!_torConnectingLock.isLocked) { + await _torConnectingLock.acquire(); + } + _requireMutex = true; + break; + + case TorConnectionStatus.connected: + case TorConnectionStatus.disconnected: + if (_torConnectingLock.isLocked) { + _torConnectingLock.release(); + } + _requireMutex = false; + break; + } + }, + ); + + // Listen for tor preference changes. + _torPreferenceListener = bus.on().listen( + (event) async { + await updateNode(); + }, + ); + + // Potentially dangerous hack. See comments in _startInit() + _startInit(); + } + // cw based wallet listener to handle synchronization of utxo frozen states + late final StreamSubscription> _streamSub; + Future _startInit() async { + // Delay required as `mainDB` is not initialized in constructor. + // This is a hack and could lead to a race condition. + Future.delayed(const Duration(seconds: 2), () { + _streamSub = mainDB.isar.utxos + .where() + .walletIdEqualTo(walletId) + .watch(fireImmediately: true) + .listen((utxos) async { + try { + await onUTXOsChanged(utxos); + await updateBalance(shouldUpdateUtxos: false); + } catch (e, s) { + lib_monero.Logging.log?.i( + "_startInit", + error: e, + stackTrace: s, + ); + } + }); + }); + } + + final lib_monero_compat.WalletType compatType; + lib_monero.Wallet? libMoneroWallet; + + lib_monero_compat.SyncStatus? get syncStatus => _syncStatus; + lib_monero_compat.SyncStatus? _syncStatus; + int _syncedCount = 0; + void _setSyncStatus(lib_monero_compat.SyncStatus status) { + if (status is lib_monero_compat.SyncedSyncStatus) { + if (_syncStatus is lib_monero_compat.SyncedSyncStatus) { + _syncedCount++; + } + } else { + _syncedCount = 0; + } + + if (_syncedCount < 3) { + _syncStatus = status; + syncStatusChanged(); + } + } + + final prepareSendMutex = Mutex(); + final estimateFeeMutex = Mutex(); + + bool _txRefreshLock = false; + int _lastCheckedHeight = -1; + int _txCount = 0; + int currentKnownChainHeight = 0; + double highestPercentCached = 0; + + void loadWallet({required String path, required String password}); + + Future getCreatedWallet({ + required String path, + required String password, + }); + + Future getRestoredWallet({ + required String path, + required String password, + required String mnemonic, + int height = 0, + }); + + Future getRestoredFromViewKeyWallet({ + required String path, + required String password, + required String address, + required String privateViewKey, + int height = 0, + }); + + void invalidSeedLengthCheck(int length); + + bool walletExists(String path); + + void _setListener() { + if (libMoneroWallet != null && libMoneroWallet!.getListeners().isEmpty) { + libMoneroWallet?.addListener( + lib_monero.WalletListener( + onSyncingUpdate: onSyncingUpdate, + onNewBlock: onNewBlock, + onBalancesChanged: onBalancesChanged, + onError: (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Warning); + }, + ), + ); + } + } + + Future open() async { + bool wasNull = false; + + if (libMoneroWallet == null) { + wasNull = true; + // libMoneroWalletT?.close(); + final path = await pathForWallet( + name: walletId, + type: compatType, + ); + + final String password; + try { + password = (await secureStorageInterface.read( + key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + ))!; + } catch (e, s) { + throw Exception("Password not found $e, $s"); + } + + loadWallet(path: path, password: password); + + _setListener(); + + await updateNode(); + } + + Address? currentAddress = await getCurrentReceivingAddress(); + if (currentAddress == null) { + currentAddress = addressFor(index: 0); + await mainDB.updateOrPutAddresses([currentAddress]); + } + if (info.cachedReceivingAddress != currentAddress.value) { + await info.updateReceivingAddress( + newAddress: currentAddress.value, + isar: mainDB.isar, + ); + } + + if (wasNull) { + try { + _setSyncStatus(lib_monero_compat.ConnectingSyncStatus()); + libMoneroWallet?.startSyncing(); + } catch (_) { + _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + // TODO log + } + } + _setListener(); + libMoneroWallet?.startListeners(); + libMoneroWallet?.startAutoSaving(); + + unawaited(refresh()); + } + + @Deprecated("Only used in the case of older wallets") + lib_monero_compat.WalletInfo? getLibMoneroWalletInfo( + String walletId, + ) { + try { + return DB.instance.moneroWalletInfoBox.values.firstWhere( + (info) => info.id == lib_monero_compat.hiveIdFor(walletId, compatType), + ); + } catch (_) { + return null; + } + } + + Future save() async { + if (!Platform.isWindows) { + final appRoot = await StackFileSystem.applicationRootDirectory(); + await lib_monero_compat.backupWalletFiles( + name: walletId, + type: compatType, + appRoot: appRoot, + ); + } + await libMoneroWallet!.save(); + } + + Address addressFor({required int index, int account = 0}) { + final address = libMoneroWallet!.getAddress( + accountIndex: account, + addressIndex: index, + ); + + final newReceivingAddress = Address( + walletId: walletId, + derivationIndex: index, + derivationPath: null, + value: address.value, + publicKey: [], + type: AddressType.cryptonote, + subType: AddressSubType.receiving, + ); + + return newReceivingAddress; + } + + Future getKeys() async { + final base = libMoneroWallet; + + final oldInfo = getLibMoneroWalletInfo( + walletId, + ); + if (base == null || (oldInfo != null && oldInfo.name != walletId)) { + return null; + } + + return CWKeyData( + walletId: walletId, + publicViewKey: base.getPublicViewKey(), + privateViewKey: base.getPrivateViewKey(), + publicSpendKey: base.getPublicSpendKey(), + privateSpendKey: base.getPrivateSpendKey(), + ); + } + + Future<(String, String)> + hackToCreateNewViewOnlyWalletDataFromNewlyCreatedWalletThisFunctionShouldNotBeCalledUnlessYouKnowWhatYouAreDoing() async { + final path = await pathForWallet(name: walletId, type: compatType); + final String password; + try { + password = (await secureStorageInterface.read( + key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + ))!; + } catch (e, s) { + throw Exception("Password not found $e, $s"); + } + loadWallet(path: path, password: password); + final wallet = libMoneroWallet!; + return (wallet.getAddress().value, wallet.getPrivateViewKey()); + } + + @override + Future init({bool? isRestore}) async { + final path = await pathForWallet( + name: walletId, + type: compatType, + ); + if (!(walletExists(path)) && isRestore != true) { + try { + final password = generatePassword(); + await secureStorageInterface.write( + key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + value: password, + ); + final wallet = await getCreatedWallet(path: path, password: password); + + final height = wallet.getRefreshFromBlockHeight(); + + await info.updateRestoreHeight( + newRestoreHeight: height, + isar: mainDB.isar, + ); + + // special case for xmr/wow. Normally mnemonic + passphrase is saved + // before wallet.init() is called + await secureStorageInterface.write( + key: Wallet.mnemonicKey(walletId: walletId), + value: wallet.getSeed().trim(), + ); + await secureStorageInterface.write( + key: Wallet.mnemonicPassphraseKey(walletId: walletId), + value: "", + ); + } catch (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Fatal); + } + await updateNode(); + } + + return super.init(); + } + + @override + Future recover({required bool isRescan}) async { + if (isRescan) { + await refreshMutex.protect(() async { + // clear blockchain info + await mainDB.deleteWalletBlockchainData(walletId); + + highestPercentCached = 0; + unawaited(libMoneroWallet?.rescanBlockchain()); + libMoneroWallet?.startSyncing(); + // unawaited(save()); + }); + unawaited(refresh()); + return; + } + + if (isViewOnly) { + await recoverViewOnly(); + return; + } + + await refreshMutex.protect(() async { + final mnemonic = await getMnemonic(); + final seedLength = mnemonic.trim().split(" ").length; + + invalidSeedLengthCheck(seedLength); + + try { + final height = max(info.restoreHeight, 0); + + await info.updateRestoreHeight( + newRestoreHeight: height, + isar: mainDB.isar, + ); + + final String name = walletId; + + final path = await pathForWallet( + name: name, + type: compatType, + ); + + try { + final password = generatePassword(); + await secureStorageInterface.write( + key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + value: password, + ); + final wallet = await getRestoredWallet( + path: path, + password: password, + mnemonic: mnemonic, + height: height, + ); + + if (libMoneroWallet != null) { + await exit(); + } + + libMoneroWallet = wallet; + + _setListener(); + + final newReceivingAddress = await getCurrentReceivingAddress() ?? + Address( + walletId: walletId, + derivationIndex: 0, + derivationPath: null, + value: wallet.getAddress().value, + publicKey: [], + type: AddressType.cryptonote, + subType: AddressSubType.receiving, + ); + + await mainDB.updateOrPutAddresses([newReceivingAddress]); + await info.updateReceivingAddress( + newAddress: newReceivingAddress.value, + isar: mainDB.isar, + ); + } catch (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Fatal); + } + await updateNode(); + _setListener(); + + // libMoneroWallet?.setRecoveringFromSeed(isRecovery: true); + unawaited(libMoneroWallet?.rescanBlockchain()); + libMoneroWallet?.startSyncing(); + + // await save(); + libMoneroWallet?.startListeners(); + libMoneroWallet?.startAutoSaving(); + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from recoverFromMnemonic(): $e\n$s", + level: LogLevel.Error, + ); + rethrow; + } + }); + } + + @override + Future pingCheck() async { + return (await libMoneroWallet?.isConnectedToDaemon()) ?? false; + } + + @override + Future updateNode() async { + final node = getCurrentNode(); + + final host = Uri.parse(node.host).host; + ({InternetAddress host, int port})? proxy; + if (prefs.useTor) { + if (node.clearnetEnabled && !node.torEnabled) { + libMoneroWallet?.stopAutoSaving(); + libMoneroWallet?.stopListeners(); + libMoneroWallet?.stopSyncing(); + _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + throw Exception("TOR - clearnet mismatch"); + } + proxy = TorService.sharedInstance.getProxyInfo(); + } else { + if (!node.clearnetEnabled && node.torEnabled) { + libMoneroWallet?.stopAutoSaving(); + libMoneroWallet?.stopListeners(); + libMoneroWallet?.stopSyncing(); + _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + throw Exception("TOR - clearnet mismatch"); + } + } + + _setSyncStatus(lib_monero_compat.ConnectingSyncStatus()); + try { + if (_requireMutex) { + await _torConnectingLock.protect(() async { + await libMoneroWallet?.connect( + daemonAddress: "$host:${node.port}", + daemonUsername: node.loginName, + daemonPassword: await node.getPassword(secureStorageInterface), + trusted: node.trusted ?? false, + useSSL: node.useSSL, + socksProxyAddress: + proxy == null ? null : "${proxy.host.address}:${proxy.port}", + ); + }); + } else { + await libMoneroWallet?.connect( + daemonAddress: "$host:${node.port}", + daemonUsername: node.loginName, + daemonPassword: await node.getPassword(secureStorageInterface), + trusted: node.trusted ?? false, + useSSL: node.useSSL, + socksProxyAddress: + proxy == null ? null : "${proxy.host.address}:${proxy.port}", + ); + } + libMoneroWallet?.startSyncing(); + libMoneroWallet?.startListeners(); + libMoneroWallet?.startAutoSaving(); + + _setSyncStatus(lib_monero_compat.ConnectedSyncStatus()); + } catch (e, s) { + _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + Logging.instance.log( + "Exception caught in $runtimeType.updateNode(): $e\n$s", + level: LogLevel.Error, + ); + } + + return; + } + + @override + Future updateTransactions() async { + final base = libMoneroWallet; + + if (base == null) { + return; + } + + final transactions = await base.getTxs(refresh: true); + + final allOutputs = await base.getOutputs(includeSpent: true, refresh: true); + + // final cachedTransactions = + // DB.instance.get(boxName: walletId, key: 'latest_tx_model') + // as TransactionData?; + // int latestTxnBlockHeight = + // DB.instance.get(boxName: walletId, key: "storedTxnDataHeight") + // as int? ?? + // 0; + // + // final txidsList = DB.instance + // .get(boxName: walletId, key: "cachedTxids") as List? ?? + // []; + // + // final Set cachedTxids = Set.from(txidsList); + + // TODO: filter to skip cached + confirmed txn processing in next step + // final unconfirmedCachedTransactions = + // cachedTransactions?.getAllTransactions() ?? {}; + // unconfirmedCachedTransactions + // .removeWhere((key, value) => value.confirmedStatus); + // + // if (cachedTransactions != null) { + // for (final tx in allTxHashes.toList(growable: false)) { + // final txHeight = tx["height"] as int; + // if (txHeight > 0 && + // txHeight < latestTxnBlockHeight - MINIMUM_CONFIRMATIONS) { + // if (unconfirmedCachedTransactions[tx["tx_hash"] as String] == null) { + // allTxHashes.remove(tx); + // } + // } + // } + // } + + final List txns = []; + + for (final tx in transactions) { + final associatedOutputs = allOutputs.where((e) => e.hash == tx.hash); + final List inputs = []; + final List outputs = []; + TransactionType type; + if (!tx.isSpend) { + type = TransactionType.incoming; + for (final output in associatedOutputs) { + outputs.add( + OutputV2.isarCantDoRequiredInDefaultConstructor( + scriptPubKeyHex: "", + valueStringSats: output.value.toString(), + addresses: [output.address], + walletOwns: true, + ), + ); + } + } else { + type = TransactionType.outgoing; + for (final output in associatedOutputs) { + inputs.add( + InputV2.isarCantDoRequiredInDefaultConstructor( + scriptSigHex: null, + scriptSigAsm: null, + sequence: null, + outpoint: null, + addresses: [output.address], + valueStringSats: output.value.toString(), + witness: null, + innerRedeemScriptAsm: null, + coinbase: null, + walletOwns: true, + ), + ); + } + } + + final txn = TransactionV2( + walletId: walletId, + blockHash: null, // not exposed via current cs_monero + hash: tx.hash, + txid: tx.hash, + timestamp: (tx.timeStamp.millisecondsSinceEpoch ~/ 1000), + height: tx.blockHeight, + inputs: inputs, + outputs: outputs, + version: -1, // not exposed via current cs_monero + type: type, + subType: TransactionSubType.none, + otherData: jsonEncode({ + TxV2OdKeys.overrideFee: Amount( + rawValue: tx.fee, + fractionDigits: cryptoCurrency.fractionDigits, + ).toJsonString(), + TxV2OdKeys.moneroAmount: Amount( + rawValue: tx.amount, + fractionDigits: cryptoCurrency.fractionDigits, + ).toJsonString(), + TxV2OdKeys.moneroAccountIndex: tx.accountIndex, + TxV2OdKeys.isMoneroTransaction: true, + }), + ); + + txns.add(txn); + } + + await mainDB.updateOrPutTransactionV2s(txns); + } + + Future get availableBalance async { + try { + return Amount( + rawValue: libMoneroWallet!.getUnlockedBalance(), + fractionDigits: cryptoCurrency.fractionDigits, + ); + } catch (_) { + return info.cachedBalance.spendable; + } + } + + Future get totalBalance async { + try { + final full = libMoneroWallet?.getBalance(); + if (full != null) { + return Amount( + rawValue: full, + fractionDigits: cryptoCurrency.fractionDigits, + ); + } else { + final transactions = await libMoneroWallet!.getTxs(refresh: true); + BigInt transactionBalance = BigInt.zero; + for (final tx in transactions) { + if (!tx.isSpend) { + transactionBalance += tx.amount; + } else { + transactionBalance += -tx.amount - tx.fee; + } + } + + return Amount( + rawValue: transactionBalance, + fractionDigits: cryptoCurrency.fractionDigits, + ); + } + } catch (_) { + return info.cachedBalance.total; + } + } + + @override + Future exit() async { + libMoneroWallet?.stopAutoSaving(); + libMoneroWallet?.stopListeners(); + libMoneroWallet?.stopSyncing(); + await libMoneroWallet?.save(); + } + + Future pathForWalletDir({ + required String name, + required lib_monero_compat.WalletType type, + }) async { + final Directory root = await StackFileSystem.applicationRootDirectory(); + return lib_monero_compat.pathForWalletDir( + name: name, + type: type.name.toLowerCase(), + appRoot: root, + ); + } + + Future pathForWallet({ + required String name, + required lib_monero_compat.WalletType type, + }) async => + await pathForWalletDir(name: name, type: type) + .then((path) => '$path/$name'); + + void onSyncingUpdate({ + required int syncHeight, + required int nodeHeight, + String? message, + }) { + if (nodeHeight > 0 && syncHeight >= 0) { + currentKnownChainHeight = nodeHeight; + updateChainHeight(); + final blocksLeft = nodeHeight - syncHeight; + final lib_monero_compat.SyncStatus status; + if (blocksLeft < 100) { + status = lib_monero_compat.SyncedSyncStatus(); + + // if (!_hasSyncAfterStartup) { + // _hasSyncAfterStartup = true; + // await save(); + // } + // + // if (walletInfo.isRecovery!) { + // await setAsRecovered(); + // } + } else { + final percent = syncHeight / currentKnownChainHeight; + + status = lib_monero_compat.SyncingSyncStatus( + blocksLeft, + percent, + currentKnownChainHeight, + ); + } + + _setSyncStatus(status); + _refreshTxDataHelper(); + } + } + + void onBalancesChanged({ + required BigInt newBalance, + required BigInt newUnlockedBalance, + }) { + // do something? + } + + void onNewBlock(int nodeHeight) { + // do something? + } + + final _utxosUpdateLock = Mutex(); + Future onUTXOsChanged(List utxos) async { + await _utxosUpdateLock.protect(() async { + final cwUtxos = await libMoneroWallet?.getOutputs(refresh: true) ?? []; + + // bool changed = false; + + for (final cw in cwUtxos) { + final match = utxos.where( + (e) => + e.keyImage != null && + e.keyImage!.isNotEmpty && + e.keyImage == cw.keyImage, + ); + + if (match.isNotEmpty) { + final u = match.first; + + if (u.isBlocked) { + if (!cw.isFrozen) { + await libMoneroWallet?.freezeOutput(cw.keyImage); + // changed = true; + } + } else { + if (cw.isFrozen) { + await libMoneroWallet?.thawOutput(cw.keyImage); + // changed = true; + } + } + } + } + + // if (changed) { + // await libMoneroWallet?.updateUTXOs(); + // } + }); + } + + void onNewTransaction() { + // TODO: [prio=low] get rid of UpdatedInBackgroundEvent and move to + // adding the v2 tx to the db which would update ui automagically since the + // db is watched by the ui + // call this here? + GlobalEventBus.instance.fire( + UpdatedInBackgroundEvent( + "New data found in $walletId ${info.name} in background!", + walletId, + ), + ); + } + + void syncStatusChanged() async { + final _syncStatus = syncStatus; + + if (_syncStatus != null) { + if (_syncStatus.progress() == 1 && refreshMutex.isLocked) { + refreshMutex.release(); + } + + WalletSyncStatus? status; + xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(true); + + if (_syncStatus is lib_monero_compat.SyncingSyncStatus) { + final int blocksLeft = _syncStatus.blocksLeft; + + // ensure at least 1 to prevent math errors + final int height = max(1, _syncStatus.height); + + final nodeHeight = height + blocksLeft; + currentKnownChainHeight = nodeHeight; + + // final percent = height / nodeHeight; + final percent = _syncStatus.ptc; + + final highest = max(highestPercentCached, percent); + + final unchanged = highest == highestPercentCached; + if (unchanged) { + return; + } + + // update cached + if (highestPercentCached < percent) { + highestPercentCached = percent; + } + + GlobalEventBus.instance.fire( + RefreshPercentChangedEvent( + highest, + walletId, + ), + ); + GlobalEventBus.instance.fire( + BlocksRemainingEvent( + blocksLeft, + walletId, + ), + ); + } else if (_syncStatus is lib_monero_compat.SyncedSyncStatus) { + status = WalletSyncStatus.synced; + } else if (_syncStatus is lib_monero_compat.NotConnectedSyncStatus) { + status = WalletSyncStatus.unableToSync; + xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); + } else if (_syncStatus is lib_monero_compat.StartingSyncStatus) { + status = WalletSyncStatus.syncing; + GlobalEventBus.instance.fire( + RefreshPercentChangedEvent( + highestPercentCached, + walletId, + ), + ); + } else if (_syncStatus is lib_monero_compat.FailedSyncStatus) { + status = WalletSyncStatus.unableToSync; + xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); + } else if (_syncStatus is lib_monero_compat.ConnectingSyncStatus) { + status = WalletSyncStatus.syncing; + GlobalEventBus.instance.fire( + RefreshPercentChangedEvent( + highestPercentCached, + walletId, + ), + ); + } else if (_syncStatus is lib_monero_compat.ConnectedSyncStatus) { + status = WalletSyncStatus.syncing; + GlobalEventBus.instance.fire( + RefreshPercentChangedEvent( + highestPercentCached, + walletId, + ), + ); + } else if (_syncStatus is lib_monero_compat.LostConnectionSyncStatus) { + status = WalletSyncStatus.unableToSync; + xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); + } + + if (status != null) { + GlobalEventBus.instance.fire( + WalletSyncStatusChangedEvent( + status, + walletId, + info.coin, + ), + ); + } + } + } + + @override + Future checkSaveInitialReceivingAddress() async { + // this doesn't work without opening the wallet first which takes a while + } + + // ============ Private ====================================================== + Future _refreshTxDataHelper() async { + if (_txRefreshLock) return; + _txRefreshLock = true; + + final _syncStatus = syncStatus; + + if (_syncStatus != null && + _syncStatus is lib_monero_compat.SyncingSyncStatus) { + final int blocksLeft = _syncStatus.blocksLeft; + final tenKChange = blocksLeft ~/ 10000; + + // only refresh transactions periodically during a sync + if (_lastCheckedHeight == -1 || tenKChange < _lastCheckedHeight) { + _lastCheckedHeight = tenKChange; + await _refreshTxData(); + } + } else { + await _refreshTxData(); + } + + _txRefreshLock = false; + } + + Future _refreshTxData() async { + await updateTransactions(); + final count = await mainDB.getTransactions(walletId).count(); + + if (count > _txCount) { + _txCount = count; + await updateBalance(); + GlobalEventBus.instance.fire( + UpdatedInBackgroundEvent( + "New transaction data found in $walletId ${info.name}!", + walletId, + ), + ); + } + } + + // ============ Overrides ==================================================== + + @override + FilterOperation? get changeAddressFilterOperation => null; + + @override + FilterOperation? get receivingAddressFilterOperation => null; + + @override + Future updateUTXOs() async { + final List outputArray = []; + final utxos = await libMoneroWallet?.getOutputs(refresh: true) ?? + []; + for (final rawUTXO in utxos) { + if (!rawUTXO.spent) { + final current = await mainDB.isar.utxos + .where() + .walletIdEqualTo(walletId) + .filter() + .voutEqualTo(rawUTXO.vout) + .and() + .txidEqualTo(rawUTXO.hash) + .findFirst(); + final tx = await mainDB.isar.transactionV2s + .where() + .walletIdEqualTo(walletId) + .filter() + .txidEqualTo(rawUTXO.hash) + .findFirst(); + + final otherDataMap = { + "keyImage": rawUTXO.keyImage, + "spent": rawUTXO.spent, + }; + + final utxo = UTXO( + address: rawUTXO.address, + walletId: walletId, + txid: rawUTXO.hash, + vout: rawUTXO.vout, + value: rawUTXO.value.toInt(), + name: current?.name ?? "", + isBlocked: current?.isBlocked ?? rawUTXO.isFrozen, + blockedReason: current?.blockedReason ?? "", + isCoinbase: rawUTXO.coinbase, + blockHash: "", + blockHeight: + tx?.height ?? (rawUTXO.height > 0 ? rawUTXO.height : null), + blockTime: tx?.timestamp, + otherData: jsonEncode(otherDataMap), + ); + + outputArray.add(utxo); + } + } + + await mainDB.updateUTXOs(walletId, outputArray); + + return true; + } + + @override + Future updateBalance({bool shouldUpdateUtxos = true}) async { + if (shouldUpdateUtxos) { + await updateUTXOs(); + } + + final total = await totalBalance; + final available = await availableBalance; + + final balance = Balance( + total: total, + spendable: available, + blockedTotal: Amount( + rawValue: BigInt.zero, + fractionDigits: cryptoCurrency.fractionDigits, + ), + pendingSpendable: total - available, + ); + + await info.updateBalance(newBalance: balance, isar: mainDB.isar); + } + + @override + Future refresh() async { + // Awaiting this lock could be dangerous. + // Since refresh is periodic (generally) + if (refreshMutex.isLocked) { + return; + } + + final node = getCurrentNode(); + + if (prefs.useTor) { + if (node.clearnetEnabled && !node.torEnabled) { + libMoneroWallet?.stopAutoSaving(); + libMoneroWallet?.stopListeners(); + libMoneroWallet?.stopSyncing(); + _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + throw Exception("TOR - clearnet mismatch"); + } + } else { + if (!node.clearnetEnabled && node.torEnabled) { + libMoneroWallet?.stopAutoSaving(); + libMoneroWallet?.stopListeners(); + libMoneroWallet?.stopSyncing(); + _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + throw Exception("TOR - clearnet mismatch"); + } + } + + // this acquire should be almost instant due to above check. + // Slight possibility of race but should be irrelevant + await refreshMutex.acquire(); + + libMoneroWallet?.startSyncing(); + _setSyncStatus(lib_monero_compat.StartingSyncStatus()); + + await updateTransactions(); + await updateBalance(); + + if (info.otherData[WalletInfoKeys.reuseAddress] != true) { + await checkReceivingAddressForTransactions(); + } + + if (refreshMutex.isLocked) { + refreshMutex.release(); + } + + final synced = await libMoneroWallet?.isSynced(); + + if (synced == true) { + _setSyncStatus(lib_monero_compat.SyncedSyncStatus()); + } + } + + @override + Future generateNewReceivingAddress() async { + try { + final currentReceiving = await getCurrentReceivingAddress(); + + final newReceivingIndex = + currentReceiving == null ? 0 : currentReceiving.derivationIndex + 1; + + final newReceivingAddress = addressFor(index: newReceivingIndex); + + // Add that new receiving address + await mainDB.putAddress(newReceivingAddress); + await info.updateReceivingAddress( + newAddress: newReceivingAddress.value, + isar: mainDB.isar, + ); + } catch (e, s) { + Logging.instance.log( + "Exception in generateNewAddress(): $e\n$s", + level: LogLevel.Error, + ); + } + } + + @override + Future checkReceivingAddressForTransactions() async { + if (info.otherData[WalletInfoKeys.reuseAddress] == true) { + try { + throw Exception(); + } catch (_, s) { + Logging.instance.log( + "checkReceivingAddressForTransactions called but reuse address flag set: $s", + level: LogLevel.Error, + ); + } + } + + try { + int highestIndex = -1; + final entries = await libMoneroWallet?.getTxs(refresh: true); + if (entries != null) { + for (final element in entries) { + if (!element.isSpend) { + final int curAddressIndex = element.addressIndexes.isEmpty + ? 0 + : element.addressIndexes.reduce(max); + if (curAddressIndex > highestIndex) { + highestIndex = curAddressIndex; + } + } + } + } + + // Check the new receiving index + final currentReceiving = await getCurrentReceivingAddress(); + final curIndex = currentReceiving?.derivationIndex ?? -1; + + if (highestIndex >= curIndex) { + // First increment the receiving index + final newReceivingIndex = curIndex + 1; + + // Use new index to derive a new receiving address + final newReceivingAddress = addressFor(index: newReceivingIndex); + + final existing = await mainDB + .getAddresses(walletId) + .filter() + .valueEqualTo(newReceivingAddress.value) + .findFirst(); + if (existing == null) { + // Add that new change address + await mainDB.putAddress(newReceivingAddress); + } else { + // we need to update the address + await mainDB.updateAddress(existing, newReceivingAddress); + } + if (info.otherData[WalletInfoKeys.reuseAddress] != true) { + // keep checking until address with no tx history is set as current + await checkReceivingAddressForTransactions(); + } + } + } on SocketException catch (se, s) { + Logging.instance.log( + "SocketException caught in _checkReceivingAddressForTransactions(): $se\n$s", + level: LogLevel.Error, + ); + return; + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from _checkReceivingAddressForTransactions(): $e\n$s", + level: LogLevel.Error, + ); + rethrow; + } + } + + // TODO: this needs some work. Prio's may need to be changed as well as estimated blocks + @override + Future get fees async => FeeObject( + numberOfBlocksFast: 10, + numberOfBlocksAverage: 15, + numberOfBlocksSlow: 20, + fast: lib_monero.TransactionPriority.high.value, + medium: lib_monero.TransactionPriority.medium.value, + slow: lib_monero.TransactionPriority.normal.value, + ); + + @override + Future updateChainHeight() async { + await info.updateCachedChainHeight( + newHeight: currentKnownChainHeight, + isar: mainDB.isar, + ); + } + + @override + Future checkChangeAddressForTransactions() async { + // do nothing + } + + @override + Future generateNewChangeAddress() async { + // do nothing + } + + @override + Future prepareSend({required TxData txData}) async { + try { + final feeRate = txData.feeRateType; + if (feeRate is FeeRateType) { + lib_monero.TransactionPriority feePriority; + switch (feeRate) { + case FeeRateType.fast: + feePriority = lib_monero.TransactionPriority.high; + break; + case FeeRateType.average: + feePriority = lib_monero.TransactionPriority.medium; + break; + case FeeRateType.slow: + feePriority = lib_monero.TransactionPriority.normal; + break; + default: + throw ArgumentError("Invalid use of custom fee"); + } + + try { + final bool sweep; + + if (txData.utxos == null) { + final balance = await availableBalance; + sweep = txData.amount! == balance; + } else { + final totalInputsValue = txData.utxos! + .map((e) => e.value) + .fold(BigInt.zero, (p, e) => p + BigInt.from(e)); + sweep = txData.amount!.raw == totalInputsValue; + } + + // TODO: test this one day + // cs_monero may not support this yet properly + if (sweep && txData.recipients!.length > 1) { + throw Exception("Send all not supported with multiple recipients"); + } + + final List outputs = []; + for (final recipient in txData.recipients!) { + final output = lib_monero.Recipient( + address: recipient.address, + amount: recipient.amount.raw, + ); + + outputs.add(output); + } + + if (outputs.isEmpty) { + throw Exception("No recipients provided"); + } + + final height = await chainHeight; + final inputs = txData.utxos + ?.map( + (e) => lib_monero.Output( + address: e.address!, + hash: e.txid, + keyImage: e.keyImage!, + value: BigInt.from(e.value), + isFrozen: e.isBlocked, + isUnlocked: e.blockHeight != null && + (height - (e.blockHeight ?? 0)) >= + cryptoCurrency.minConfirms, + height: e.blockHeight ?? 0, + vout: e.vout, + spent: e.used ?? false, + spentHeight: null, // doesn't matter here + coinbase: e.isCoinbase, + ), + ) + .toList(); + + return await prepareSendMutex.protect(() async { + final lib_monero.PendingTransaction pendingTransaction; + if (outputs.length == 1) { + pendingTransaction = await libMoneroWallet!.createTx( + output: outputs.first, + paymentId: "", + sweep: sweep, + priority: feePriority, + preferredInputs: inputs, + accountIndex: 0, // sw only uses account 0 at this time + ); + } else { + pendingTransaction = await libMoneroWallet!.createTxMultiDest( + outputs: outputs, + paymentId: "", + priority: feePriority, + preferredInputs: inputs, + sweep: sweep, + accountIndex: 0, // sw only uses account 0 at this time + ); + } + + final realFee = Amount( + rawValue: pendingTransaction.fee, + fractionDigits: cryptoCurrency.fractionDigits, + ); + + return txData.copyWith( + fee: realFee, + pendingTransaction: pendingTransaction, + ); + }); + } catch (e) { + rethrow; + } + } else { + throw ArgumentError("Invalid fee rate argument provided!"); + } + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from prepare send(): $e\n$s", + level: LogLevel.Info, + ); + + if (e.toString().contains("Incorrect unlocked balance")) { + throw Exception("Insufficient balance!"); + } else { + throw Exception("Transaction failed with error: $e"); + } + } + } + + @override + Future confirmSend({required TxData txData}) async { + try { + try { + await libMoneroWallet!.commitTx( + txData.pendingTransaction!, + ); + + Logging.instance.log( + "transaction ${txData.pendingTransaction!.txid} has been sent", + level: LogLevel.Info, + ); + return txData.copyWith(txid: txData.pendingTransaction!.txid); + } catch (e, s) { + Logging.instance.log( + "${info.name} ${compatType.name.toLowerCase()} confirmSend: $e\n$s", + level: LogLevel.Error, + ); + rethrow; + } + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from confirmSend(): $e\n$s", + level: LogLevel.Info, + ); + rethrow; + } + } + + // ============== View only ================================================== + + @override + Future recoverViewOnly() async { + await refreshMutex.protect(() async { + final data = + await getViewOnlyWalletData() as CryptonoteViewOnlyWalletData; + + try { + final height = max(info.restoreHeight, 0); + + await info.updateRestoreHeight( + newRestoreHeight: height, + isar: mainDB.isar, + ); + + final String name = walletId; + + final path = await pathForWallet( + name: name, + type: compatType, + ); + + final password = generatePassword(); + await secureStorageInterface.write( + key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + value: password, + ); + final wallet = await getRestoredFromViewKeyWallet( + path: path, + password: password, + address: data.address, + privateViewKey: data.privateViewKey, + height: height, + ); + + if (libMoneroWallet != null) { + await exit(); + } + + libMoneroWallet = wallet; + + _setListener(); + + final newReceivingAddress = await getCurrentReceivingAddress() ?? + Address( + walletId: walletId, + derivationIndex: 0, + derivationPath: null, + value: wallet.getAddress().value, + publicKey: [], + type: AddressType.cryptonote, + subType: AddressSubType.receiving, + ); + + await mainDB.updateOrPutAddresses([newReceivingAddress]); + await info.updateReceivingAddress( + newAddress: newReceivingAddress.value, + isar: mainDB.isar, + ); + + await updateNode(); + _setListener(); + + unawaited(libMoneroWallet?.rescanBlockchain()); + libMoneroWallet?.startSyncing(); + + // await save(); + libMoneroWallet?.startListeners(); + libMoneroWallet?.startAutoSaving(); + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from recoverViewOnly(): $e\n$s", + level: LogLevel.Error, + ); + rethrow; + } + }); + } + + // ============== Private ==================================================== + + StreamSubscription? _torStatusListener; + StreamSubscription? _torPreferenceListener; + + final Mutex _torConnectingLock = Mutex(); + bool _requireMutex = false; +} diff --git a/lib/wallets/wallet/wallet.dart b/lib/wallets/wallet/wallet.dart index 46540eef7..0beaf2a80 100644 --- a/lib/wallets/wallet/wallet.dart +++ b/lib/wallets/wallet/wallet.dart @@ -7,6 +7,7 @@ import 'package:mutex/mutex.dart'; import '../../db/isar/main_db.dart'; import '../../models/isar/models/blockchain_data/address.dart'; import '../../models/isar/models/ethereum/eth_contract.dart'; +import '../../models/keys/view_only_wallet_data.dart'; import '../../models/node_model.dart'; import '../../models/paymint/fee_object_model.dart'; import '../../services/event_bus/events/global/node_connection_status_changed_event.dart'; @@ -28,6 +29,7 @@ import 'impl/banano_wallet.dart'; import 'impl/bitcoin_frost_wallet.dart'; import 'impl/bitcoin_wallet.dart'; import 'impl/bitcoincash_wallet.dart'; +import 'impl/cardano_wallet.dart'; import 'impl/dash_wallet.dart'; import 'impl/dogecoin_wallet.dart'; import 'impl/ecash_wallet.dart'; @@ -53,6 +55,7 @@ import 'wallet_mixin_interfaces/multi_address_interface.dart'; import 'wallet_mixin_interfaces/paynym_interface.dart'; import 'wallet_mixin_interfaces/private_key_interface.dart'; import 'wallet_mixin_interfaces/spark_interface.dart'; +import 'wallet_mixin_interfaces/view_only_option_interface.dart'; abstract class Wallet { // default to Transaction class. For TransactionV2 set to 2 @@ -144,7 +147,13 @@ abstract class Wallet { String? mnemonic, String? mnemonicPassphrase, String? privateKey, + ViewOnlyWalletData? viewOnlyData, }) async { + // TODO: rework soon? + if (walletInfo.isViewOnly && viewOnlyData == null) { + throw Exception("Missing view key while creating view only wallet!"); + } + final Wallet wallet = await _construct( walletInfo: walletInfo, mainDB: mainDB, @@ -153,7 +162,12 @@ abstract class Wallet { prefs: prefs, ); - if (wallet is MnemonicInterface) { + if (wallet is ViewOnlyOptionInterface && walletInfo.isViewOnly) { + await secureStorageInterface.write( + key: getViewOnlyWalletDataSecStoreKey(walletId: walletInfo.walletId), + value: viewOnlyData!.toJsonEncodedString(), + ); + } else if (wallet is MnemonicInterface) { if (wallet is CryptonoteWallet) { // currently a special case due to the xmr/wow libraries handling their // own mnemonic generation on new wallet creation @@ -278,6 +292,12 @@ abstract class Wallet { }) => "${walletId}_privateKey"; + // secure storage key + static String getViewOnlyWalletDataSecStoreKey({ + required String walletId, + }) => + "${walletId}_viewOnlyWalletData"; + //============================================================================ // ========== Private ======================================================== @@ -324,6 +344,9 @@ abstract class Wallet { case const (Bitcoincash): return BitcoincashWallet(net); + case const (Cardano): + return CardanoWallet(net); + case const (Dash): return DashWallet(net); @@ -407,6 +430,17 @@ abstract class Wallet { ); _isConnected = hasNetwork; + + if (status == NodeConnectionStatus.disconnected) { + GlobalEventBus.instance.fire( + WalletSyncStatusChangedEvent( + WalletSyncStatus.unableToSync, + walletId, + cryptoCurrency, + ), + ); + } + if (hasNetwork) { unawaited(refresh()); } @@ -472,6 +506,61 @@ abstract class Wallet { // Should fire events Future refresh() async { + final refreshCompleter = Completer(); + final future = refreshCompleter.future.then( + (_) { + GlobalEventBus.instance.fire( + WalletSyncStatusChangedEvent( + WalletSyncStatus.synced, + walletId, + cryptoCurrency, + ), + ); + + if (shouldAutoSync) { + _periodicRefreshTimer ??= + Timer.periodic(const Duration(seconds: 150), (timer) async { + // chain height check currently broken + // if ((await chainHeight) != (await storedChainHeight)) { + + // TODO: [prio=med] some kind of quick check if wallet needs to refresh to replace the old refreshIfThereIsNewData call + // if (await refreshIfThereIsNewData()) { + unawaited(refresh()); + + // } + // } + }); + } + }, + onError: (Object error, StackTrace strace) { + GlobalEventBus.instance.fire( + NodeConnectionStatusChangedEvent( + NodeConnectionStatus.disconnected, + walletId, + cryptoCurrency, + ), + ); + GlobalEventBus.instance.fire( + WalletSyncStatusChangedEvent( + WalletSyncStatus.unableToSync, + walletId, + cryptoCurrency, + ), + ); + Logging.instance.log( + "Caught exception in refreshWalletData(): $error\n$strace", + level: LogLevel.Error, + ); + }, + ); + + unawaited(_refresh(refreshCompleter)); + + return future; + } + + // Should fire events + Future _refresh(Completer completer) async { // Awaiting this lock could be dangerous. // Since refresh is periodic (generally) if (refreshMutex.isLocked) { @@ -479,6 +568,25 @@ abstract class Wallet { } final start = DateTime.now(); + bool tAlive = true; + final t = Timer.periodic(const Duration(seconds: 1), (timer) async { + if (tAlive) { + final pingSuccess = await pingCheck(); + if (!pingSuccess) { + tAlive = false; + } + } else { + timer.cancel(); + } + }); + + void _checkAlive() { + if (!tAlive) throw Exception("refresh alive ping failure"); + } + + final viewOnly = this is ViewOnlyOptionInterface && + (this as ViewOnlyOptionInterface).isViewOnly; + try { // this acquire should be almost instant due to above check. // Slight possibility of race but should be irrelevant @@ -492,132 +600,137 @@ abstract class Wallet { ), ); + _checkAlive(); + // add some small buffer before making calls. // this can probably be removed in the future but was added as a // debugging feature await Future.delayed(const Duration(milliseconds: 300)); + _checkAlive(); // TODO: [prio=low] handle this differently. Extra modification of this file for coin specific functionality should be avoided. final Set codesToCheck = {}; - if (this is PaynymInterface) { + _checkAlive(); + if (this is PaynymInterface && !viewOnly) { // isSegwit does not matter here at all final myCode = await (this as PaynymInterface).getPaymentCode(isSegwit: false); + _checkAlive(); final nym = await PaynymIsApi().nym(myCode.toString()); + _checkAlive(); if (nym.value != null) { for (final follower in nym.value!.followers) { codesToCheck.add(follower.code); } + _checkAlive(); for (final following in nym.value!.following) { codesToCheck.add(following.code); } } + _checkAlive(); } GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.0, walletId)); + _checkAlive(); await updateChainHeight(); + _checkAlive(); + if (this is BitcoinFrostWallet) { + await (this as BitcoinFrostWallet).lookAhead(); + } + _checkAlive(); + GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.1, walletId)); + _checkAlive(); // TODO: [prio=low] handle this differently. Extra modification of this file for coin specific functionality should be avoided. if (this is MultiAddressInterface) { if (info.otherData[WalletInfoKeys.reuseAddress] != true) { await (this as MultiAddressInterface) .checkReceivingAddressForTransactions(); } + _checkAlive(); } GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.2, walletId)); + _checkAlive(); // TODO: [prio=low] handle this differently. Extra modification of this file for coin specific functionality should be avoided. if (this is MultiAddressInterface) { - await (this as MultiAddressInterface) - .checkChangeAddressForTransactions(); + if (info.otherData[WalletInfoKeys.reuseAddress] != true) { + await (this as MultiAddressInterface) + .checkChangeAddressForTransactions(); + } } + _checkAlive(); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.3, walletId)); - if (this is SparkInterface) { + if (this is SparkInterface && !viewOnly) { // this should be called before updateTransactions() await (this as SparkInterface).refreshSparkData(); } + _checkAlive(); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.50, walletId)); + _checkAlive(); final fetchFuture = updateTransactions(); + _checkAlive(); final utxosRefreshFuture = updateUTXOs(); // if (currentHeight != storedHeight) { GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.60, walletId)); + _checkAlive(); await utxosRefreshFuture; GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.70, walletId)); + _checkAlive(); await fetchFuture; // TODO: [prio=low] handle this differently. Extra modification of this file for coin specific functionality should be avoided. - if (this is PaynymInterface && codesToCheck.isNotEmpty) { + if (!viewOnly && this is PaynymInterface && codesToCheck.isNotEmpty) { + _checkAlive(); await (this as PaynymInterface) .checkForNotificationTransactionsTo(codesToCheck); // check utxos again for notification outputs + _checkAlive(); await updateUTXOs(); } + _checkAlive(); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.80, walletId)); // await getAllTxsToWatch(); + _checkAlive(); // TODO: [prio=low] handle this differently. Extra modification of this file for coin specific functionality should be avoided. - if (this is LelantusInterface) { + if (this is LelantusInterface && !viewOnly) { if (info.otherData[WalletInfoKeys.enableLelantusScanning] as bool? ?? false) { await (this as LelantusInterface).refreshLelantusData(); + _checkAlive(); } } GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.90, walletId)); + _checkAlive(); await updateBalance(); + _checkAlive(); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(1.0, walletId)); - GlobalEventBus.instance.fire( - WalletSyncStatusChangedEvent( - WalletSyncStatus.synced, - walletId, - cryptoCurrency, - ), - ); - if (shouldAutoSync) { - _periodicRefreshTimer ??= - Timer.periodic(const Duration(seconds: 150), (timer) async { - // chain height check currently broken - // if ((await chainHeight) != (await storedChainHeight)) { + tAlive = false; // interrupt timer as its not needed anymore - // TODO: [prio=med] some kind of quick check if wallet needs to refresh to replace the old refreshIfThereIsNewData call - // if (await refreshIfThereIsNewData()) { - unawaited(refresh()); - - // } - // } - }); - } + completer.complete(); } catch (error, strace) { - GlobalEventBus.instance.fire( - NodeConnectionStatusChangedEvent( - NodeConnectionStatus.disconnected, - walletId, - cryptoCurrency, - ), - ); - GlobalEventBus.instance.fire( - WalletSyncStatusChangedEvent( - WalletSyncStatus.unableToSync, - walletId, - cryptoCurrency, - ), - ); - Logging.instance.log( - "Caught exception in refreshWalletData(): $error\n$strace", - level: LogLevel.Error, - ); + completer.completeError(error, strace); } finally { + t.cancel(); refreshMutex.release(); + if (!completer.isCompleted) { + completer.completeError( + "finally block hit before completer completed", + StackTrace.current, + ); + } Logging.instance.log( "Refresh for " diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart index 36cd710fd..f72ec36b2 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart @@ -1,7 +1,7 @@ -import '../../crypto_currency/intermediate/bip39_hd_currency.dart'; -import '../intermediate/bip39_hd_wallet.dart'; +import '../../crypto_currency/crypto_currency.dart'; +import '../wallet.dart'; -mixin CoinControlInterface on Bip39HDWallet { +mixin CoinControlInterface on Wallet { // any required here? // currently only used to id which wallets support coin control } diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart deleted file mode 100644 index 661b10bd3..000000000 --- a/lib/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart +++ /dev/null @@ -1,443 +0,0 @@ -import 'dart:async'; -import 'dart:io'; -import 'dart:math'; - -import 'package:cw_core/monero_transaction_priority.dart'; -import 'package:cw_core/sync_status.dart'; -import 'package:cw_core/transaction_direction.dart'; -import 'package:cw_core/wallet_base.dart'; -import 'package:cw_core/wallet_service.dart'; -import 'package:cw_core/wallet_type.dart'; -import 'package:flutter_libmonero/core/key_service.dart'; -import 'package:isar/isar.dart'; -import 'package:mutex/mutex.dart'; - -import '../../../models/balance.dart'; -import '../../../models/isar/models/blockchain_data/address.dart'; -import '../../../models/keys/cw_key_data.dart'; -import '../../../models/paymint/fee_object_model.dart'; -import '../../../services/event_bus/events/global/blocks_remaining_event.dart'; -import '../../../services/event_bus/events/global/refresh_percent_changed_event.dart'; -import '../../../services/event_bus/events/global/updated_in_background_event.dart'; -import '../../../services/event_bus/events/global/wallet_sync_status_changed_event.dart'; -import '../../../services/event_bus/global_event_bus.dart'; -import '../../../utilities/amount/amount.dart'; -import '../../../utilities/logger.dart'; -import '../../../utilities/stack_file_system.dart'; -import '../../crypto_currency/intermediate/cryptonote_currency.dart'; -import '../../isar/models/wallet_info.dart'; -import '../intermediate/cryptonote_wallet.dart'; -import 'multi_address_interface.dart'; - -mixin CwBasedInterface on CryptonoteWallet - implements MultiAddressInterface { - final prepareSendMutex = Mutex(); - final estimateFeeMutex = Mutex(); - - KeyService? _cwKeysStorageCached; - KeyService get cwKeysStorage => - _cwKeysStorageCached ??= KeyService(secureStorageInterface); - - static WalletService? cwWalletService; - static WalletBase? cwWalletBase; - - bool _hasCalledExit = false; - bool _txRefreshLock = false; - int _lastCheckedHeight = -1; - int _txCount = 0; - int currentKnownChainHeight = 0; - double highestPercentCached = 0; - - Timer? autoSaveTimer; - Future pathForWalletDir({ - required String name, - required WalletType type, - }) async { - final Directory root = await StackFileSystem.applicationRootDirectory(); - - final prefix = walletTypeToString(type).toLowerCase(); - final walletsDir = Directory('${root.path}/wallets'); - final walletDire = Directory('${walletsDir.path}/$prefix/$name'); - - if (!walletDire.existsSync()) { - walletDire.createSync(recursive: true); - } - - return walletDire.path; - } - - Future pathForWallet({ - required String name, - required WalletType type, - }) async => - await pathForWalletDir(name: name, type: type) - .then((path) => '$path/$name'); - - void onNewBlock({required int height, required int blocksLeft}) { - currentKnownChainHeight = height; - updateChainHeight(); - _refreshTxDataHelper(); - } - - void onNewTransaction() { - // TODO: [prio=low] get rid of UpdatedInBackgroundEvent and move to - // adding the v2 tx to the db which would update ui automagically since the - // db is watched by the ui - // call this here? - GlobalEventBus.instance.fire( - UpdatedInBackgroundEvent( - "New data found in $walletId ${info.name} in background!", - walletId, - ), - ); - } - - void syncStatusChanged() async { - final syncStatus = cwWalletBase?.syncStatus; - if (syncStatus != null) { - if (syncStatus.progress() == 1 && refreshMutex.isLocked) { - refreshMutex.release(); - } - - WalletSyncStatus? status; - xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(true); - - if (syncStatus is SyncingSyncStatus) { - final int blocksLeft = syncStatus.blocksLeft; - - // ensure at least 1 to prevent math errors - final int height = max(1, syncStatus.height); - - final nodeHeight = height + blocksLeft; - currentKnownChainHeight = nodeHeight; - - final percent = height / nodeHeight; - - final highest = max(highestPercentCached, percent); - - // update cached - if (highestPercentCached < percent) { - highestPercentCached = percent; - } - - GlobalEventBus.instance.fire( - RefreshPercentChangedEvent( - highest, - walletId, - ), - ); - GlobalEventBus.instance.fire( - BlocksRemainingEvent( - blocksLeft, - walletId, - ), - ); - } else if (syncStatus is SyncedSyncStatus) { - status = WalletSyncStatus.synced; - } else if (syncStatus is NotConnectedSyncStatus) { - status = WalletSyncStatus.unableToSync; - xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); - } else if (syncStatus is StartingSyncStatus) { - status = WalletSyncStatus.syncing; - GlobalEventBus.instance.fire( - RefreshPercentChangedEvent( - highestPercentCached, - walletId, - ), - ); - } else if (syncStatus is FailedSyncStatus) { - status = WalletSyncStatus.unableToSync; - xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); - } else if (syncStatus is ConnectingSyncStatus) { - status = WalletSyncStatus.syncing; - GlobalEventBus.instance.fire( - RefreshPercentChangedEvent( - highestPercentCached, - walletId, - ), - ); - } else if (syncStatus is ConnectedSyncStatus) { - status = WalletSyncStatus.syncing; - GlobalEventBus.instance.fire( - RefreshPercentChangedEvent( - highestPercentCached, - walletId, - ), - ); - } else if (syncStatus is LostConnectionSyncStatus) { - status = WalletSyncStatus.unableToSync; - xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); - } - - if (status != null) { - GlobalEventBus.instance.fire( - WalletSyncStatusChangedEvent( - status, - walletId, - info.coin, - ), - ); - } - } - } - - @override - Future checkSaveInitialReceivingAddress() async { - // this doesn't work without opening the wallet first which takes a while - } - - // ============ Interface ==================================================== - - Future get availableBalance; - Future get totalBalance; - - Future exitCwWallet(); - - Future open(); - - Address addressFor({required int index, int account = 0}); - - Future getKeys(); - - // ============ Private ====================================================== - Future _refreshTxDataHelper() async { - if (_txRefreshLock) return; - _txRefreshLock = true; - - final syncStatus = cwWalletBase?.syncStatus; - - if (syncStatus != null && syncStatus is SyncingSyncStatus) { - final int blocksLeft = syncStatus.blocksLeft; - final tenKChange = blocksLeft ~/ 10000; - - // only refresh transactions periodically during a sync - if (_lastCheckedHeight == -1 || tenKChange < _lastCheckedHeight) { - _lastCheckedHeight = tenKChange; - await _refreshTxData(); - } - } else { - await _refreshTxData(); - } - - _txRefreshLock = false; - } - - Future _refreshTxData() async { - await updateTransactions(); - final count = await mainDB.getTransactions(walletId).count(); - - if (count > _txCount) { - _txCount = count; - await updateBalance(); - GlobalEventBus.instance.fire( - UpdatedInBackgroundEvent( - "New transaction data found in $walletId ${info.name}!", - walletId, - ), - ); - } - } - - // ============ Overrides ==================================================== - - @override - FilterOperation? get changeAddressFilterOperation => null; - - @override - FilterOperation? get receivingAddressFilterOperation => null; - - @override - Future updateBalance() async { - final total = await totalBalance; - final available = await availableBalance; - - final balance = Balance( - total: total, - spendable: available, - blockedTotal: Amount( - rawValue: BigInt.zero, - fractionDigits: cryptoCurrency.fractionDigits, - ), - pendingSpendable: total - available, - ); - - await info.updateBalance(newBalance: balance, isar: mainDB.isar); - } - - @override - Future refresh() async { - // Awaiting this lock could be dangerous. - // Since refresh is periodic (generally) - if (refreshMutex.isLocked) { - return; - } - - // this acquire should be almost instant due to above check. - // Slight possibility of race but should be irrelevant - await refreshMutex.acquire(); - - GlobalEventBus.instance.fire( - WalletSyncStatusChangedEvent( - WalletSyncStatus.syncing, - walletId, - info.coin, - ), - ); - - await updateTransactions(); - await updateBalance(); - - if (info.otherData[WalletInfoKeys.reuseAddress] != true) { - await checkReceivingAddressForTransactions(); - } - - if (cwWalletBase?.syncStatus is SyncedSyncStatus) { - refreshMutex.release(); - GlobalEventBus.instance.fire( - WalletSyncStatusChangedEvent( - WalletSyncStatus.synced, - walletId, - info.coin, - ), - ); - } - } - - static Mutex exitMutex = Mutex(); - - @override - Future exit() async { - if (!_hasCalledExit) { - await exitMutex.protect(() async { - _hasCalledExit = true; - autoSaveTimer?.cancel(); - await exitCwWallet(); - cwWalletBase?.close(); - cwWalletBase = null; - cwWalletService = null; - }); - } - } - - @override - Future generateNewReceivingAddress() async { - try { - final currentReceiving = await getCurrentReceivingAddress(); - - final newReceivingIndex = - currentReceiving == null ? 0 : currentReceiving.derivationIndex + 1; - - final newReceivingAddress = addressFor(index: newReceivingIndex); - - // Add that new receiving address - await mainDB.putAddress(newReceivingAddress); - await info.updateReceivingAddress( - newAddress: newReceivingAddress.value, - isar: mainDB.isar, - ); - } catch (e, s) { - Logging.instance.log( - "Exception in generateNewAddress(): $e\n$s", - level: LogLevel.Error, - ); - } - } - - @override - Future checkReceivingAddressForTransactions() async { - if (info.otherData[WalletInfoKeys.reuseAddress] == true) { - try { - throw Exception(); - } catch (_, s) { - Logging.instance.log( - "checkReceivingAddressForTransactions called but reuse address flag set: $s", - level: LogLevel.Error, - ); - } - } - - try { - int highestIndex = -1; - final entries = cwWalletBase?.transactionHistory?.transactions?.entries; - if (entries != null) { - for (final element in entries) { - if (element.value.direction == TransactionDirection.incoming) { - final int curAddressIndex = - element.value.additionalInfo!['addressIndex'] as int; - if (curAddressIndex > highestIndex) { - highestIndex = curAddressIndex; - } - } - } - } - - // Check the new receiving index - final currentReceiving = await getCurrentReceivingAddress(); - final curIndex = currentReceiving?.derivationIndex ?? -1; - - if (highestIndex >= curIndex) { - // First increment the receiving index - final newReceivingIndex = curIndex + 1; - - // Use new index to derive a new receiving address - final newReceivingAddress = addressFor(index: newReceivingIndex); - - final existing = await mainDB - .getAddresses(walletId) - .filter() - .valueEqualTo(newReceivingAddress.value) - .findFirst(); - if (existing == null) { - // Add that new change address - await mainDB.putAddress(newReceivingAddress); - } else { - // we need to update the address - await mainDB.updateAddress(existing, newReceivingAddress); - } - if (info.otherData[WalletInfoKeys.reuseAddress] != true) { - // keep checking until address with no tx history is set as current - await checkReceivingAddressForTransactions(); - } - } - } on SocketException catch (se, s) { - Logging.instance.log( - "SocketException caught in _checkReceivingAddressForTransactions(): $se\n$s", - level: LogLevel.Error, - ); - return; - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from _checkReceivingAddressForTransactions(): $e\n$s", - level: LogLevel.Error, - ); - rethrow; - } - } - - @override - Future get fees async => FeeObject( - numberOfBlocksFast: 10, - numberOfBlocksAverage: 15, - numberOfBlocksSlow: 20, - fast: MoneroTransactionPriority.fast.raw!, - medium: MoneroTransactionPriority.regular.raw!, - slow: MoneroTransactionPriority.slow.raw!, - ); - @override - Future updateChainHeight() async { - await info.updateCachedChainHeight( - newHeight: currentKnownChainHeight, - isar: mainDB.isar, - ); - } - - @override - Future checkChangeAddressForTransactions() async { - // do nothing - } - - @override - Future generateNewChangeAddress() async { - // do nothing - } -} diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart index c31f7cd4a..cc49149d9 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart @@ -13,6 +13,7 @@ import '../../../models/isar/models/blockchain_data/v2/input_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/output_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; import '../../../models/isar/models/isar_models.dart'; +import '../../../models/keys/view_only_wallet_data.dart'; import '../../../models/paymint/fee_object_model.dart'; import '../../../models/signing_data.dart'; import '../../../utilities/amount/amount.dart'; @@ -32,9 +33,10 @@ import '../intermediate/bip39_hd_wallet.dart'; import 'cpfp_interface.dart'; import 'paynym_interface.dart'; import 'rbf_interface.dart'; +import 'view_only_option_interface.dart'; mixin ElectrumXInterface - on Bip39HDWallet { + on Bip39HDWallet implements ViewOnlyOptionInterface { late ElectrumXClient electrumXClient; late CachedElectrumXClient electrumXCachedClient; @@ -105,6 +107,7 @@ mixin ElectrumXInterface required TxData txData, required bool coinControl, required bool isSendAll, + required bool isSendAllCoinControlUtxos, int additionalOutputs = 0, List? utxos, }) async { @@ -136,7 +139,10 @@ mixin ElectrumXInterface (e.used != true) && (canCPFP || e.isConfirmed( - currentChainHeight, cryptoCurrency.minConfirms)), + currentChainHeight, + cryptoCurrency.minConfirms, + cryptoCurrency.minCoinbaseConfirms, + )), ) .toList(); final spendableSatoshiValue = @@ -144,7 +150,9 @@ mixin ElectrumXInterface if (spendableSatoshiValue < satoshiAmountToSend) { throw Exception("Insufficient balance"); - } else if (spendableSatoshiValue == satoshiAmountToSend && !isSendAll) { + } else if (spendableSatoshiValue == satoshiAmountToSend && + !isSendAll && + !isSendAllCoinControlUtxos) { throw Exception("Insufficient balance to pay transaction fee"); } @@ -220,7 +228,13 @@ mixin ElectrumXInterface // gather required signing data final utxoSigningData = await fetchBuildTxData(utxoObjectsToUse); - if (isSendAll) { + if (isSendAll || isSendAllCoinControlUtxos) { + if (satoshiAmountToSend != satoshisBeingUsed) { + throw Exception( + "Something happened that should never actually happen. " + "Please report this error to the developers.", + ); + } return await _sendAllBuilder( txData: txData, recipientAddress: recipientAddress, @@ -357,6 +371,7 @@ mixin ElectrumXInterface additionalOutputs: additionalOutputs + 1, utxos: utxos, coinControl: coinControl, + isSendAllCoinControlUtxos: isSendAllCoinControlUtxos, ); } throw Exception("Insufficient balance to pay transaction fee"); @@ -827,6 +842,11 @@ mixin ElectrumXInterface Future fetchChainHeight({int retries = 1}) async { try { + // Ensure server version is initialized and genesis hash is checked. + if (_serverVersion == null) { + await _initializeServerVersionAndCheckGenesisHash(); + } + return await ClientManager.sharedInstance.getChainHeightFor( cryptoCurrency, ); @@ -886,6 +906,8 @@ mixin ElectrumXInterface name: node.name, useSSL: node.useSSL, id: node.id, + torEnabled: node.torEnabled, + clearnetEnabled: node.clearnetEnabled, ); } @@ -899,6 +921,8 @@ mixin ElectrumXInterface name: e.name, id: e.id, useSSL: e.useSSL, + torEnabled: e.torEnabled, + clearnetEnabled: e.clearnetEnabled, ), ) .toList(); @@ -929,7 +953,7 @@ mixin ElectrumXInterface Future<({List
addresses, int index})> checkGapsBatched( int txCountBatchSize, - coinlib.HDPrivateKey root, + coinlib.HDKey node, DerivePathType type, int chain, ) async { @@ -954,7 +978,14 @@ mixin ElectrumXInterface index: index + j, ); - final keys = root.derivePath(derivePath); + final coinlib.HDKey keys; + if (isViewOnly) { + final idx = derivePath.lastIndexOf("'/"); + final path = derivePath.substring(idx + 2); + keys = node.derivePath(path); + } else { + keys = node.derivePath(derivePath); + } final addressData = cryptoCurrency.getAddressForPublicKey( publicKey: keys.publicKey, @@ -971,7 +1002,8 @@ mixin ElectrumXInterface publicKey: keys.publicKey.data, type: addressData.addressType, derivationIndex: index + j, - derivationPath: DerivationPath()..value = derivePath, + derivationPath: + isViewOnly ? null : (DerivationPath()..value = derivePath), subType: chain == 0 ? AddressSubType.receiving : AddressSubType.change, ); @@ -1010,13 +1042,14 @@ mixin ElectrumXInterface } Future<({List
addresses, int index})> checkGapsLinearly( - coinlib.HDPrivateKey root, + coinlib.HDKey node, DerivePathType type, int chain, ) async { final List
addressArray = []; int gapCounter = 0; int index = 0; + for (; gapCounter < cryptoCurrency.maxUnusedAddressGap; index++) { Logging.instance.log( "index: $index, \t GapCounter chain=$chain ${type.name}: $gapCounter", @@ -1028,7 +1061,16 @@ mixin ElectrumXInterface chain: chain, index: index, ); - final keys = root.derivePath(derivePath); + + final coinlib.HDKey keys; + if (isViewOnly) { + final idx = derivePath.lastIndexOf("'/"); + final path = derivePath.substring(idx + 2); + keys = node.derivePath(path); + } else { + keys = node.derivePath(derivePath); + } + final addressData = cryptoCurrency.getAddressForPublicKey( publicKey: keys.publicKey, derivePathType: type, @@ -1044,7 +1086,8 @@ mixin ElectrumXInterface publicKey: keys.publicKey.data, type: addressData.addressType, derivationIndex: index, - derivationPath: DerivationPath()..value = derivePath, + derivationPath: + isViewOnly ? null : (DerivationPath()..value = derivePath), subType: chain == 0 ? AddressSubType.receiving : AddressSubType.change, ); @@ -1151,6 +1194,9 @@ mixin ElectrumXInterface cryptoCurrency: cryptoCurrency, ); + final inputs = txn["vin"] as List? ?? []; + final isCoinbase = inputs.any((e) => (e as Map?)?["coinbase"] != null); + final vout = jsonUTXO["tx_pos"] as int; final outputs = txn["vout"] as List; @@ -1182,7 +1228,10 @@ mixin ElectrumXInterface name: checkBlockResult.utxoLabel ?? "", isBlocked: checkBlockResult.blocked, blockedReason: checkBlockResult.blockedReason, - isCoinbase: txn["is_coinbase"] as bool? ?? false, + isCoinbase: txn["is_coinbase"] as bool? ?? + txn["is-coinbase"] as bool? ?? + txn["iscoinbase"] as bool? ?? + isCoinbase, blockHash: txn["blockhash"] as String?, blockHeight: jsonUTXO["height"] as int?, blockTime: txn["blocktime"] as int?, @@ -1316,6 +1365,10 @@ mixin ElectrumXInterface @override Future checkReceivingAddressForTransactions() async { + if (isViewOnly && viewOnlyType == ViewOnlyWalletType.addressOnly) { + return; + } + if (info.otherData[WalletInfoKeys.reuseAddress] == true) { try { throw Exception(); @@ -1367,6 +1420,21 @@ mixin ElectrumXInterface @override Future checkChangeAddressForTransactions() async { + if (isViewOnly && viewOnlyType == ViewOnlyWalletType.addressOnly) { + return; + } + + if (info.otherData[WalletInfoKeys.reuseAddress] == true) { + try { + throw Exception(); + } catch (_, s) { + Logging.instance.log( + "checkChangeAddressForTransactions called but reuse address flag set: $s", + level: LogLevel.Error, + ); + } + } + try { final currentChange = await getCurrentChangeAddress(); @@ -1404,6 +1472,11 @@ mixin ElectrumXInterface @override Future recover({required bool isRescan}) async { + if (isViewOnly) { + await recoverViewOnly(isRescan: isRescan); + return; + } + final root = await getRootHDNode(); final List addresses})>> receiveFutures = @@ -1560,7 +1633,7 @@ mixin ElectrumXInterface ); } catch (e, s) { Logging.instance.log( - "Failed to check paynym.is followers/following for history during " + "Failed to check ${PaynymIsApi.baseURL} followers/following for history during " "bitcoin wallet ($walletId ${info.name}) " "_recoverWalletFromBIP32SeedPhrase: $e/n$s", level: LogLevel.Error, @@ -1681,11 +1754,23 @@ mixin ElectrumXInterface @override Future prepareSend({required TxData txData}) async { try { + if (txData.amount == null) { + throw Exception("No recipients in attempted transaction!"); + } + final feeRateType = txData.feeRateType; final customSatsPerVByte = txData.satsPerVByte; final feeRateAmount = txData.feeRateAmount; final utxos = txData.utxos; + final bool coinControl = utxos != null; + + final isSendAllCoinControlUtxos = coinControl && + txData.amount!.raw == + utxos + .map((e) => e.value) + .fold(BigInt.zero, (p, e) => p + BigInt.from(e)); + if (customSatsPerVByte != null) { // check for send all bool isSendAll = false; @@ -1694,8 +1779,6 @@ mixin ElectrumXInterface isSendAll = true; } - final bool coinControl = utxos != null; - if (coinControl && this is CpfpInterface && txData.amount == @@ -1709,6 +1792,7 @@ mixin ElectrumXInterface isSendAll: isSendAll, utxos: utxos?.toList(), coinControl: coinControl, + isSendAllCoinControlUtxos: isSendAllCoinControlUtxos, ); Logging.instance @@ -1750,8 +1834,6 @@ mixin ElectrumXInterface isSendAll = true; } - final bool coinControl = utxos != null; - final result = await coinSelection( txData: txData.copyWith( feeRateAmount: rate, @@ -1759,6 +1841,7 @@ mixin ElectrumXInterface isSendAll: isSendAll, utxos: utxos?.toList(), coinControl: coinControl, + isSendAllCoinControlUtxos: isSendAllCoinControlUtxos, ); Logging.instance.log("prepare send: $result", level: LogLevel.Info); @@ -1783,6 +1866,21 @@ mixin ElectrumXInterface @override Future init() async { + try { + // Server features and genesis hash check deferred. + // See _initializeServerVersionAndCheckGenesisHash. + + await super.init(); + } catch (e, s) { + // do nothing, still allow user into wallet + Logging.instance.log( + "$runtimeType init() did not complete: $e\n$s", + level: LogLevel.Warning, + ); + } + } + + Future _initializeServerVersionAndCheckGenesisHash() async { try { final features = await electrumXClient .getServerFeatures() @@ -1794,17 +1892,14 @@ mixin ElectrumXInterface _parseServerVersion(features["server_version"] as String); if (cryptoCurrency.genesisHash != features['genesis_hash']) { - throw Exception("genesis hash does not match!"); + throw Exception("Genesis hash does not match!"); } } catch (e, s) { - // do nothing, still allow user into wallet Logging.instance.log( - "$runtimeType init() did not complete: $e\n$s", + "$runtimeType _initializeServerVersionAndCheckGenesisHash() did not complete: $e\n$s", level: LogLevel.Warning, ); } - - await super.init(); } // =========================================================================== @@ -1836,6 +1931,7 @@ mixin ElectrumXInterface e.isConfirmed( info.cachedChainHeight, cryptoCurrency.minConfirms, + cryptoCurrency.minCoinbaseConfirms, ), ) .toList(); @@ -1881,5 +1977,219 @@ mixin ElectrumXInterface return address; } + // ============== View only ================================================== + + @override + Future recoverViewOnly({bool isRescan = false}) async { + final data = await getViewOnlyWalletData(); + + final coinlib.HDKey? root; + if (data is AddressViewOnlyWalletData) { + root = null; + } else { + if ((data as ExtendedKeysViewOnlyWalletData).xPubs.length != 1) { + throw Exception( + "Only single xpub view only wallets are currently supported", + ); + } + + root = coinlib.HDPublicKey.decode(data.xPubs.first.encoded); + } + + final List addresses})>> receiveFutures = + []; + final List addresses})>> changeFutures = + []; + + const receiveChain = 0; + const changeChain = 1; + + const txCountBatchSize = 12; + + try { + await refreshMutex.protect(() async { + if (isRescan) { + // clear cache + await electrumXCachedClient.clearSharedTransactionCache( + cryptoCurrency: info.coin, + ); + // clear blockchain info + await mainDB.deleteWalletBlockchainData(walletId); + } + + final List
addressesToStore = []; + + if (root != null) { + // receiving addresses + Logging.instance.log( + "checking receiving addresses...", + level: LogLevel.Info, + ); + + final canBatch = await serverCanBatch; + + for (final type in cryptoCurrency.supportedDerivationPathTypes) { + final path = cryptoCurrency.constructDerivePath( + derivePathType: type, + chain: 0, + index: 0, + ); + if (path.startsWith( + (data as ExtendedKeysViewOnlyWalletData).xPubs.first.path, + )) { + receiveFutures.add( + canBatch + ? checkGapsBatched( + txCountBatchSize, + root, + type, + receiveChain, + ) + : checkGapsLinearly( + root, + type, + receiveChain, + ), + ); + } + } + + // change addresses + Logging.instance.log( + "checking change addresses...", + level: LogLevel.Info, + ); + for (final type in cryptoCurrency.supportedDerivationPathTypes) { + final path = cryptoCurrency.constructDerivePath( + derivePathType: type, + chain: 0, + index: 0, + ); + if (path.startsWith( + (data as ExtendedKeysViewOnlyWalletData).xPubs.first.path, + )) { + changeFutures.add( + canBatch + ? checkGapsBatched( + txCountBatchSize, + root, + type, + changeChain, + ) + : checkGapsLinearly( + root, + type, + changeChain, + ), + ); + } + } + + // io limitations may require running these linearly instead + final futuresResult = await Future.wait([ + Future.wait(receiveFutures), + Future.wait(changeFutures), + ]); + + final receiveResults = futuresResult[0]; + final changeResults = futuresResult[1]; + + int highestReceivingIndexWithHistory = 0; + + for (final tuple in receiveResults) { + if (tuple.addresses.isEmpty) { + await checkReceivingAddressForTransactions(); + } else { + highestReceivingIndexWithHistory = max( + tuple.index, + highestReceivingIndexWithHistory, + ); + addressesToStore.addAll(tuple.addresses); + } + } + + int highestChangeIndexWithHistory = 0; + // If restoring a wallet that never sent any funds with change, then set changeArray + // manually. If we didn't do this, it'd store an empty array. + for (final tuple in changeResults) { + if (tuple.addresses.isEmpty) { + await checkChangeAddressForTransactions(); + } else { + highestChangeIndexWithHistory = max( + tuple.index, + highestChangeIndexWithHistory, + ); + addressesToStore.addAll(tuple.addresses); + } + } + + // remove extra addresses to help minimize risk of creating a large gap + addressesToStore.removeWhere( + (e) => + e.subType == AddressSubType.change && + e.derivationIndex > highestChangeIndexWithHistory, + ); + addressesToStore.removeWhere( + (e) => + e.subType == AddressSubType.receiving && + e.derivationIndex > highestReceivingIndexWithHistory, + ); + } else { + final clAddress = coinlib.Address.fromString( + (data as AddressViewOnlyWalletData).address, + cryptoCurrency.networkParams, + ); + + final AddressType addressType; + switch (clAddress.runtimeType) { + case const (coinlib.P2PKHAddress): + addressType = AddressType.p2pkh; + break; + + case const (coinlib.P2SHAddress): + addressType = AddressType.p2sh; + break; + + case const (coinlib.P2WPKHAddress): + addressType = AddressType.p2wpkh; + break; + + case const (coinlib.P2TRAddress): + addressType = AddressType.p2tr; + break; + + default: + throw Exception( + "Unsupported address type: ${clAddress.runtimeType}", + ); + } + + addressesToStore.add( + Address( + walletId: walletId, + value: clAddress.toString(), + publicKey: [], + derivationIndex: -1, + derivationPath: null, + type: addressType, + subType: AddressSubType.receiving, + ), + ); + } + + await mainDB.updateOrPutAddresses(addressesToStore); + }); + + unawaited(refresh()); + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from electrumx_mixin recoverViewOnly(): $e\n$s", + level: LogLevel.Info, + ); + + rethrow; + } + } + // =========================================================================== } diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart index 1606b1295..affb6d7de 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart @@ -2,35 +2,44 @@ import '../../../models/keys/xpriv_data.dart'; import '../../crypto_currency/interfaces/electrumx_currency_interface.dart'; import 'electrumx_interface.dart'; -typedef XPub = ({String path, String xpub}); -typedef XPriv = ({String path, String xpriv}); +abstract class XKey { + XKey({required this.path}); + final String path; + + @override + String toString() => "Path: $path"; +} + +class XPub extends XKey { + XPub({required super.path, required this.encoded}); + final String encoded; + + @override + String toString() => "XPub { path: $path, encoded: $encoded }"; +} + +class XPriv extends XKey { + XPriv({required super.path, required this.encoded}); + final String encoded; + + @override + String toString() => "XPriv { path: $path, encoded: $encoded }"; +} mixin ExtendedKeysInterface on ElectrumXInterface { Future<({List xpubs, String fingerprint})> getXPubs() async { - final paths = cryptoCurrency.supportedDerivationPathTypes.map( - (e) => ( - path: e, - addressType: e.getAddressType(), - ), - ); + final paths = cryptoCurrency.supportedHardenedDerivationPaths; final master = await getRootHDNode(); final fingerprint = master.fingerprint.toRadixString(16); - final futures = paths.map((e) async { - String path = cryptoCurrency.constructDerivePath( - derivePathType: e.path, - chain: 0, - index: 0, - ); - // trim chain and address index - path = path.substring(0, path.lastIndexOf("'") + 1); + final futures = paths.map((path) async { final node = master.derivePath(path); - return ( + return XPub( path: path, - xpub: node.hdPublicKey.encode( + encoded: node.hdPublicKey.encode( cryptoCurrency.networkParams.pubHDPrefix, // 0x04b24746, ), @@ -44,29 +53,17 @@ mixin ExtendedKeysInterface } Future getXPrivs() async { - final paths = cryptoCurrency.supportedDerivationPathTypes.map( - (e) => ( - path: e, - addressType: e.getAddressType(), - ), - ); + final paths = cryptoCurrency.supportedHardenedDerivationPaths; final master = await getRootHDNode(); final fingerprint = master.fingerprint.toRadixString(16); - final futures = paths.map((e) async { - String path = cryptoCurrency.constructDerivePath( - derivePathType: e.path, - chain: 0, - index: 0, - ); - // trim chain and address index - path = path.substring(0, path.lastIndexOf("'") + 1); + final futures = paths.map((path) async { final node = master.derivePath(path); - return ( + return XPriv( path: path, - xpriv: node.encode( + encoded: node.encode( cryptoCurrency.networkParams.privHDPrefix, ), ); @@ -76,9 +73,9 @@ mixin ExtendedKeysInterface walletId: walletId, fingerprint: fingerprint, xprivs: [ - ( + XPriv( path: "Master", - xpriv: master.encode( + encoded: master.encode( cryptoCurrency.networkParams.privHDPrefix, ), ), diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart index 7116f6c7d..3d57f3c36 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart @@ -925,6 +925,7 @@ mixin LelantusInterface if (availableOutputs[i].isConfirmed( currentChainHeight, cryptoCurrency.minConfirms, + cryptoCurrency.minCoinbaseConfirms, ) == true && !(availableOutputs[i].isCoinbase && diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/nano_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/nano_interface.dart index 39d16497e..e08bc3b45 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/nano_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/nano_interface.dart @@ -5,6 +5,7 @@ import 'package:isar/isar.dart'; import 'package:nanodart/nanodart.dart'; import 'package:tuple/tuple.dart'; +import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart'; import '../../../external_api_keys.dart'; import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; @@ -18,6 +19,7 @@ import '../../../services/tor_service.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/extensions/impl/string.dart'; import '../../../utilities/logger.dart'; +import '../../../utilities/tor_plain_net_option_enum.dart'; import '../../crypto_currency/intermediate/nano_currency.dart'; import '../../models/tx_data.dart'; import '../intermediate/bip39_wallet.dart'; @@ -47,6 +49,7 @@ mixin NanoInterface on Bip39Wallet { final _httpClient = HTTP(); Future _requestWork(String hash) async { + _hackedCheckTorNodePrefs(); return _httpClient .post( url: Uri.parse(_kWorkServer), // this should be a @@ -104,6 +107,7 @@ mixin NanoInterface on Bip39Wallet { String amountRaw, String publicAddress, ) async { + _hackedCheckTorNodePrefs(); // TODO: the opening block of an account is a special case bool openBlock = false; @@ -223,6 +227,7 @@ mixin NanoInterface on Bip39Wallet { } Future _confirmAllReceivable(String accountAddress) async { + _hackedCheckTorNodePrefs(); final node = getCurrentNode(); final receivableResponse = await _httpClient.post( url: Uri.parse(node.host), @@ -254,6 +259,7 @@ mixin NanoInterface on Bip39Wallet { //========= public =========================================================== Future getCurrentRepresentative() async { + _hackedCheckTorNodePrefs(); final serverURI = Uri.parse(getCurrentNode().host); final address = (_cachedAddress ?? await getCurrentReceivingAddress())!.value; @@ -272,6 +278,7 @@ mixin NanoInterface on Bip39Wallet { Future changeRepresentative(String newRepresentative) async { try { + _hackedCheckTorNodePrefs(); final node = getCurrentNode(); final serverURI = Uri.parse(node.host); await updateBalance(); @@ -347,6 +354,11 @@ mixin NanoInterface on Bip39Wallet { @override Future pingCheck() async { + try { + _hackedCheckTorNodePrefs(); + } catch (_) { + return false; + } final node = getCurrentNode(); final uri = Uri.parse(node.host); final response = await _httpClient.post( @@ -365,6 +377,7 @@ mixin NanoInterface on Bip39Wallet { @override Future prepareSend({required TxData txData}) async { + _hackedCheckTorNodePrefs(); if (txData.recipients!.length != 1) { throw ArgumentError( "${cryptoCurrency.runtimeType} currently only " @@ -383,6 +396,7 @@ mixin NanoInterface on Bip39Wallet { @override Future confirmSend({required TxData txData}) async { try { + _hackedCheckTorNodePrefs(); // our address: final String publicAddress = (_cachedAddress ?? await getCurrentReceivingAddress())!.value; @@ -483,6 +497,7 @@ mixin NanoInterface on Bip39Wallet { @override Future recover({required bool isRescan}) async { try { + _hackedCheckTorNodePrefs(); await refreshMutex.protect(() async { if (isRescan) { await mainDB.deleteWalletBlockchainData(walletId); @@ -505,6 +520,7 @@ mixin NanoInterface on Bip39Wallet { String? previous, Map? data, ) async { + _hackedCheckTorNodePrefs(); final node = getCurrentNode(); final body = { "action": "account_history", @@ -543,6 +559,7 @@ mixin NanoInterface on Bip39Wallet { @override Future updateTransactions() async { + _hackedCheckTorNodePrefs(); await updateChainHeight(); final receivingAddress = (_cachedAddress ?? await getCurrentReceivingAddress())!; @@ -613,6 +630,7 @@ mixin NanoInterface on Bip39Wallet { @override Future updateBalance() async { try { + _hackedCheckTorNodePrefs(); final addressString = (_cachedAddress ??= (await getCurrentReceivingAddress())!).value; final body = jsonEncode({ @@ -661,6 +679,7 @@ mixin NanoInterface on Bip39Wallet { @override Future updateChainHeight() async { try { + _hackedCheckTorNodePrefs(); final String publicAddress = (_cachedAddress ??= (await getCurrentReceivingAddress())!).value; @@ -724,4 +743,26 @@ mixin NanoInterface on Bip39Wallet { medium: 0, slow: 0, ); + + void _hackedCheckTorNodePrefs() { + final node = getCurrentNode(); + final netOption = TorPlainNetworkOption.fromNodeData( + node.torEnabled, + node.clearnetEnabled, + ); + + if (prefs.useTor) { + if (netOption == TorPlainNetworkOption.clear) { + throw NodeTorMismatchConfigException( + message: "TOR enabled but node set to clearnet only", + ); + } + } else { + if (netOption == TorPlainNetworkOption.tor) { + throw NodeTorMismatchConfigException( + message: "TOR off but node set to TOR only", + ); + } + } + } } diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart index f86e76d0d..b20638362 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart @@ -479,6 +479,7 @@ mixin PaynymInterface availableOutputs[i].isConfirmed( await fetchChainHeight(), cryptoCurrency.minConfirms, + cryptoCurrency.minCoinbaseConfirms, ) == true) { spendableOutputs.add(availableOutputs[i]); diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/rbf_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/rbf_interface.dart index c71fa4c36..80bff1a7a 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/rbf_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/rbf_interface.dart @@ -121,6 +121,7 @@ mixin RbfInterface (e) => !e.isConfirmed( height, cryptoCurrency.minConfirms, + cryptoCurrency.minCoinbaseConfirms, ), ); diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart index 09d4dc6f1..977a95124 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart @@ -23,6 +23,7 @@ import '../../isar/models/spark_coin.dart'; import '../../isar/models/wallet_info.dart'; import '../../models/tx_data.dart'; import '../intermediate/bip39_hd_wallet.dart'; +import 'cpfp_interface.dart'; import 'electrumx_interface.dart'; const kDefaultSparkIndex = 1; @@ -183,14 +184,75 @@ mixin SparkInterface } Future estimateFeeForSpark(Amount amount) async { - // int spendAmount = amount.raw.toInt(); - // if (spendAmount == 0) { - return Amount( - rawValue: BigInt.from(0), - fractionDigits: cryptoCurrency.fractionDigits, - ); - // } - // TODO actual fee estimation + final spendAmount = amount.raw.toInt(); + if (spendAmount == 0) { + return Amount( + rawValue: BigInt.from(0), + fractionDigits: cryptoCurrency.fractionDigits, + ); + } else { + // fetch spendable spark coins + final coins = await mainDB.isar.sparkCoins + .where() + .walletIdEqualToAnyLTagHash(walletId) + .filter() + .isUsedEqualTo(false) + .and() + .heightIsNotNull() + .and() + .not() + .valueIntStringEqualTo("0") + .findAll(); + + final available = + coins.map((e) => e.value).fold(BigInt.zero, (p, e) => p + e); + + if (amount.raw > available) { + return Amount( + rawValue: BigInt.from(0), + fractionDigits: cryptoCurrency.fractionDigits, + ); + } + + // prepare coin data for ffi + final serializedCoins = coins + .map( + (e) => ( + serializedCoin: e.serializedCoinB64!, + serializedCoinContext: e.contextB64!, + groupId: e.groupId, + height: e.height!, + ), + ) + .toList(); + + final root = await getRootHDNode(); + final String derivationPath; + if (cryptoCurrency.network.isTestNet) { + derivationPath = "$kSparkBaseDerivationPathTestnet$kDefaultSparkIndex"; + } else { + derivationPath = "$kSparkBaseDerivationPath$kDefaultSparkIndex"; + } + final privateKey = root.derivePath(derivationPath).privateKey.data; + int estimate = await _asyncSparkFeesWrapper( + privateKeyHex: privateKey.toHex, + index: kDefaultSparkIndex, + sendAmount: spendAmount, + subtractFeeFromAmount: true, + serializedCoins: serializedCoins, + // privateRecipientsCount: (txData.sparkRecipients?.length ?? 0), + privateRecipientsCount: 1, // ROUGHLY! + ); + + if (estimate < 0) { + estimate = 0; + } + + return Amount( + rawValue: BigInt.from(estimate), + fractionDigits: cryptoCurrency.fractionDigits, + ); + } } /// Spark to Spark/Transparent (spend) creation @@ -374,7 +436,7 @@ mixin SparkInterface recipientCount + (txData.sparkRecipients?.length ?? 0); final BigInt estimatedFee; if (isSendAll) { - final estFee = LibSpark.estimateSparkFee( + final estFee = await _asyncSparkFeesWrapper( privateKeyHex: privateKey.toHex, index: kDefaultSparkIndex, sendAmount: txAmount.raw.toInt(), @@ -1688,6 +1750,7 @@ mixin SparkInterface (e) => !e.isConfirmed( currentHeight, cryptoCurrency.minConfirms, + cryptoCurrency.minCoinbaseConfirms, ), ); @@ -1748,36 +1811,48 @@ mixin SparkInterface throw Exception("Attempted send of zero amount"); } + final utxos = txData.utxos; + final bool coinControl = utxos != null; + + final utxosTotal = coinControl + ? utxos + .map((e) => e.value) + .fold(BigInt.zero, (p, e) => p + BigInt.from(e)) + : null; + + if (coinControl && utxosTotal! < total) { + throw Exception("Insufficient selected UTXOs!"); + } + + final isSendAllCoinControlUtxos = coinControl && total == utxosTotal; + final currentHeight = await chainHeight; - // coin control not enabled for firo currently so we can ignore this - // final utxosToUse = txData.utxos?.toList() ?? await mainDB.isar.utxos - // .where() - // .walletIdEqualTo(walletId) - // .filter() - // .isBlockedEqualTo(false) - // .and() - // .group((q) => q.usedEqualTo(false).or().usedIsNull()) - // .and() - // .valueGreaterThan(0) - // .findAll(); - final spendableUtxos = await mainDB.isar.utxos - .where() - .walletIdEqualTo(walletId) - .filter() - .isBlockedEqualTo(false) - .and() - .group((q) => q.usedEqualTo(false).or().usedIsNull()) - .and() - .valueGreaterThan(0) - .findAll(); + final availableOutputs = utxos?.toList() ?? + await mainDB.isar.utxos + .where() + .walletIdEqualTo(walletId) + .filter() + .isBlockedEqualTo(false) + .and() + .group((q) => q.usedEqualTo(false).or().usedIsNull()) + .and() + .valueGreaterThan(0) + .findAll(); - spendableUtxos.removeWhere( - (e) => !e.isConfirmed( - currentHeight, - cryptoCurrency.minConfirms, - ), - ); + final canCPFP = this is CpfpInterface && coinControl; + + final spendableUtxos = availableOutputs + .where( + (e) => + canCPFP || + e.isConfirmed( + currentHeight, + cryptoCurrency.minConfirms, + cryptoCurrency.minCoinbaseConfirms, + ), + ) + .toList(); if (spendableUtxos.isEmpty) { throw Exception("No available UTXOs found to anonymize"); @@ -1788,7 +1863,9 @@ mixin SparkInterface .reduce((value, element) => value += element); final bool subtractFeeFromAmount; - if (available < total) { + if (isSendAllCoinControlUtxos) { + subtractFeeFromAmount = true; + } else if (available < total) { throw Exception("Insufficient balance"); } else if (available == total) { subtractFeeFromAmount = true; @@ -2002,3 +2079,53 @@ class MutableSparkRecipient { return 'MutableSparkRecipient{ address: $address, value: $value, memo: $memo }'; } } + +typedef SerializedCoinData = ({ + int groupId, + int height, + String serializedCoin, + String serializedCoinContext +}); + +Future _asyncSparkFeesWrapper({ + required String privateKeyHex, + int index = 1, + required int sendAmount, + required bool subtractFeeFromAmount, + required List serializedCoins, + required int privateRecipientsCount, +}) async { + return await compute( + _estSparkFeeComputeFunc, + ( + privateKeyHex: privateKeyHex, + index: index, + sendAmount: sendAmount, + subtractFeeFromAmount: subtractFeeFromAmount, + serializedCoins: serializedCoins, + privateRecipientsCount: privateRecipientsCount, + ), + ); +} + +int _estSparkFeeComputeFunc( + ({ + String privateKeyHex, + int index, + int sendAmount, + bool subtractFeeFromAmount, + List serializedCoins, + int privateRecipientsCount, + }) args, +) { + final est = LibSpark.estimateSparkFee( + privateKeyHex: args.privateKeyHex, + index: args.index, + sendAmount: args.sendAmount, + subtractFeeFromAmount: args.subtractFeeFromAmount, + serializedCoins: args.serializedCoins, + privateRecipientsCount: args.privateRecipientsCount, + ); + + return est; +} diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart new file mode 100644 index 000000000..4989ce0d0 --- /dev/null +++ b/lib/wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart @@ -0,0 +1,27 @@ +import '../../../models/keys/view_only_wallet_data.dart'; +import '../../crypto_currency/interfaces/view_only_option_currency_interface.dart'; +import '../wallet.dart'; + +mixin ViewOnlyOptionInterface + on Wallet { + ViewOnlyWalletType get viewOnlyType => info.viewOnlyWalletType!; + + bool get isViewOnly => info.isViewOnly; + + Future recoverViewOnly(); + + Future getViewOnlyWalletData() async { + if (!isViewOnly) { + throw Exception("This is not a view only wallet"); + } + + final encoded = await secureStorageInterface.read( + key: Wallet.getViewOnlyWalletDataSecStoreKey(walletId: walletId), + ); + + return ViewOnlyWalletData.fromJsonEncodedString( + encoded!, + walletId: walletId, + ); + } +} diff --git a/lib/widgets/churning/churn_progress_item.dart b/lib/widgets/churning/churn_progress_item.dart new file mode 100644 index 000000000..8e989bc08 --- /dev/null +++ b/lib/widgets/churning/churn_progress_item.dart @@ -0,0 +1,98 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +import '../../pages/settings_views/global_settings_view/stack_backup_views/sub_widgets/restoring_item_card.dart'; +import '../../services/churning_service.dart'; +import '../../themes/stack_colors.dart'; +import '../../utilities/assets.dart'; +import '../../utilities/text_styles.dart'; +import '../../utilities/util.dart'; +import '../conditional_parent.dart'; +import '../rounded_container.dart'; + +class ProgressItem extends StatelessWidget { + const ProgressItem({ + super.key, + required this.iconAsset, + required this.label, + required this.status, + this.error, + }); + + final String iconAsset; + final String label; + final ChurnStatus status; + final Object? error; + + Widget _getIconForState(ChurnStatus status, BuildContext context) { + switch (status) { + case ChurnStatus.waiting: + return SvgPicture.asset( + Assets.svg.loader, + color: + Theme.of(context).extension()!.buttonBackSecondary, + ); + case ChurnStatus.running: + return SvgPicture.asset( + Assets.svg.loader, + color: Theme.of(context).extension()!.accentColorGreen, + ); + case ChurnStatus.success: + return SvgPicture.asset( + Assets.svg.checkCircle, + color: Theme.of(context).extension()!.accentColorGreen, + ); + case ChurnStatus.failed: + return SvgPicture.asset( + Assets.svg.circleAlert, + color: Theme.of(context).extension()!.textError, + ); + } + } + + @override + Widget build(BuildContext context) { + return ConditionalParent( + condition: Util.isDesktop, + builder: (child) => RoundedContainer( + padding: EdgeInsets.zero, + color: Theme.of(context).extension()!.popupBG, + borderColor: Theme.of(context).extension()!.background, + child: child, + ), + child: RestoringItemCard( + left: SizedBox( + width: 32, + height: 32, + child: RoundedContainer( + padding: const EdgeInsets.all(0), + color: + Theme.of(context).extension()!.buttonBackSecondary, + child: Center( + child: SvgPicture.asset( + iconAsset, + width: 18, + height: 18, + color: Theme.of(context).extension()!.textDark, + ), + ), + ), + ), + right: SizedBox( + width: 20, + height: 20, + child: _getIconForState(status, context), + ), + title: label, + subTitle: error != null + ? Text( + error!.toString(), + style: STextStyles.w500_12(context).copyWith( + color: Theme.of(context).extension()!.textError, + ), + ) + : null, + ), + ); + } +} diff --git a/lib/widgets/custom_buttons/checkbox_text_button.dart b/lib/widgets/custom_buttons/checkbox_text_button.dart index 29e6c5ada..6a451335b 100644 --- a/lib/widgets/custom_buttons/checkbox_text_button.dart +++ b/lib/widgets/custom_buttons/checkbox_text_button.dart @@ -1,18 +1,31 @@ import 'package:flutter/material.dart'; + import '../../utilities/text_styles.dart'; class CheckboxTextButton extends StatefulWidget { - const CheckboxTextButton({super.key, required this.label, this.onChanged}); + const CheckboxTextButton({ + super.key, + required this.label, + this.onChanged, + this.initialValue = false, + }); final String label; final void Function(bool)? onChanged; + final bool initialValue; @override State createState() => _CheckboxTextButtonState(); } class _CheckboxTextButtonState extends State { - bool _value = false; + late bool _value; + + @override + void initState() { + super.initState(); + _value = widget.initialValue; + } @override Widget build(BuildContext context) { diff --git a/lib/widgets/custom_buttons/draggable_switch_button.dart b/lib/widgets/custom_buttons/draggable_switch_button.dart index 47e706704..064746c6f 100644 --- a/lib/widgets/custom_buttons/draggable_switch_button.dart +++ b/lib/widgets/custom_buttons/draggable_switch_button.dart @@ -77,6 +77,7 @@ class DraggableSwitchButtonState extends State { _enabled = widget.enabled; valueListener = _isOn ? ValueNotifier(1.0) : ValueNotifier(0.0); + widget.controller?.isOn = () => _isOn; widget.controller?.activate = () { _isOn = !_isOn; // widget.onValueChanged?.call(_isOn); @@ -212,4 +213,5 @@ class DraggableSwitchButtonState extends State { class DSBController { VoidCallback? activate; + bool Function()? isOn; } diff --git a/lib/widgets/custom_buttons/paynym_follow_toggle_button.dart b/lib/widgets/custom_buttons/paynym_follow_toggle_button.dart index 5bc6143d1..daa87415c 100644 --- a/lib/widgets/custom_buttons/paynym_follow_toggle_button.dart +++ b/lib/widgets/custom_buttons/paynym_follow_toggle_button.dart @@ -34,6 +34,8 @@ enum PaynymFollowToggleButtonStyle { detailsDesktop, } +const kDisableFollowing = true; + class PaynymFollowToggleButton extends ConsumerStatefulWidget { const PaynymFollowToggleButton({ super.key, @@ -55,7 +57,7 @@ class _PaynymFollowToggleButtonState extends ConsumerState { final isDesktop = Util.isDesktop; - Future follow() async { + Future _follow() async { bool loadingPopped = false; unawaited( showDialog( @@ -160,7 +162,7 @@ class _PaynymFollowToggleButtonState } } - Future unfollow() async { + Future _unfollow() async { bool loadingPopped = false; unawaited( showDialog( @@ -264,9 +266,9 @@ class _PaynymFollowToggleButtonState if (!_lock) { _lock = true; if (isFollowing) { - await unfollow(); + await _unfollow(); } else { - await follow(); + await _follow(); } _lock = false; } @@ -291,7 +293,7 @@ class _PaynymFollowToggleButtonState width: isDesktop ? 120 : 100, buttonHeight: isDesktop ? ButtonHeight.s : ButtonHeight.xl, label: isFollowing ? "Unfollow" : "Follow", - onPressed: _onPressed, + onPressed: kDisableFollowing ? null : _onPressed, ); case PaynymFollowToggleButtonStyle.detailsPopup: @@ -306,7 +308,7 @@ class _PaynymFollowToggleButtonState color: Theme.of(context).extension()!.buttonTextSecondary, ), - onPressed: _onPressed, + onPressed: kDisableFollowing ? null : _onPressed, ); case PaynymFollowToggleButtonStyle.detailsDesktop: @@ -321,7 +323,7 @@ class _PaynymFollowToggleButtonState Theme.of(context).extension()!.buttonTextSecondary, ), iconSpacing: 6, - onPressed: _onPressed, + onPressed: kDisableFollowing ? null : _onPressed, ); } } diff --git a/lib/widgets/desktop/desktop_fee_dialog.dart b/lib/widgets/desktop/desktop_fee_dialog.dart index 90304e80c..322fbb876 100644 --- a/lib/widgets/desktop/desktop_fee_dialog.dart +++ b/lib/widgets/desktop/desktop_fee_dialog.dart @@ -1,6 +1,7 @@ -import 'package:cw_core/monero_transaction_priority.dart'; import 'package:flutter/material.dart'; +import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../models/models.dart'; import '../../pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart'; import '../../pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart'; @@ -61,7 +62,7 @@ class _DesktopFeeDialogState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.fast.raw!, + lib_monero.TransactionPriority.high.value, ); ref.read(feeSheetSessionCacheProvider).fast[amount] = fee; } else if (coin is Firo) { @@ -111,7 +112,7 @@ class _DesktopFeeDialogState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.regular.raw!, + lib_monero.TransactionPriority.medium.value, ); ref.read(feeSheetSessionCacheProvider).average[amount] = fee; } else if (coin is Firo) { @@ -161,7 +162,7 @@ class _DesktopFeeDialogState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.slow.raw!, + lib_monero.TransactionPriority.normal.value, ); ref.read(feeSheetSessionCacheProvider).slow[amount] = fee; } else if (coin is Firo) { diff --git a/lib/widgets/desktop/qr_code_scanner_dialog.dart b/lib/widgets/desktop/qr_code_scanner_dialog.dart new file mode 100644 index 000000000..edb6a074a --- /dev/null +++ b/lib/widgets/desktop/qr_code_scanner_dialog.dart @@ -0,0 +1,401 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:camera_linux/camera_linux.dart'; +import 'package:camera_macos/camera_macos_arguments.dart'; +import 'package:camera_macos/camera_macos_device.dart'; +import 'package:camera_macos/camera_macos_platform_interface.dart'; +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:camera_windows/camera_windows.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:image/image.dart' as img; +import 'package:zxing2/qrcode.dart'; + +import '../../notifications/show_flush_bar.dart'; +import '../../utilities/assets.dart'; +import '../../utilities/logger.dart'; +import '../../utilities/text_styles.dart'; +import 'desktop_dialog.dart'; +import 'desktop_dialog_close_button.dart'; +import 'primary_button.dart'; +import 'secondary_button.dart'; + +class QrCodeScannerDialog extends StatefulWidget { + final Function(String) onQrCodeDetected; + + QrCodeScannerDialog({ + required this.onQrCodeDetected, + }); + + @override + _QrCodeScannerDialogState createState() => _QrCodeScannerDialogState(); +} + +class _QrCodeScannerDialogState extends State { + final CameraLinux? _cameraLinuxPlugin = + Platform.isLinux ? CameraLinux() : null; + final CameraWindows? _cameraWindowsPlugin = + Platform.isWindows ? CameraWindows() : null; + bool _isCameraOpen = false; + Image? _image; + bool _isScanning = false; + int _cameraId = -1; + String? _macOSDeviceId; + final int _imageDelayInMs = 250; + + @override + void initState() { + super.initState(); + _isCameraOpen = false; + _isScanning = false; + _initializeCamera(); + } + + @override + void dispose() { + _stopCamera(); + super.dispose(); + } + + Future _initializeCamera() async { + try { + setState(() { + _isScanning = true; // Show the progress indicator + }); + + if (Platform.isLinux && _cameraLinuxPlugin != null) { + await _cameraLinuxPlugin!.initializeCamera(); + Logging.instance.log("Linux Camera initialized", level: LogLevel.Info); + } else if (Platform.isWindows && _cameraWindowsPlugin != null) { + final List cameras = + await _cameraWindowsPlugin!.availableCameras(); + if (cameras.isEmpty) { + throw CameraException('No cameras available', 'No cameras found.'); + } + final CameraDescription camera = cameras[0]; // Could be user-selected. + _cameraId = await _cameraWindowsPlugin!.createCameraWithSettings( + camera, + const MediaSettings( + resolutionPreset: ResolutionPreset.low, + fps: 4, + videoBitrate: 200000, + enableAudio: false, + ), + ); + await _cameraWindowsPlugin!.initializeCamera(_cameraId); + // await _cameraWindowsPlugin!.onCameraInitialized(_cameraId).first; + // TODO [prio=low]: Make this work. ^^^ + Logging.instance.log("Windows Camera initialized with ID: $_cameraId", + level: LogLevel.Info); + } else if (Platform.isMacOS) { + final List videoDevices = await CameraMacOS.instance + .listDevices(deviceType: CameraMacOSDeviceType.video); + if (videoDevices.isEmpty) { + throw Exception('No cameras available'); + } + _macOSDeviceId = videoDevices.first.deviceId; + await CameraMacOS.instance + .initialize(cameraMacOSMode: CameraMacOSMode.photo); + + setState(() { + _isCameraOpen = true; + }); + + Logging.instance.log( + "macOS Camera initialized with ID: $_macOSDeviceId", + level: LogLevel.Info); + } + if (mounted) { + setState(() { + _isCameraOpen = true; + _isScanning = true; + }); + } + unawaited(_captureAndScanImage()); // Could be awaited. + } catch (e, s) { + Logging.instance + .log("Failed to initialize camera: $e\n$s", level: LogLevel.Error); + if (mounted) { + // widget.onSnackbar("Failed to initialize camera. Please try again."); + setState(() { + _isScanning = false; + }); + } + } + } + + Future _stopCamera() async { + try { + if (Platform.isLinux && _cameraLinuxPlugin != null) { + _cameraLinuxPlugin!.stopCamera(); + Logging.instance.log("Linux Camera stopped", level: LogLevel.Info); + } else if (Platform.isWindows && _cameraWindowsPlugin != null) { + // if (_cameraId >= 0) { + await _cameraWindowsPlugin!.dispose(_cameraId); + Logging.instance.log("Windows Camera stopped with ID: $_cameraId", + level: LogLevel.Info); + // } else { + // Logging.instance.log("Windows Camera ID is null. Cannot dispose.", + // level: LogLevel.Error); + // } + } else if (Platform.isMacOS) { + // if (_macOSDeviceId != null) { + await CameraMacOS.instance.stopImageStream(); + Logging.instance.log("macOS Camera stopped with ID: $_macOSDeviceId", + level: LogLevel.Info); + // } else { + // Logging.instance.log("macOS Camera ID is null. Cannot stop.", + // level: LogLevel.Error); + // } + } + } catch (e, s) { + Logging.instance + .log("Failed to stop camera: $e\n$s", level: LogLevel.Error); + } finally { + if (mounted) { + setState(() { + _isScanning = false; + _isCameraOpen = false; + }); + } + } + } + + Future _captureAndScanImage() async { + while (_isCameraOpen && _isScanning) { + try { + String? base64Image; + if (Platform.isLinux && _cameraLinuxPlugin != null) { + base64Image = await _cameraLinuxPlugin!.captureImage(); + } else if (Platform.isWindows) { + final XFile xfile = + await _cameraWindowsPlugin!.takePicture(_cameraId); + final bytes = await xfile.readAsBytes(); + base64Image = base64Encode(bytes); + // We could use a Uint8List to optimize for Windows and macOS. + } else if (Platform.isMacOS) { + final macOSimg = await CameraMacOS.instance.takePicture(); + if (macOSimg == null) { + Logging.instance + .log("Failed to capture image", level: LogLevel.Error); + await Future.delayed(Duration(milliseconds: _imageDelayInMs)); + continue; + } + final img.Image? image = img.decodeImage(macOSimg.bytes!); + if (image == null) { + Logging.instance + .log("Failed to capture image", level: LogLevel.Error); + await Future.delayed(Duration(milliseconds: _imageDelayInMs)); + continue; + } + base64Image = base64Encode(img.encodePng(image)); + } + if (base64Image == null || base64Image.isEmpty) { + // Logging.instance + // .log("Failed to capture image", level: LogLevel.Error); + // Spammy. + await Future.delayed(Duration(milliseconds: _imageDelayInMs)); + continue; + } + final img.Image? image = img.decodeImage(base64Decode(base64Image)); + // TODO [prio=low]: Optimize this process. Docs say: + // > WARNING Since this will check the image data against all known + // > decoders, it is much slower than using an explicit decoder + if (image == null) { + Logging.instance.log("Failed to decode image", level: LogLevel.Error); + await Future.delayed(Duration(milliseconds: _imageDelayInMs)); + continue; + } + + if (mounted) { + setState(() { + _image = Image.memory( + base64Decode(base64Image!), + fit: BoxFit.cover, + ); + }); + } + + final String? scanResult = await _scanImage(image); + if (scanResult != null && scanResult.isNotEmpty) { + widget.onQrCodeDetected(scanResult); + if (mounted) { + Navigator.of(context).pop(); + } + break; + } else { + // Logging.instance.log("No QR code found in the image", level: LogLevel.Info); + // if (mounted) { + // widget.onSnackbar("No QR code found in the image."); + // } + // Spammy. + } + + await Future.delayed(Duration(milliseconds: _imageDelayInMs)); + } catch (e, s) { + // Logging.instance.log("Failed to capture and scan image: $e\n$s", level: LogLevel.Error); + // Spammy. + + // if (mounted) { + // widget.onSnackbar( + // "Error capturing or scanning the image. Please try again."); + // } + } + } + } + + Future _scanImage(img.Image image) async { + try { + final LuminanceSource source = RGBLuminanceSource( + image.width, + image.height, + image + .convert(numChannels: 4) + .getBytes(order: img.ChannelOrder.abgr) + .buffer + .asInt32List(), + ); + final BinaryBitmap bitmap = + BinaryBitmap(GlobalHistogramBinarizer(source)); + + final QRCodeReader reader = QRCodeReader(); + final qrDecode = reader.decode(bitmap); + if (qrDecode.text.isEmpty) { + return null; + } + return qrDecode.text; + } catch (e, s) { + // Logging.instance.log("Failed to decode QR code: $e\n$s", level: LogLevel.Error); + // Spammy. + return null; + } + } + + @override + Widget build(BuildContext context) { + return DesktopDialog( + maxWidth: 696, + maxHeight: 600, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only(left: 32), + child: Text( + "Scan QR code", + style: STextStyles.desktopH3(context), + ), + ), + const DesktopDialogCloseButton(), + ], + ), + Expanded( + child: _isCameraOpen + ? _image != null + ? _image! + : const Center( + child: CircularProgressIndicator(), + ) + : const Center( + child: + CircularProgressIndicator(), // Show progress indicator immediately + ), + ), + Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Expanded(child: Container()), + // "Select file" button. + SecondaryButton( + buttonHeight: ButtonHeight.l, + label: "Select file", + width: 200, + onPressed: () async { + final result = await FilePicker.platform.pickFiles( + type: FileType.custom, + allowedExtensions: ["png", "jpg", "jpeg"], + ); + + if (result == null || result.files.single.path == null) { + await showFloatingFlushBar( + type: FlushBarType.info, + message: "No file selected", + iconAsset: Assets.svg.file, + context: context, + ); + return; + } + + final filePath = result?.files.single.path!; + if (filePath == null) { + await showFloatingFlushBar( + type: FlushBarType.info, + message: "Error selecting file.", + iconAsset: Assets.svg.file, + context: context, + ); + return; + } + try { + final img.Image? image = + img.decodeImage(File(filePath!).readAsBytesSync()); + if (image == null) { + await showFloatingFlushBar( + type: FlushBarType.info, + message: "Failed to decode image.", + iconAsset: Assets.svg.file, + context: context, + ); + return; + } + + final String? scanResult = await _scanImage(image); + if (scanResult != null && scanResult.isNotEmpty) { + widget.onQrCodeDetected(scanResult); + Navigator.of(context).pop(); + } else { + await showFloatingFlushBar( + type: FlushBarType.info, + message: "No QR code found in the image.", + iconAsset: Assets.svg.file, + context: context, + ); + } + } catch (e, s) { + Logging.instance.log("Failed to decode image: $e\n$s", + level: LogLevel.Error); + await showFloatingFlushBar( + type: FlushBarType.info, + message: + "Error processing the image. Please try again.", + iconAsset: Assets.svg.file, + context: context, + ); + } + }, + ), + const SizedBox(width: 16), + // Close button. + PrimaryButton( + buttonHeight: ButtonHeight.l, + label: "Close", + width: 272.5, + onPressed: () { + _stopCamera(); + Navigator.of(context).pop(); + }, + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/widgets/monero_chan_dance.dart b/lib/widgets/monero_chan_dance.dart new file mode 100644 index 000000000..197f75b2c --- /dev/null +++ b/lib/widgets/monero_chan_dance.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +import '../utilities/assets.dart'; + +class MoneroChanDance extends StatelessWidget { + const MoneroChanDance({super.key, this.height = 200}); + + final double height; + @override + Widget build(BuildContext context) { + return Image( + height: height, + image: AssetImage( + Assets.gif.moneroChanDance, + ), + ); + } +} diff --git a/lib/widgets/node_card.dart b/lib/widgets/node_card.dart index b8a6f9cad..85f16a135 100644 --- a/lib/widgets/node_card.dart +++ b/lib/widgets/node_card.dart @@ -13,6 +13,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:tuple/tuple.dart'; + import '../notifications/show_flush_bar.dart'; import '../pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart'; import '../pages/settings_views/global_settings_view/manage_nodes_views/node_details_view.dart'; @@ -26,6 +28,7 @@ import '../utilities/default_nodes.dart'; import '../utilities/enums/sync_type_enum.dart'; import '../utilities/test_node_connection.dart'; import '../utilities/text_styles.dart'; +import '../utilities/tor_plain_net_option_enum.dart'; import '../utilities/util.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; import 'conditional_parent.dart'; @@ -33,7 +36,6 @@ import 'custom_buttons/blue_text_button.dart'; import 'expandable.dart'; import 'node_options_sheet.dart'; import 'rounded_white_container.dart'; -import 'package:tuple/tuple.dart'; class NodeCard extends ConsumerStatefulWidget { const NodeCard({ @@ -165,6 +167,15 @@ class _NodeCardState extends ConsumerState { text: "Connect", enabled: _status == "Disconnected", onTap: () async { + final TorPlainNetworkOption netOption; + if (_node.torEnabled && !_node.clearnetEnabled) { + netOption = TorPlainNetworkOption.tor; + } else if (_node.clearnetEnabled && !_node.torEnabled) { + netOption = TorPlainNetworkOption.clear; + } else { + netOption = TorPlainNetworkOption.both; + } + final nodeFormData = NodeFormData() ..useSSL = _node.useSSL ..trusted = _node.trusted @@ -172,6 +183,7 @@ class _NodeCardState extends ConsumerState { ..host = _node.host ..login = _node.loginName ..port = _node.port + ..netOption = netOption ..isFailover = _node.isFailover; nodeFormData.password = await _node.getPassword( ref.read(secureStoreProvider), diff --git a/lib/widgets/node_options_sheet.dart b/lib/widgets/node_options_sheet.dart index 553f60c05..511be2395 100644 --- a/lib/widgets/node_options_sheet.dart +++ b/lib/widgets/node_options_sheet.dart @@ -27,6 +27,7 @@ import '../utilities/default_nodes.dart'; import '../utilities/enums/sync_type_enum.dart'; import '../utilities/test_node_connection.dart'; import '../utilities/text_styles.dart'; +import '../utilities/tor_plain_net_option_enum.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; import 'rounded_white_container.dart'; @@ -256,6 +257,16 @@ class NodeOptionsSheet extends ConsumerWidget { ref.read(secureStoreProvider), ); if (context.mounted) { + final TorPlainNetworkOption netOption; + if (node.torEnabled && + !node.clearnetEnabled) { + netOption = TorPlainNetworkOption.tor; + } else if (node.clearnetEnabled && + !node.torEnabled) { + netOption = TorPlainNetworkOption.clear; + } else { + netOption = TorPlainNetworkOption.both; + } final canConnect = await testNodeConnection( context: context, nodeFormData: NodeFormData() @@ -266,6 +277,7 @@ class NodeOptionsSheet extends ConsumerWidget { ..port = node.port ..useSSL = node.useSSL ..isFailover = node.isFailover + ..netOption = netOption ..trusted = node.trusted, cryptoCurrency: coin, ref: ref, diff --git a/lib/widgets/stack_dialog.dart b/lib/widgets/stack_dialog.dart index 1541256a0..ff921d24c 100644 --- a/lib/widgets/stack_dialog.dart +++ b/lib/widgets/stack_dialog.dart @@ -94,7 +94,7 @@ class StackDialog extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Flexible( - child: Text( + child: SelectableText( title, style: STextStyles.pageTitleH2(context), ), @@ -110,7 +110,7 @@ class StackDialog extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( + SelectableText( message!, style: STextStyles.smallMed14(context), ), diff --git a/lib/widgets/stack_text_field.dart b/lib/widgets/stack_text_field.dart index c64c6a5e8..318b59119 100644 --- a/lib/widgets/stack_text_field.dart +++ b/lib/widgets/stack_text_field.dart @@ -9,10 +9,15 @@ */ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import '../themes/stack_colors.dart'; +import '../utilities/constants.dart'; import '../utilities/text_styles.dart'; import '../utilities/util.dart'; +import 'icon_widgets/clipboard_icon.dart'; +import 'icon_widgets/x_icon.dart'; +import 'textfield_icon_button.dart'; InputDecoration standardInputDecoration( String? labelText, @@ -52,3 +57,113 @@ InputDecoration standardInputDecoration( focusedErrorBorder: InputBorder.none, ); } + +class FullTextField extends StatefulWidget { + const FullTextField({ + super.key, + this.controller, + this.focusNode, + required this.label, + this.onChanged, + }); + + final String label; + + final TextEditingController? controller; + final FocusNode? focusNode; + + final void Function(String)? onChanged; + + @override + State createState() => _FullTextFieldState(); +} + +class _FullTextFieldState extends State { + late final TextEditingController controller; + late final FocusNode focusNode; + + bool _hasValue = false; + + @override + void initState() { + super.initState(); + controller = widget.controller ?? TextEditingController(); + focusNode = widget.focusNode ?? FocusNode(); + } + + @override + void dispose() { + if (widget.controller == null) { + controller.dispose(); + } + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + controller: controller, + autocorrect: false, + enableSuggestions: false, + onChanged: (newValue) { + widget.onChanged?.call(newValue); + }, + focusNode: focusNode, + style: STextStyles.field(context), + decoration: standardInputDecoration( + widget.label, + focusNode, + context, + ).copyWith( + contentPadding: const EdgeInsets.only( + left: 16, + top: 6, + bottom: 8, + right: 5, + ), + suffixIcon: Padding( + padding: controller.text.isEmpty + ? const EdgeInsets.only(right: 8) + : const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + TextFieldIconButton( + onTap: () async { + if (_hasValue) { + controller.text = ""; + + setState(() { + _hasValue = false; + }); + } else { + final data = + await Clipboard.getData(Clipboard.kTextPlain); + if (data?.text != null && data!.text!.isNotEmpty) { + final content = data.text!.trim(); + + controller.text = content; + setState(() { + _hasValue = content.isNotEmpty; + }); + } + } + + widget.onChanged?.call(controller.text); + }, + child: _hasValue ? const XIcon() : const ClipboardIcon(), + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/widgets/textfields/frost_step_field.dart b/lib/widgets/textfields/frost_step_field.dart index f805dc64a..d6e6b0cb8 100644 --- a/lib/widgets/textfields/frost_step_field.dart +++ b/lib/widgets/textfields/frost_step_field.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:barcode_scan2/barcode_scan2.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -8,6 +10,7 @@ import '../../utilities/logger.dart'; import '../../utilities/text_styles.dart'; import '../../utilities/util.dart'; import '../conditional_parent.dart'; +import '../desktop/qr_code_scanner_dialog.dart'; import '../icon_widgets/clipboard_icon.dart'; import '../icon_widgets/qrcode_icon.dart'; import '../icon_widgets/x_icon.dart'; @@ -71,6 +74,50 @@ class _FrostStepFieldState extends State { super.initState(); } + Future scanQr() async { + try { + if (Platform.isAndroid || Platform.isIOS) { + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed( + const Duration(milliseconds: 75), + ); + } + + final qrResult = await BarcodeScanner.scan(); + + widget.controller.text = qrResult.rawContent; + + _changed(widget.controller.text); + } else { + // Platform.isLinux, Platform.isWindows, or Platform.isMacOS. + await showDialog( + context: context, + builder: (context) { + return QrCodeScannerDialog( + onQrCodeDetected: (qrCodeData) { + try { + // TODO [prio=low]: Validate QR code data. + widget.controller.text = qrCodeData; + + _changed(widget.controller.text); + } catch (e, s) { + Logging.instance.log("Error processing QR code data: $e\n$s", + level: LogLevel.Error); + } + }, + ); + }, + ); + } + } on PlatformException catch (e, s) { + Logging.instance.log( + "Failed to get camera permissions while trying to scan qr code: $e\n$s", + level: LogLevel.Warning, + ); + } + } + @override Widget build(BuildContext context) { return ConditionalParent( @@ -150,27 +197,7 @@ class _FrostStepFieldState extends State { semanticsLabel: "Scan QR Button. Opens Camera For Scanning QR Code.", key: _qrKey, - onTap: () async { - try { - if (FocusScope.of(context).hasFocus) { - FocusScope.of(context).unfocus(); - await Future.delayed( - const Duration(milliseconds: 75), - ); - } - - final qrResult = await BarcodeScanner.scan(); - - widget.controller.text = qrResult.rawContent; - - _changed(widget.controller.text); - } on PlatformException catch (e, s) { - Logging.instance.log( - "Failed to get camera permissions while trying to scan qr code: $e\n$s", - level: LogLevel.Warning, - ); - } - }, + onTap: scanQr, child: const QrCodeIcon(), ), ], diff --git a/lib/widgets/wallet_card.dart b/lib/widgets/wallet_card.dart index a2185186c..814d4b321 100644 --- a/lib/widgets/wallet_card.dart +++ b/lib/widgets/wallet_card.dart @@ -23,12 +23,13 @@ import '../providers/providers.dart'; import '../utilities/constants.dart'; import '../utilities/logger.dart'; import '../utilities/show_loading.dart'; +import '../utilities/show_node_tor_settings_mismatch.dart'; import '../utilities/util.dart'; import '../wallets/isar/providers/eth/current_token_wallet_provider.dart'; import '../wallets/wallet/impl/ethereum_wallet.dart'; import '../wallets/wallet/impl/sub_wallets/eth_token_wallet.dart'; +import '../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../wallets/wallet/wallet.dart'; -import '../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; import 'conditional_parent.dart'; import 'desktop/primary_button.dart'; import 'dialogs/basic_dialog.dart'; @@ -95,9 +96,22 @@ class SimpleWalletCard extends ConsumerWidget { final wallet = ref.read(pWallets).getWallet(walletId); + final canContinue = await checkShowNodeTorSettingsMismatch( + context: context, + currency: wallet.cryptoCurrency, + prefs: ref.read(prefsChangeNotifierProvider), + nodeService: ref.read(nodeServiceChangeNotifierProvider), + allowCancel: true, + rootNavigator: Util.isDesktop, + ); + + if (!canContinue) { + return; + } + if (context.mounted) { final Future loadFuture; - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { loadFuture = wallet.init().then((value) async => await (wallet).open()); } else { loadFuture = wallet.init(); diff --git a/lib/widgets/wallet_navigation_bar/components/icons/churn_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/churn_nav_icon.dart new file mode 100644 index 000000000..af404f5c2 --- /dev/null +++ b/lib/widgets/wallet_navigation_bar/components/icons/churn_nav_icon.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +import '../../../../themes/stack_colors.dart'; +import '../../../../utilities/assets.dart'; + +class ChurnNavIcon extends StatelessWidget { + const ChurnNavIcon({super.key}); + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + Assets.svg.churn, + height: 20, + width: 20, + color: Theme.of(context).extension()!.bottomNavIconIcon, + ); + } +} diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 499e835f2..88c196c5e 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,10 +6,10 @@ #include "generated_plugin_registrant.h" +#include #include #include #include -#include #include #include #include @@ -18,6 +18,9 @@ #include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) cs_monero_flutter_libs_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "CsMoneroFlutterLibsLinuxPlugin"); + cs_monero_flutter_libs_linux_plugin_register_with_registrar(cs_monero_flutter_libs_linux_registrar); g_autoptr(FlPluginRegistrar) desktop_drop_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopDropPlugin"); desktop_drop_plugin_register_with_registrar(desktop_drop_registrar); @@ -27,9 +30,6 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) flutter_libepiccash_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterLibepiccashPlugin"); flutter_libepiccash_plugin_register_with_registrar(flutter_libepiccash_registrar); - g_autoptr(FlPluginRegistrar) flutter_libmonero_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterLibmoneroPlugin"); - flutter_libmonero_plugin_register_with_registrar(flutter_libmonero_registrar); g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index a8bc74800..72a81025c 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,10 +3,10 @@ # list(APPEND FLUTTER_PLUGIN_LIST + cs_monero_flutter_libs_linux desktop_drop devicelocale flutter_libepiccash - flutter_libmonero flutter_secure_storage_linux isar_flutter_libs sqlite3_flutter_libs @@ -16,6 +16,7 @@ list(APPEND FLUTTER_PLUGIN_LIST ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + camera_linux coinlib_flutter flutter_libsparkmobile frostdart diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 762e46f56..66db749d9 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,7 +5,9 @@ import FlutterMacOS import Foundation +import camera_macos import connectivity_plus +import cs_monero_flutter_libs_macos import desktop_drop import device_info_plus import devicelocale @@ -14,17 +16,20 @@ import flutter_local_notifications import flutter_secure_storage_macos import isar_flutter_libs import lelantus +import local_auth_darwin import package_info_plus import path_provider_foundation import share_plus import sqlite3_flutter_libs import stack_wallet_backup import url_launcher_macos -import wakelock_macos +import wakelock_plus import window_size func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + CameraMacosPlugin.register(with: registry.registrar(forPlugin: "CameraMacosPlugin")) ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) + CsMoneroFlutterLibsMacosPlugin.register(with: registry.registrar(forPlugin: "CsMoneroFlutterLibsMacosPlugin")) DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin")) @@ -33,12 +38,13 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin")) LelantusPlugin.register(with: registry.registrar(forPlugin: "LelantusPlugin")) - FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) + FLALocalAuthPlugin.register(with: registry.registrar(forPlugin: "FLALocalAuthPlugin")) + FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) StackWalletBackupPlugin.register(with: registry.registrar(forPlugin: "StackWalletBackupPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) - WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin")) + WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) WindowSizePlugin.register(with: registry.registrar(forPlugin: "WindowSizePlugin")) } diff --git a/macos/Podfile.lock b/macos/Podfile.lock index f7100bab5..b9159cd55 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -1,10 +1,14 @@ PODS: - - coinlib_flutter (0.3.2): + - camera_macos (0.0.1): + - FlutterMacOS + - coinlib_flutter (0.5.0): - Flutter - FlutterMacOS - connectivity_plus (0.0.1): - FlutterMacOS - ReachabilitySwift + - cs_monero_flutter_libs (0.0.1): + - FlutterMacOS - desktop_drop (0.0.1): - FlutterMacOS - device_info_plus (0.0.1): @@ -26,6 +30,9 @@ PODS: - FlutterMacOS - lelantus (0.0.1): - FlutterMacOS + - local_auth_darwin (0.0.1): + - Flutter + - FlutterMacOS - package_info_plus (0.0.1): - FlutterMacOS - path_provider_foundation (0.0.1): @@ -34,18 +41,21 @@ PODS: - ReachabilitySwift (5.0.0) - share_plus (0.0.1): - FlutterMacOS - - sqlite3 (3.46.0): - - sqlite3/common (= 3.46.0) - - sqlite3/common (3.46.0) - - sqlite3/fts5 (3.46.0): + - "sqlite3 (3.46.0+1)": + - "sqlite3/common (= 3.46.0+1)" + - "sqlite3/common (3.46.0+1)" + - "sqlite3/dbstatvtab (3.46.0+1)": - sqlite3/common - - sqlite3/perf-threadsafe (3.46.0): + - "sqlite3/fts5 (3.46.0+1)": - sqlite3/common - - sqlite3/rtree (3.46.0): + - "sqlite3/perf-threadsafe (3.46.0+1)": + - sqlite3/common + - "sqlite3/rtree (3.46.0+1)": - sqlite3/common - sqlite3_flutter_libs (0.0.1): - FlutterMacOS - - sqlite3 (~> 3.46.0) + - "sqlite3 (~> 3.46.0+1)" + - sqlite3/dbstatvtab - sqlite3/fts5 - sqlite3/perf-threadsafe - sqlite3/rtree @@ -54,14 +64,16 @@ PODS: - tor_ffi_plugin (0.0.1) - url_launcher_macos (0.0.1): - FlutterMacOS - - wakelock_macos (0.0.1): + - wakelock_plus (0.0.1): - FlutterMacOS - window_size (0.0.2): - FlutterMacOS DEPENDENCIES: + - camera_macos (from `Flutter/ephemeral/.symlinks/plugins/camera_macos/macos`) - coinlib_flutter (from `Flutter/ephemeral/.symlinks/plugins/coinlib_flutter/darwin`) - connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`) + - cs_monero_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/cs_monero_flutter_libs/macos`) - desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`) - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - devicelocale (from `Flutter/ephemeral/.symlinks/plugins/devicelocale/macos`) @@ -73,6 +85,7 @@ DEPENDENCIES: - frostdart (from `Flutter/ephemeral/.symlinks/plugins/frostdart/macos`) - isar_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos`) - lelantus (from `Flutter/ephemeral/.symlinks/plugins/lelantus/macos`) + - local_auth_darwin (from `Flutter/ephemeral/.symlinks/plugins/local_auth_darwin/darwin`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) @@ -80,7 +93,7 @@ DEPENDENCIES: - stack_wallet_backup (from `Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos`) - tor_ffi_plugin (from `Flutter/ephemeral/.symlinks/plugins/tor_ffi_plugin/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - - wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`) + - wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`) - window_size (from `Flutter/ephemeral/.symlinks/plugins/window_size/macos`) SPEC REPOS: @@ -89,10 +102,14 @@ SPEC REPOS: - sqlite3 EXTERNAL SOURCES: + camera_macos: + :path: Flutter/ephemeral/.symlinks/plugins/camera_macos/macos coinlib_flutter: :path: Flutter/ephemeral/.symlinks/plugins/coinlib_flutter/darwin connectivity_plus: :path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos + cs_monero_flutter_libs: + :path: Flutter/ephemeral/.symlinks/plugins/cs_monero_flutter_libs/macos desktop_drop: :path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos device_info_plus: @@ -115,6 +132,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos lelantus: :path: Flutter/ephemeral/.symlinks/plugins/lelantus/macos + local_auth_darwin: + :path: Flutter/ephemeral/.symlinks/plugins/local_auth_darwin/darwin package_info_plus: :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos path_provider_foundation: @@ -129,35 +148,38 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/tor_ffi_plugin/macos url_launcher_macos: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos - wakelock_macos: - :path: Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos + wakelock_plus: + :path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos window_size: :path: Flutter/ephemeral/.symlinks/plugins/window_size/macos SPEC CHECKSUMS: - coinlib_flutter: 6abec900d67762a6e7ccfd567a3cd3ae00bbee35 + camera_macos: c2603f5eed16f05076cf17e12030d2ce55a77839 + coinlib_flutter: 9275e8255ef67d3da33beb6e117d09ced4f46eb5 connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747 + cs_monero_flutter_libs: e91a436103857259f5855cad4971301a5a29b38d desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898 - device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f + device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720 devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225 flutter_libepiccash: be1560a04150c5cc85bcf08d236ec2b3d1f5d8da flutter_libsparkmobile: df2d36af1691379c81249e7be7b68be3c81d388b flutter_local_notifications: 3805ca215b2fb7f397d78b66db91f6a747af52e4 - flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea + flutter_secure_storage_macos: 59459653abe1adb92abbc8ea747d79f8d19866c9 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 frostdart: e6bf3119527ccfbcec1b8767da6ede5bb4c4f716 isar_flutter_libs: 43385c99864c168fadba7c9adeddc5d38838ca6a lelantus: 308e42c5a648598936a07a234471dd8cf8e687a0 - package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce - path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 + local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3 + package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 - sqlite3: 154b084339ede06960a5b3c8160066adc9176b7d - sqlite3_flutter_libs: 1be4459672f8168ded2d8667599b8e3ca5e72b83 + sqlite3: 292c3e1bfe89f64e51ea7fc7dab9182a017c8630 + sqlite3_flutter_libs: 5ca46c1a04eddfbeeb5b16566164aa7ad1616e7b stack_wallet_backup: 6ebc60b1bdcf11cf1f1cbad9aa78332e1e15778c tor_ffi_plugin: 2566c1ed174688cca560fa0c64b7a799c66f07cb - url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95 - wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9 + url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399 + wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269 window_size: 339dafa0b27a95a62a843042038fa6c3c48de195 PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift index d53ef6437..b3c176141 100644 --- a/macos/Runner/AppDelegate.swift +++ b/macos/Runner/AppDelegate.swift @@ -1,9 +1,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements index c946719a1..3d2512f1e 100644 --- a/macos/Runner/DebugProfile.entitlements +++ b/macos/Runner/DebugProfile.entitlements @@ -10,5 +10,9 @@ com.apple.security.network.client + com.apple.security.device.audio-input + + com.apple.security.device.camera + diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist index 4789daa6a..82ae07c13 100644 --- a/macos/Runner/Info.plist +++ b/macos/Runner/Info.plist @@ -28,5 +28,11 @@ MainMenu NSPrincipalClass NSApplication + NSCameraUsageDescription + QR Code scanning + NSMicrophoneUsageDescription + QR Code Scanning. A temporary requirement due to limitations in the camera_macos package that are being worked on to remove the need for this permission. + NSCameraUseContinuityCameraDeviceType + diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements index 95f3da71e..4094a6367 100644 --- a/macos/Runner/Release.entitlements +++ b/macos/Runner/Release.entitlements @@ -8,5 +8,9 @@ com.apple.security.network.server + com.apple.security.device.audio-input + + com.apple.security.device.camera + diff --git a/pubspec.lock b/pubspec.lock index f311f27ea..a973ef92f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,23 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 url: "https://pub.dev" source: hosted - version: "61.0.0" + version: "72.0.0" + _macros: + dependency: transitive + description: dart + source: sdk + version: "0.3.2" analyzer: dependency: "direct dev" description: name: analyzer - sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 url: "https://pub.dev" source: hosted - version: "5.13.0" + version: "6.7.0" another_flushbar: dependency: "direct main" description: @@ -29,34 +34,26 @@ packages: dependency: transitive description: name: ansicolor - sha256: "8bf17a8ff6ea17499e40a2d2542c2f481cd7615760c6d34065cb22bfd22e6880" + sha256: "50e982d500bc863e1d703448afdbf9e5a72eb48840a4f766fa361ffd6877055f" url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.0.3" archive: dependency: "direct main" description: name: archive - sha256: e0902a06f0e00414e4e3438a084580161279f137aeb862274710f29ec10cf01e + sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d url: "https://pub.dev" source: hosted - version: "3.3.9" + version: "3.6.1" args: dependency: transitive description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 url: "https://pub.dev" source: hosted - version: "2.4.2" - asn1lib: - dependency: transitive - description: - name: asn1lib - sha256: "21afe4333076c02877d14f4a89df111e658a6d466cbfc802eb705eb91bd5adfd" - url: "https://pub.dev" - source: hosted - version: "1.5.0" + version: "2.6.0" async: dependency: "direct main" description: @@ -69,18 +66,18 @@ packages: dependency: "direct main" description: name: barcode_scan2 - sha256: "0b0625d27841a21e36e896195d86b2aada335e3c486f63647cce701495718e16" + sha256: a2ab566027cd57b2795ea42aa26835dbaa8fe70bcc1aff54942a14d3705dff97 url: "https://pub.dev" source: hosted - version: "4.2.4" + version: "4.3.3" basic_utils: dependency: "direct main" description: name: basic_utils - sha256: "8815477fcf58499e42326bd858e391442425fa57db9a45e48e15224c62049262" + sha256: "2064b21d3c41ed7654bc82cc476fd65542e04d60059b74d5eed490a4da08fc6c" url: "https://pub.dev" source: hosted - version: "5.5.4" + version: "5.7.0" bech32: dependency: "direct main" description: @@ -150,6 +147,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.0" + blockchain_utils: + dependency: "direct main" + description: + name: blockchain_utils + sha256: ebb6c336ba0120de0982c50d8bc597cb494a530bd22bd462895bb5cebde405af + url: "https://pub.dev" + source: hosted + version: "3.4.0" boolean_selector: dependency: transitive description: @@ -194,34 +199,34 @@ packages: dependency: transitive description: name: build_daemon - sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.2" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: d912852cce27c9e80a93603db721c267716894462e7033165178b91138587972 + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.2" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" + sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d" url: "https://pub.dev" source: hosted - version: "2.4.6" + version: "2.4.13" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41" + sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0 url: "https://pub.dev" source: hosted - version: "7.2.10" + version: "7.3.2" built_collection: dependency: transitive description: @@ -234,18 +239,59 @@ packages: dependency: transitive description: name: built_value - sha256: ff627b645b28fb8bdb69e645f910c2458fd6b65f6585c3a53e0626024897dedf + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb url: "https://pub.dev" source: hosted - version: "8.6.2" + version: "8.9.2" calendar_date_picker2: dependency: "direct main" description: name: calendar_date_picker2 - sha256: "7ff3f372faff6814a2ba69427d116fb9a3d52e28644b9de4b06db6638fdac798" + sha256: "986955aea43081c8cf70fe01735ccdfe9a7041218b2be53ed92d5d4e639e8a1a" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.7" + camera_linux: + dependency: "direct main" + description: + name: camera_linux + sha256: "6ea08c23f643364e650e8fad73653747c049cbd00803a7c317132379ee3653ac" + url: "https://pub.dev" + source: hosted + version: "0.0.8" + camera_macos: + dependency: "direct main" + description: + name: camera_macos + sha256: a0e15729caf4e7c2831b9cd964e8c2e2ea985cd816e56316be03355de44aa743 + url: "https://pub.dev" + source: hosted + version: "0.0.9" + camera_platform_interface: + dependency: "direct main" + description: + name: camera_platform_interface + sha256: b3ede1f171532e0d83111fe0980b46d17f1aa9788a07a2fbed07366bbdbb9061 + url: "https://pub.dev" + source: hosted + version: "2.8.0" + camera_windows: + dependency: "direct main" + description: + path: "packages/camera/camera_windows" + ref: HEAD + resolved-ref: "9bfbfd643ba4e6865ec34124e42a1cc502c400c0" + url: "https://github.com/cypherstack/packages.git" + source: git + version: "0.2.4" + cbor: + dependency: "direct main" + description: + name: cbor + sha256: "69b1fe99f4632acc597034d73a5eb440f82667b58e2489223bb798e31b6c90db" + url: "https://pub.dev" + source: hosted + version: "6.3.3" characters: dependency: transitive description: @@ -254,6 +300,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + charcode: + dependency: transitive + description: + name: charcode + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 + url: "https://pub.dev" + source: hosted + version: "1.3.1" checked_yaml: dependency: transitive description: @@ -266,10 +320,10 @@ packages: dependency: transitive description: name: cli_util - sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.4.2" clock: dependency: transitive description: @@ -282,28 +336,28 @@ packages: dependency: transitive description: name: code_builder - sha256: "315a598c7fbe77f22de1c9da7cfd6fd21816312f16ffa124453b4fc679e540f1" + sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" url: "https://pub.dev" source: hosted - version: "4.6.0" + version: "4.10.1" coinlib: dependency: "direct overridden" description: path: coinlib - ref: b88e68e2e10ffe54d802deeed6b9e83da7721a1f - resolved-ref: b88e68e2e10ffe54d802deeed6b9e83da7721a1f - url: "https://github.com/peercoin/coinlib.git" + ref: "0acacfd17eacf72135c693a7b862bd9b7cc56739" + resolved-ref: "0acacfd17eacf72135c693a7b862bd9b7cc56739" + url: "https://github.com/julian-CStack/coinlib.git" source: git - version: "2.1.0-rc.1" + version: "2.2.0" coinlib_flutter: dependency: "direct main" description: path: coinlib_flutter - ref: b88e68e2e10ffe54d802deeed6b9e83da7721a1f - resolved-ref: b88e68e2e10ffe54d802deeed6b9e83da7721a1f - url: "https://github.com/peercoin/coinlib.git" + ref: "0acacfd17eacf72135c693a7b862bd9b7cc56739" + resolved-ref: "0acacfd17eacf72135c693a7b862bd9b7cc56739" + url: "https://github.com/julian-CStack/coinlib.git" source: git - version: "2.1.0-rc.1" + version: "2.2.0" collection: dependency: transitive description: @@ -312,6 +366,15 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + compat: + dependency: "direct main" + description: + path: compat + ref: b2b12cfeaf15d057ea18b14c196e04f3002de2da + resolved-ref: b2b12cfeaf15d057ea18b14c196e04f3002de2da + url: "https://github.com/cypherstack/cs_monero" + source: git + version: "1.0.0" connectivity_plus: dependency: "direct main" description: @@ -332,26 +395,26 @@ packages: dependency: "direct main" description: name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" coverage: dependency: transitive description: name: coverage - sha256: "8acabb8306b57a409bf4c83522065672ee13179297a6bb0cb9ead73948df7c76" + sha256: "88b0fddbe4c92910fefc09cc0248f5e7f0cd23e450ded4c28f16ab8ee8f83268" url: "https://pub.dev" source: hosted - version: "1.7.2" + version: "1.10.0" cross_file: dependency: transitive description: name: cross_file - sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" url: "https://pub.dev" source: hosted - version: "0.3.4+1" + version: "0.3.4+2" crypto: dependency: "direct main" description: @@ -364,39 +427,106 @@ packages: dependency: transitive description: name: cryptography - sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35 + sha256: d146b76d33d94548cf035233fbc2f4338c1242fa119013bead807d033fc4ae05 url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.7.0" + cs_monero: + dependency: "direct main" + description: + name: cs_monero + sha256: b9b9db6602361587b1ce512002f174fd833818ff2a63787c3058e0532fc0d9d8 + url: "https://pub.dev" + source: hosted + version: "1.0.0-pre" + cs_monero_flutter_libs: + dependency: "direct main" + description: + name: cs_monero_flutter_libs + sha256: ccfd2c80e3f283f447602ecc548c9922b526002928a0fa1d34f1d6d74f73952e + url: "https://pub.dev" + source: hosted + version: "1.0.0-pre.0" + cs_monero_flutter_libs_android: + dependency: transitive + description: + name: cs_monero_flutter_libs_android + sha256: "8a03a93b84a091a6c09be2a3504002885af5e3e8e316b2776544271e9509d352" + url: "https://pub.dev" + source: hosted + version: "1.0.0-pre" + cs_monero_flutter_libs_android_arm64_v8a: + dependency: transitive + description: + name: cs_monero_flutter_libs_android_arm64_v8a + sha256: a412c30e8f72aefc2671f2ed76b50fdb70fc9eaa697f8c7050e0ede941f5863e + url: "https://pub.dev" + source: hosted + version: "1.0.0-pre" + cs_monero_flutter_libs_android_armeabi_v7a: + dependency: transitive + description: + name: cs_monero_flutter_libs_android_armeabi_v7a + sha256: "2177af9a62ca9c2997f88af09d54c784dc1ee49a3540abe73c0271d25eb8dadb" + url: "https://pub.dev" + source: hosted + version: "1.0.0-pre" + cs_monero_flutter_libs_android_x86_64: + dependency: transitive + description: + name: cs_monero_flutter_libs_android_x86_64 + sha256: ac5e03624c86438bbe47c986dab5bfe1fb3060a0b4bdb5dd8eea09795a5fc2c8 + url: "https://pub.dev" + source: hosted + version: "1.0.0-pre" + cs_monero_flutter_libs_ios: + dependency: transitive + description: + name: cs_monero_flutter_libs_ios + sha256: ec2e5b9b3ae3100f390deeff6114ffe3259a0701fafdc0f1028996e0da78b17b + url: "https://pub.dev" + source: hosted + version: "1.0.0-pre.0" + cs_monero_flutter_libs_linux: + dependency: transitive + description: + name: cs_monero_flutter_libs_linux + sha256: "65651535e028211d4c535dac53fdfec940935f1037f0a01bfdf4f6cf8b904362" + url: "https://pub.dev" + source: hosted + version: "1.0.0-pre" + cs_monero_flutter_libs_macos: + dependency: transitive + description: + name: cs_monero_flutter_libs_macos + sha256: "609586b4e4524452698b4877ff886ad9aafd721373cddebba45d9d8a63a8cfc9" + url: "https://pub.dev" + source: hosted + version: "1.0.0-pre" + cs_monero_flutter_libs_platform_interface: + dependency: transitive + description: + name: cs_monero_flutter_libs_platform_interface + sha256: "9df4ced42f5746c85c008f504f70b39efd05aa409bb82eaf4b2058d8454e0bb7" + url: "https://pub.dev" + source: hosted + version: "1.0.0-pre" + cs_monero_flutter_libs_windows: + dependency: transitive + description: + name: cs_monero_flutter_libs_windows + sha256: "4294f62e40ba2f155c52fccfdbb12029ed5c856ea8a546aebb3ebc94bc856e43" + url: "https://pub.dev" + source: hosted + version: "1.0.0-pre" csslib: dependency: transitive description: name: csslib - sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" url: "https://pub.dev" source: hosted - version: "1.0.0" - cw_core: - dependency: "direct main" - description: - path: "crypto_plugins/flutter_libmonero/cw_core" - relative: true - source: path - version: "0.0.1" - cw_monero: - dependency: "direct main" - description: - path: "crypto_plugins/flutter_libmonero/cw_monero" - relative: true - source: path - version: "0.0.1" - cw_wownero: - dependency: "direct main" - description: - path: "crypto_plugins/flutter_libmonero/cw_wownero" - relative: true - source: path - version: "0.0.1" + version: "1.0.2" dart_base_x: dependency: transitive description: @@ -433,10 +563,10 @@ packages: dependency: transitive description: name: dart_style - sha256: abd7625e16f51f554ea244d090292945ec4d4be7bfbaf2ec8cccea568919d334 + sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.3.7" dartx: dependency: transitive description: @@ -449,10 +579,10 @@ packages: dependency: transitive description: name: dbus - sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" url: "https://pub.dev" source: hosted - version: "0.7.8" + version: "0.7.10" decimal: dependency: "direct main" description: @@ -465,50 +595,58 @@ packages: dependency: "direct dev" description: name: dependency_validator - sha256: "08349175533ed0bd06eb9b6043cde66c45b2bfc7ebc222a7542cdb1324f1bf03" + sha256: f727a5627aa405965fab4aef4f468e50a9b632ba0737fd2f98c932fec6d712b9 url: "https://pub.dev" source: hosted - version: "3.2.2" + version: "3.2.3" desktop_drop: dependency: "direct main" description: name: desktop_drop - sha256: ebba9c9cb0b54385998a977d741cc06fd8324878c08d5a36e9da61cd56b04cc6 + sha256: d55a010fe46c8e8fcff4ea4b451a9ff84a162217bdb3b2a0aa1479776205e15d url: "https://pub.dev" source: hosted - version: "0.4.3" + version: "0.4.4" device_info_plus: dependency: "direct main" description: name: device_info_plus - sha256: "86add5ef97215562d2e090535b0a16f197902b10c369c558a100e74ea06e8659" + sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074 url: "https://pub.dev" source: hosted - version: "9.0.3" + version: "10.1.2" device_info_plus_platform_interface: dependency: transitive description: name: device_info_plus_platform_interface - sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64 + sha256: "282d3cf731045a2feb66abfe61bbc40870ae50a3ed10a4d3d217556c35c8c2ba" url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "7.0.1" devicelocale: dependency: "direct main" description: name: devicelocale - sha256: "09cc0bce937b34dafcaac0b2a9b350d763dd16770f15e59560b7abaaaad3d65b" + sha256: "0812b66f9eac57bc55c6ed4c178e0779440aa4e4e7c7e32fe1db02a758501d0e" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.7.1" dio: dependency: transitive description: name: dio - sha256: "11e40df547d418cc0c4900a9318b26304e665da6fa4755399a9ff9efd09034b5" + sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260" url: "https://pub.dev" source: hosted - version: "5.4.3+1" + version: "5.7.0" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8" + url: "https://pub.dev" + source: hosted + version: "2.0.0" dropdown_button2: dependency: "direct main" description: @@ -521,18 +659,18 @@ packages: dependency: transitive description: name: ed25519_hd_key - sha256: c5c9f11a03f5789bf9dcd9ae88d641571c802640851f1cacdb13123f171b3a26 + sha256: "31e191ec97492873067e46dc9cc0c7d55170559c83a478400feffa0627acaccf" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.3.0" eip1559: dependency: transitive description: name: eip1559 - sha256: de454d6321bd753eb79116e9ec4f8df20895c2e97f9a3839a032f3a728985516 + sha256: c2b81ac85f3e0e71aaf558201dd9a4600f051ece7ebacd0c5d70065c9b458004 url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.6.2" eip55: dependency: transitive description: @@ -558,14 +696,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.9" - encrypt: - dependency: transitive - description: - name: encrypt - sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2" - url: "https://pub.dev" - source: hosted - version: "5.0.3" equatable: dependency: "direct main" description: @@ -586,10 +716,10 @@ packages: dependency: "direct main" description: name: event_bus - sha256: "44baa799834f4c803921873e7446a2add0f3efa45e101a054b1f0ab9b95f8edc" + sha256: "1a55e97923769c286d295240048fc180e7b0768902c3c2e869fe059aafa15304" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.0.1" fake_async: dependency: transitive description: @@ -602,10 +732,10 @@ packages: dependency: "direct main" description: name: ffi - sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.3" file: dependency: transitive description: @@ -627,10 +757,10 @@ packages: dependency: transitive description: name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" fixnum_nanodart: dependency: transitive description: @@ -661,10 +791,10 @@ packages: dependency: "direct main" description: name: flutter_hooks - sha256: "7c8db779c2d1010aa7f9ea3fbefe8f86524fcb87b69e8b0af31e1a4b55422dec" + sha256: cde36b12f7188c85286fba9b38cc5a902e7279f36dd676967106c041dc9dde70 url: "https://pub.dev" source: hosted - version: "0.20.3" + version: "0.20.5" flutter_launcher_icons: dependency: "direct dev" description: @@ -680,19 +810,12 @@ packages: relative: true source: path version: "0.0.1" - flutter_libmonero: - dependency: "direct main" - description: - path: "crypto_plugins/flutter_libmonero" - relative: true - source: path - version: "0.0.1" flutter_libsparkmobile: dependency: "direct main" description: path: "." - ref: "7a11d0cadf8c7a6a5d5144dab18cef9536aa5943" - resolved-ref: "7a11d0cadf8c7a6a5d5144dab18cef9536aa5943" + ref: "9318bdd8e76e4dc8a49e3d64e8851c85e017eff3" + resolved-ref: "9318bdd8e76e4dc8a49e3d64e8851c85e017eff3" url: "https://github.com/cypherstack/flutter_libsparkmobile.git" source: git version: "0.0.1" @@ -700,58 +823,50 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 + sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" flutter_local_notifications: dependency: "direct main" description: name: flutter_local_notifications - sha256: f9a05409385b77b06c18f200a41c7c2711ebf7415669350bb0f8474c07bd40d1 + sha256: "674173fd3c9eda9d4c8528da2ce0ea69f161577495a9cc835a2a4ecd7eadeb35" url: "https://pub.dev" source: hosted - version: "17.0.0" + version: "17.2.4" flutter_local_notifications_linux: dependency: transitive description: name: flutter_local_notifications_linux - sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03" + sha256: c49bd06165cad9beeb79090b18cd1eb0296f4bf4b23b84426e37dd7c027fc3af url: "https://pub.dev" source: hosted - version: "4.0.0+1" + version: "4.0.1" flutter_local_notifications_platform_interface: dependency: transitive description: name: flutter_local_notifications_platform_interface - sha256: "7cf643d6d5022f3baed0be777b0662cce5919c0a7b86e700299f22dc4ae660ef" + sha256: "85f8d07fe708c1bdcf45037f2c0109753b26ae077e9d9e899d55971711a4ea66" url: "https://pub.dev" source: hosted - version: "7.0.0+1" - flutter_mobx: - dependency: transitive - description: - name: flutter_mobx - sha256: "0da4add0016387a7bf309a0d0c41d36c6b3ae25ed7a176409267f166509e723e" - url: "https://pub.dev" - source: hosted - version: "2.0.6+5" + version: "7.2.0" flutter_native_splash: dependency: "direct main" description: name: flutter_native_splash - sha256: ecff62b3b893f2f665de7e4ad3de89f738941fcfcaaba8ee601e749efafa4698 + sha256: "17d9671396fb8ec45ad10f4a975eb8a0f70bedf0fdaf0720b31ea9de6da8c4da" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.7" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f" + sha256: "9b78450b89f059e96c9ebb355fa6b3df1d6b330436e0b885fb49594c41721398" url: "https://pub.dev" source: hosted - version: "2.0.19" + version: "2.0.23" flutter_riverpod: dependency: "direct main" description: @@ -772,34 +887,34 @@ packages: dependency: transitive description: name: flutter_secure_storage_linux - sha256: "3d5032e314774ee0e1a7d0a9f5e2793486f0dff2dd9ef5a23f4e3fb2a0ae6a9e" + sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" flutter_secure_storage_macos: dependency: transitive description: name: flutter_secure_storage_macos - sha256: bd33935b4b628abd0b86c8ca20655c5b36275c3a3f5194769a7b3f37c905369c + sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.1.2" flutter_secure_storage_platform_interface: dependency: transitive description: name: flutter_secure_storage_platform_interface - sha256: "0d4d3a5dd4db28c96ae414d7ba3b8422fd735a8255642774803b2532c9a61d7e" + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.2" flutter_secure_storage_web: dependency: transitive description: name: flutter_secure_storage_web - sha256: "30f84f102df9dcdaa2241866a958c2ec976902ebdaa8883fbfe525f1f2f3cf20" + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.2.1" flutter_secure_storage_windows: dependency: transitive description: @@ -812,10 +927,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: "8c5d68a82add3ca76d792f058b186a0599414f279f00ece4830b9b231b570338" + sha256: "1b7723a814d84fb65869ea7115cdb3ee7c3be5a27a755c1ec60e049f6b9fcbb2" url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.0.11" flutter_test: dependency: "direct dev" description: flutter @@ -830,18 +945,18 @@ packages: dependency: transitive description: name: freezed_annotation - sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.4" frontend_server_client: dependency: transitive description: name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "4.0.0" frostdart: dependency: "direct main" description: @@ -858,8 +973,8 @@ packages: dependency: "direct main" description: path: "." - ref: "7dd8ff0dc9cb0caaac795fa44841a26437edfec3" - resolved-ref: "7dd8ff0dc9cb0caaac795fa44841a26437edfec3" + ref: "9dc883f4432c8db4ec44cb8cc836963295d63952" + resolved-ref: "9dc883f4432c8db4ec44cb8cc836963295d63952" url: "https://github.com/cypherstack/fusiondart.git" source: git version: "1.0.0" @@ -875,18 +990,18 @@ packages: dependency: "direct main" description: name: google_fonts - sha256: "6b6f10f0ce3c42f6552d1c70d2c28d764cf22bb487f50f66cca31dcd5194f4d6" + sha256: "2776c66b3e97c6cdd58d1bd3281548b074b64f1fd5c8f82391f7456e38849567" url: "https://pub.dev" source: hosted - version: "4.0.4" + version: "4.0.5" graphs: dependency: transitive description: name: graphs - sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" hex: dependency: "direct main" description: @@ -931,10 +1046,10 @@ packages: dependency: transitive description: name: html - sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" + sha256: "1fc58edeaec4307368c60d59b7e15b9d658b57d7f3125098b6294153c75337ec" url: "https://pub.dev" source: hosted - version: "0.15.4" + version: "0.15.5" http: dependency: "direct main" description: @@ -959,14 +1074,22 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + ieee754: + dependency: transitive + description: + name: ieee754 + sha256: "7d87451c164a56c156180d34a4e93779372edd191d2c219206100b976203128c" + url: "https://pub.dev" + source: hosted + version: "1.0.3" image: dependency: transitive description: name: image - sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf + sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d url: "https://pub.dev" source: hosted - version: "4.0.17" + version: "4.3.0" import_sorter: dependency: "direct dev" description: @@ -1000,26 +1123,26 @@ packages: dependency: "direct main" description: name: isar - sha256: "5be35dbc489880fccc535da3d1c4b3f5fdeee6ebfcacd4b149e39e803c4029cd" - url: "https://pub.dev" + sha256: e17a9555bc7f22ff26568b8c64d019b4ffa2dc6bd4cb1c8d9b269aefd32e53ad + url: "https://pub.isar-community.dev" source: hosted - version: "3.0.5" + version: "3.1.8" isar_flutter_libs: dependency: "direct main" description: name: isar_flutter_libs - sha256: "9794524734856a8a3629652f9f359b66e3fea3cebeec4dbdeb3e3a8fb253073e" - url: "https://pub.dev" + sha256: "78710781e658ce4bff59b3f38c5b2735e899e627f4e926e1221934e77b95231a" + url: "https://pub.isar-community.dev" source: hosted - version: "3.0.5" + version: "3.1.8" isar_generator: dependency: "direct dev" description: name: isar_generator - sha256: ee4ab5d5b251bc7e86e1257793b57af100065831f00f3a12404b177ae53c2d69 - url: "https://pub.dev" + sha256: "484e73d3b7e81dbd816852fe0b9497333118a9aeb646fd2d349a62cc8980ffe1" + url: "https://pub.isar-community.dev" source: hosted - version: "3.0.5" + version: "3.1.8" js: dependency: transitive description: @@ -1032,26 +1155,26 @@ packages: dependency: transitive description: name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted - version: "4.8.1" + version: "4.9.0" json_rpc_2: dependency: transitive description: name: json_rpc_2 - sha256: "5e469bffa23899edacb7b22787780068d650b106a21c76db3c49218ab7ca447e" + sha256: "246b321532f0e8e2ba474b4d757eaa558ae4fdd0688fdbc1e1ca9705f9b8ca0e" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" json_serializable: dependency: transitive description: name: json_serializable - sha256: aa1f5a8912615733e0fdc7a02af03308933c93235bdc8d50d0b0c8a8ccb0b969 + sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b url: "https://pub.dev" source: hosted - version: "6.7.1" + version: "6.8.0" keyboard_dismisser: dependency: "direct main" description: @@ -1064,18 +1187,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -1103,26 +1226,74 @@ packages: dependency: "direct main" description: name: local_auth - sha256: d3fece0749101725b03206f84a7dab7aaafb702dbbd09131ff8d8173259a9b19 + sha256: "434d854cf478f17f12ab29a76a02b3067f86a63a6d6c4eb8fbfdcfe4879c1b7b" url: "https://pub.dev" source: hosted - version: "1.1.11" + version: "2.3.0" + local_auth_android: + dependency: transitive + description: + name: local_auth_android + sha256: "6763aaf8965f21822624cb2fd3c03d2a8b3791037b5efb0fe4b13e110f5afc92" + url: "https://pub.dev" + source: hosted + version: "1.0.46" + local_auth_darwin: + dependency: transitive + description: + name: local_auth_darwin + sha256: "6d2950da311d26d492a89aeb247c72b4653ddc93601ea36a84924a396806d49c" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + local_auth_platform_interface: + dependency: transitive + description: + name: local_auth_platform_interface + sha256: "1b842ff177a7068442eae093b64abe3592f816afd2a533c0ebcdbe40f9d2075a" + url: "https://pub.dev" + source: hosted + version: "1.0.10" + local_auth_windows: + dependency: transitive + description: + name: local_auth_windows + sha256: bc4e66a29b0fdf751aafbec923b5bed7ad6ed3614875d8151afe2578520b2ab5 + url: "https://pub.dev" + source: hosted + version: "1.0.11" + logger: + dependency: transitive + description: + name: logger + sha256: "697d067c60c20999686a0add96cf6aba723b3aa1f83ecf806a8097231529ec32" + url: "https://pub.dev" + source: hosted + version: "2.4.0" logging: dependency: transitive description: name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" lottie: dependency: "direct main" description: name: lottie - sha256: b8bdd54b488c54068c57d41ae85d02808da09e2bee8b8dd1f59f441e7efa60cd + sha256: a93542cc2d60a7057255405f62252533f8e8956e7e06754955669fd32fb4b216 url: "https://pub.dev" source: hosted - version: "2.6.0" + version: "2.7.0" + macros: + dependency: transitive + description: + name: macros + sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" + url: "https://pub.dev" + source: hosted + version: "0.1.2-main.4" matcher: dependency: transitive description: @@ -1135,10 +1306,10 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" memoize: dependency: transitive description: @@ -1151,26 +1322,18 @@ packages: dependency: "direct main" description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" mime: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" url: "https://pub.dev" source: hosted - version: "1.0.4" - mobx: - dependency: transitive - description: - name: mobx - sha256: "0afcf88b3ee9d6819890bf16c11a727fc8c62cf736fda8e5d3b9b4eace4e62ea" - url: "https://pub.dev" - source: hosted - version: "2.2.0" + version: "1.0.6" mockingjay: dependency: "direct dev" description: @@ -1195,23 +1358,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.0" - monero: - dependency: "direct main" - description: - path: "." - ref: d46753eca865e9e56c2f0ef6fe485c42e11982c5 - resolved-ref: d46753eca865e9e56c2f0ef6fe485c42e11982c5 - url: "https://github.com/mrcyjanek/monero.dart" - source: git - version: "0.0.0" mutex: dependency: "direct main" description: name: mutex - sha256: "03116a4e46282a671b46c12de649d72c0ed18188ffe12a8d0fc63e83f4ad88f4" + sha256: "8827da25de792088eb33e572115a5eb0d61d61a3c01acbc8bcbe76ed78f1a1f2" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.1.0" nanodart: dependency: "direct main" description: @@ -1236,6 +1390,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.2" + on_chain: + dependency: "direct main" + description: + name: on_chain + sha256: "54d8117ff007b1f4a71ce8076c2a0bbfb592e5ce61ae397c68a67d70c9246a5d" + url: "https://pub.dev" + source: hosted + version: "4.3.0" package_config: dependency: transitive description: @@ -1248,18 +1410,18 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: "6ff267fcd9d48cb61c8df74a82680e8b82e940231bb5f68356672fde0397334a" + sha256: df3eb3e0aed5c1107bb0fdb80a8e82e778114958b1c5ac5644fb1ac9cae8a998 url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "8.1.0" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" + sha256: ac1f4a4847f1ade8e6a87d1f39f5d7c67490738642e2542f559ec38c37489a66 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" path: dependency: transitive description: @@ -1272,34 +1434,34 @@ packages: dependency: transitive description: name: path_parsing - sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + sha256: caa17e8f0b386eb190dd5b6a3b71211c76375aa8b6ffb4465b5863d019bdb334 url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.0.3" path_provider: dependency: "direct main" description: name: path_provider - sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.5" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" + sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.12" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.4.0" path_provider_linux: dependency: transitive description: @@ -1312,58 +1474,66 @@ packages: dependency: transitive description: name: path_provider_platform_interface - sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.3.0" permission_handler: dependency: "direct main" description: name: permission_handler - sha256: ad65ba9af42a3d067203641de3fd9f547ded1410bad3b84400c2b4899faede70 + sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb" url: "https://pub.dev" source: hosted - version: "11.0.0" + version: "11.3.1" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: f23cfe9af0d49c6b9fd8a8b09f7b3301ca7e346204939b5afef4404d36d2608f + sha256: "71bbecfee799e65aff7c744761a57e817e73b738fedf62ab7afd5593da21f9f1" url: "https://pub.dev" source: hosted - version: "11.0.1" + version: "12.0.13" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" + sha256: e6f6d73b12438ef13e648c4ae56bd106ec60d17e90a59c4545db6781229082a0 url: "https://pub.dev" source: hosted - version: "9.1.4" + version: "9.4.5" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: af26edbbb1f2674af65a8f4b56e1a6f526156bc273d0e65dd8075fab51c78851 + url: "https://pub.dev" + source: hosted + version: "0.1.3+2" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: f2343e9fa9c22ae4fd92d4732755bfe452214e7189afcc097380950cf567b4b2 + sha256: e9c8eadee926c4532d0305dff94b85bf961f16759c3af791486613152af4b4f9 url: "https://pub.dev" source: hosted - version: "3.11.5" + version: "4.2.3" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "0.2.1" petitparser: dependency: transitive description: @@ -1376,18 +1546,18 @@ packages: dependency: "direct overridden" description: name: pinenacl - sha256: e5fb0bce1717b7f136f35ee98b5c02b3e6383211f8a77ca882fa7812232a07b9 + sha256: "57e907beaacbc3c024a098910b6240758e899674de07d6949a67b52fd984cbdf" url: "https://pub.dev" source: hosted - version: "0.3.4" + version: "0.6.0" platform: dependency: transitive description: name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.5" plugin_platform_interface: dependency: transitive description: @@ -1400,10 +1570,10 @@ packages: dependency: "direct main" description: name: pointycastle - sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" url: "https://pub.dev" source: hosted - version: "3.7.3" + version: "3.9.1" pool: dependency: transitive description: @@ -1416,10 +1586,10 @@ packages: dependency: transitive description: name: pretty_dio_logger - sha256: "00b80053063935cf9a6190da344c5373b9d0e92da4c944c878ff2fbef0ef6dc2" + sha256: "36f2101299786d567869493e2f5731de61ce130faa14679473b26905a92b6407" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.4.0" process: dependency: transitive description: @@ -1432,10 +1602,10 @@ packages: dependency: transitive description: name: protobuf - sha256: "01dd9bd0fa02548bf2ceee13545d4a0ec6046459d847b6b061d8a27237108a08" + sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "3.1.0" pub_semver: dependency: transitive description: @@ -1448,18 +1618,18 @@ packages: dependency: transitive description: name: pubspec_parse - sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 + sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 url: "https://pub.dev" source: hosted - version: "1.2.3" + version: "1.3.0" qr: dependency: transitive description: name: qr - sha256: "64957a3930367bf97cc211a5af99551d630f2f4625e38af10edd6b19131b64b3" + sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" qr_flutter: dependency: "direct main" description: @@ -1472,18 +1642,18 @@ packages: dependency: transitive description: name: quiver - sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 + sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.2.2" rational: dependency: transitive description: name: rational - sha256: ba58e9e18df9abde280e8b10051e4bce85091e41e8e7e411b6cde2e738d357cf + sha256: cb808fb6f1a839e6fc5f7d8cb3b0a10e1db48b3be102de73938c627f0b636336 url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.3" retry: dependency: transitive description: @@ -1520,18 +1690,18 @@ packages: dependency: "direct main" description: name: share_plus - sha256: "6cec740fa0943a826951223e76218df002804adb588235a8910dc3d6b0654e11" + sha256: "3ef39599b00059db0990ca2e30fca0a29d8b37aae924d60063f8e0184cf20900" url: "https://pub.dev" source: hosted - version: "7.1.0" + version: "7.2.2" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface - sha256: "357412af4178d8e11d14f41723f80f12caea54cf0d5cd29af9dcdab85d58aea7" + sha256: "251eb156a8b5fa9ce033747d73535bf53911071f8d3b6f4f0b578505ce0d4496" url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "3.4.0" shelf: dependency: transitive description: @@ -1552,18 +1722,18 @@ packages: dependency: transitive description: name: shelf_static - sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.3" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "2.0.0" sky_engine: dependency: transitive description: flutter @@ -1573,36 +1743,36 @@ packages: dependency: "direct main" description: name: socks5_proxy - sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a" + sha256: e0cba6917cd374de6f6cb0ce081e50e6efc24c61644b8e9f20c8bf8b91bb0b75 url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.3+dev.3" socks_socket: dependency: transitive description: path: "." ref: master - resolved-ref: ac6d721fe655208a6d488a088a35bab0ddc25702 + resolved-ref: b1fa8ca505e7e488edb4c2859f0218d48b15dead url: "https://github.com/cypherstack/socks_socket.git" source: git - version: "0.1.0" + version: "1.0.0" solana: dependency: "direct main" description: path: "packages/solana" - ref: "706be5f166d31736c443cca4cd311b7fcfc2a9bf" - resolved-ref: "706be5f166d31736c443cca4cd311b7fcfc2a9bf" + ref: dea799c20bc917f72b18c916ca96bc99fb1bd1c5 + resolved-ref: dea799c20bc917f72b18c916ca96bc99fb1bd1c5 url: "https://github.com/cypherstack/espresso-cash-public.git" source: git - version: "0.30.4" + version: "0.31.0" source_gen: dependency: transitive description: name: source_gen - sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16 + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.5.0" source_helper: dependency: transitive description: @@ -1615,10 +1785,10 @@ packages: dependency: transitive description: name: source_map_stack_trace - sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" source_maps: dependency: transitive description: @@ -1663,8 +1833,8 @@ packages: dependency: "direct main" description: path: "." - ref: ee1da8a9ba1cbeb50c5b354ea1fd5a25b7c5a3ed - resolved-ref: ee1da8a9ba1cbeb50c5b354ea1fd5a25b7c5a3ed + ref: "5efe8f8f259317d32b6f037cf91f62e06125c040" + resolved-ref: "5efe8f8f259317d32b6f037cf91f62e06125c040" url: "https://github.com/cypherstack/stack_wallet_backup.git" source: git version: "0.0.1" @@ -1680,10 +1850,10 @@ packages: dependency: "direct main" description: name: stellar_flutter_sdk - sha256: "574e8f40a1a1a9b18a735272196c8d3c8168a669efc8460a4d5d6f45151e8dce" + sha256: "7d505963fe11d0f90b3f798964c485ed9fa64731c38f14c9b2fb76d5d5bd6cd8" url: "https://pub.dev" source: hosted - version: "1.7.8" + version: "1.8.1" stream_channel: dependency: "direct main" description: @@ -1736,32 +1906,32 @@ packages: dependency: transitive description: name: test - sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073" + sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" url: "https://pub.dev" source: hosted - version: "1.25.2" + version: "1.25.7" test_api: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" test_core: dependency: transitive description: name: test_core - sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4" + sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.4" tezart: dependency: "direct main" description: path: "." - ref: "13fa937ea9a9fc34caf047e068df9535f65c27ad" - resolved-ref: "13fa937ea9a9fc34caf047e068df9535f65c27ad" + ref: d000cc245e51d3ff50e6467960fb3d9159d5b2a9 + resolved-ref: d000cc245e51d3ff50e6467960fb3d9159d5b2a9 url: "https://github.com/cypherstack/tezart.git" source: git version: "2.0.5" @@ -1769,18 +1939,18 @@ packages: dependency: transitive description: name: time - sha256: "83427e11d9072e038364a5e4da559e85869b227cf699a541be0da74f14140124" + sha256: ad8e018a6c9db36cb917a031853a1aae49467a93e0d464683e029537d848c221 url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" timezone: dependency: transitive description: name: timezone - sha256: "1cfd8ddc2d1cfd836bc93e67b9be88c3adaeca6f40a00ca999104c30693cdca0" + sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d" url: "https://pub.dev" source: hosted - version: "0.9.2" + version: "0.9.4" timing: dependency: transitive description: @@ -1809,8 +1979,8 @@ packages: dependency: "direct main" description: path: "." - ref: "647cadc3c82c276dc07915b02d24538fd610f220" - resolved-ref: "647cadc3c82c276dc07915b02d24538fd610f220" + ref: "534ec251b339199446b723c01a25d324ae7bb974" + resolved-ref: "534ec251b339199446b723c01a25d324ae7bb974" url: "https://github.com/cypherstack/tor.git" source: git version: "0.0.1" @@ -1826,10 +1996,10 @@ packages: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" universal_io: dependency: transitive description: @@ -1850,66 +2020,66 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27" + sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" url: "https://pub.dev" source: hosted - version: "6.1.14" + version: "6.3.1" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330 + sha256: "6fc2f56536ee873eeb867ad176ae15f304ccccc357848b351f6f0d8d4a40d193" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.3.14" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f" + sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e url: "https://pub.dev" source: hosted - version: "6.1.5" + version: "6.3.1" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: b651aad005e0cb06a01dbd84b428a301916dc75f0e7ea6165f80057fee2d8e8e + sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.2.0" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88 + sha256: "769549c999acdb42b8bcfa7c43d72bf79a382ca7441ab18a808e101149daf672" url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "3.2.1" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618" + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.3.2" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: ba140138558fcc3eead51a1c42e92a9fb074a1b1149ed3c73e66035b2ccd94f2 + sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" url: "https://pub.dev" source: hosted - version: "2.0.19" + version: "2.3.3" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "95fef3129dc7cfaba2bc3d5ba2e16063bb561fc6d78e63eee16162bc70029069" + sha256: "44cf3aabcedde30f2dba119a9dea3b0f2672fbe6fa96e85536251d678216b3c4" url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.1.3" uuid: dependency: "direct main" description: @@ -1922,26 +2092,26 @@ packages: dependency: transitive description: name: vector_graphics - sha256: "670f6e07aca990b4a2bcdc08a784193c4ccdd1932620244c3a86bb72a0eac67f" + sha256: "0b9149c6ddb013818075b072b9ddc1b89a5122fff1275d4648d297086b46c4f0" url: "https://pub.dev" source: hosted - version: "1.1.7" + version: "1.1.12" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: "7451721781d967db9933b63f5733b1c4533022c0ba373a01bdd79d1a5457f69f" + sha256: "2430b973a4ca3c4dbc9999b62b8c719a160100dcbae5c819bae0cacce32c9cdb" url: "https://pub.dev" source: hosted - version: "1.1.7" + version: "1.1.12" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: "80a13c613c8bde758b1464a1755a7b3a8f2b6cec61fbf0f5a53c94c30f03ba2e" + sha256: f3b9b6e4591c11394d4be4806c63e72d3a41778547b2c1e2a8a04fadcfd7d173 url: "https://pub.dev" source: hosted - version: "1.1.7" + version: "1.1.12" vector_math: dependency: transitive description: @@ -1954,26 +2124,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.1" - wakelock: - dependency: "direct main" - description: - name: wakelock - sha256: "769ecf42eb2d07128407b50cb93d7c10bd2ee48f0276ef0119db1d25cc2f87db" - url: "https://pub.dev" - source: hosted - version: "0.6.2" - wakelock_macos: - dependency: transitive - description: - name: wakelock_macos - sha256: "047c6be2f88cb6b76d02553bca5a3a3b95323b15d30867eca53a19a0a319d4cd" - url: "https://pub.dev" - source: hosted - version: "0.4.0" + version: "14.2.5" wakelock_platform_interface: dependency: transitive description: @@ -1982,14 +2136,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.0" - wakelock_web: - dependency: transitive + wakelock_plus: + dependency: "direct main" description: - name: wakelock_web - sha256: "1b256b811ee3f0834888efddfe03da8d18d0819317f20f6193e2922b41a501b5" + name: wakelock_plus + sha256: bf4ee6f17a2fa373ed3753ad0e602b7603f8c75af006d5b9bdade263928c0484 url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "1.2.8" + wakelock_plus_platform_interface: + dependency: transitive + description: + name: wakelock_plus_platform_interface + sha256: "422d1cdbb448079a8a62a5a770b69baa489f8f7ca21aef47800c726d404f9d16" + url: "https://pub.dev" + source: hosted + version: "1.2.1" wakelock_windows: dependency: "direct overridden" description: @@ -2003,10 +2165,10 @@ packages: dependency: transitive description: name: wallet - sha256: "569c91c2af13a9e1119c001f9c09218eccf3f383eb8d15ba13a5b558010c1bc0" + sha256: "687fd89a16557649b26189e597792962f405797fc64113e8758eabc2c2605c32" url: "https://pub.dev" source: hosted - version: "0.0.12+1" + version: "0.0.13" wasm_interop: dependency: transitive description: @@ -2064,27 +2226,27 @@ packages: source: hosted version: "1.2.1" win32: - dependency: transitive + dependency: "direct overridden" description: name: win32 - sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" + sha256: "10169d3934549017f0ae278ccb07f828f9d6ea21573bab0fb77b0e1ef0fce454" url: "https://pub.dev" source: hosted - version: "5.2.0" + version: "5.7.2" win32_registry: dependency: transitive description: name: win32_registry - sha256: e4506d60b7244251bc59df15656a3093501c37fb5af02105a944d73eb95be4c9 + sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.5" window_size: dependency: "direct main" description: path: "plugins/window_size" ref: HEAD - resolved-ref: "6c66ad23ee79749f30a8eece542cf54eaf157ed8" + resolved-ref: eb3964990cf19629c89ff8cb4a37640c7b3d5601 url: "https://github.com/google/flutter-desktop-embedding.git" source: git version: "0.1.0" @@ -2092,10 +2254,10 @@ packages: dependency: transitive description: name: xdg_directories - sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.1.0" xml: dependency: transitive description: @@ -2108,10 +2270,10 @@ packages: dependency: transitive description: name: xxh3 - sha256: a92b30944a9aeb4e3d4f3c3d4ddb3c7816ca73475cd603682c4f8149690f56d7 + sha256: cbeb0e1d10f4c6bf67b650f395eac0cc689425b5efc2ba0cc3d3e069a0beaeec url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.1.0" yaml: dependency: transitive description: @@ -2128,6 +2290,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + zxing2: + dependency: "direct main" + description: + name: zxing2 + sha256: "6cf995abd3c86f01ba882968dedffa7bc130185e382f2300239d2e857fc7912c" + url: "https://pub.dev" + source: hosted + version: "0.2.3" sdks: - dart: ">=3.3.4 <4.0.0" - flutter: ">=3.19.6" + dart: ">=3.5.3 <4.0.0" + flutter: ">=3.24.3" diff --git a/scripts/android/build_all.sh b/scripts/android/build_all.sh index 7cb2e083d..60aa13c6a 100755 --- a/scripts/android/build_all.sh +++ b/scripts/android/build_all.sh @@ -14,7 +14,6 @@ PLUGINS_DIR=../../crypto_plugins (cd "${PLUGINS_DIR}"/flutter_liblelantus/scripts/android && ./build_all.sh ) (cd "${PLUGINS_DIR}"/flutter_libepiccash/scripts/android && ./build_all.sh ) -(cd "${PLUGINS_DIR}"/flutter_libmonero/scripts/android/ && ./build_all.sh ) set_rust_to_1720 (cd "${PLUGINS_DIR}"/frostdart/scripts/android && ./build_all.sh ) diff --git a/scripts/android/build_all_campfire.sh b/scripts/android/build_all_campfire.sh index 7cb2e083d..60aa13c6a 100755 --- a/scripts/android/build_all_campfire.sh +++ b/scripts/android/build_all_campfire.sh @@ -14,7 +14,6 @@ PLUGINS_DIR=../../crypto_plugins (cd "${PLUGINS_DIR}"/flutter_liblelantus/scripts/android && ./build_all.sh ) (cd "${PLUGINS_DIR}"/flutter_libepiccash/scripts/android && ./build_all.sh ) -(cd "${PLUGINS_DIR}"/flutter_libmonero/scripts/android/ && ./build_all.sh ) set_rust_to_1720 (cd "${PLUGINS_DIR}"/frostdart/scripts/android && ./build_all.sh ) diff --git a/scripts/android/build_all_duo.sh b/scripts/android/build_all_duo.sh index aec2ebbb3..d67e700a8 100755 --- a/scripts/android/build_all_duo.sh +++ b/scripts/android/build_all_duo.sh @@ -16,7 +16,6 @@ PLUGINS_DIR=../../crypto_plugins (cd "${PLUGINS_DIR}"/flutter_liblelantus/scripts/android && ./build_all.sh ) (cd "${PLUGINS_DIR}"/flutter_libepiccash/scripts/android && ./build_all.sh ) -(cd "${PLUGINS_DIR}"/flutter_libmonero/scripts/android/ && ./build_all.sh ) set_rust_to_1720 (cd "${PLUGINS_DIR}"/frostdart/scripts/android && ./build_all.sh ) diff --git a/scripts/app_config/configure_campfire.sh b/scripts/app_config/configure_campfire.sh index ae6bba36d..883d67fa0 100755 --- a/scripts/app_config/configure_campfire.sh +++ b/scripts/app_config/configure_campfire.sh @@ -49,7 +49,9 @@ const _appDataDirName = "campfire"; const _shortDescriptionText = "Your privacy. Your wallet. Your Firo."; const _commitHash = "$BUILT_COMMIT_HASH"; -const Set _features = {}; +const Set _features = { + AppFeature.swap +}; const ({String light, String dark})? _appIconAsset = ( light: "assets/in_app_logo_icons/campfire-icon_light.svg", @@ -60,4 +62,6 @@ final List _supportedCoins = List.unmodifiable([ Firo(CryptoCurrencyNetwork.main), ]); +final ({String from, String to}) _swapDefaults = (from: "BTC", to: "FIRO"); + EOF \ No newline at end of file diff --git a/scripts/app_config/configure_stack_duo.sh b/scripts/app_config/configure_stack_duo.sh index 143faf644..7d1a7665a 100755 --- a/scripts/app_config/configure_stack_duo.sh +++ b/scripts/app_config/configure_stack_duo.sh @@ -64,4 +64,6 @@ final List _supportedCoins = List.unmodifiable([ BitcoinFrost(CryptoCurrencyNetwork.test4), ]); +final ({String from, String to}) _swapDefaults = (from: "BTC", to: "XMR"); + EOF \ No newline at end of file diff --git a/scripts/app_config/configure_stack_wallet.sh b/scripts/app_config/configure_stack_wallet.sh index 7f7c51ab7..0fd8e5e8a 100755 --- a/scripts/app_config/configure_stack_wallet.sh +++ b/scripts/app_config/configure_stack_wallet.sh @@ -57,6 +57,7 @@ final List _supportedCoins = List.unmodifiable([ Banano(CryptoCurrencyNetwork.main), Bitcoincash(CryptoCurrencyNetwork.main), BitcoinFrost(CryptoCurrencyNetwork.main), + Cardano(CryptoCurrencyNetwork.main), Dash(CryptoCurrencyNetwork.main), Dogecoin(CryptoCurrencyNetwork.main), Ecash(CryptoCurrencyNetwork.main), @@ -84,4 +85,6 @@ final List _supportedCoins = List.unmodifiable([ Stellar(CryptoCurrencyNetwork.test), ]); +final ({String from, String to}) _swapDefaults = (from: "BTC", to: "XMR"); + EOF \ No newline at end of file diff --git a/scripts/app_config/templates/android/app/build.gradle b/scripts/app_config/templates/android/app/build.gradle index e440e9d0d..b9cebc7b4 100644 --- a/scripts/app_config/templates/android/app/build.gradle +++ b/scripts/app_config/templates/android/app/build.gradle @@ -1,30 +1,10 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } +plugins { + id "com.android.application" + id "kotlin-android" + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id "dev.flutter.flutter-gradle-plugin" } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - def keystoreProperties = new Properties() def keystorePropertiesFile = rootProject.file('key.properties') if (keystorePropertiesFile.exists()) { @@ -32,47 +12,51 @@ if (keystorePropertiesFile.exists()) { } android { - compileSdkVersion 34 + namespace "com.place.holder" + compileSdk flutter.compileSdkVersion +// ndkVersion flutter.ndkVersion + ndkVersion = "26.1.10909125" -// ndkVersion = "21.1.6352462" -// ndkVersion = "25.2.9519653" - ndkVersion = "23.1.7779620" - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - lintOptions { - disable 'InvalidPackage' - } packagingOptions { pickFirst 'lib/x86/libc++_shared.so' pickFirst 'lib/x86_64/libc++_shared.so' pickFirst 'lib/armeabi-v7a/libc++_shared.so' pickFirst 'lib/arm64-v8a/libc++_shared.so' } + defaultConfig { applicationId "com.place.holder" - minSdkVersion 23 - targetSdkVersion 33 -// ndkVersion = "21.1.6352462" -// ndkVersion = "25.2.9519653" - ndkVersion = "23.1.7779620" - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + minSdk flutter.minSdkVersion + targetSdk flutter.targetSdkVersion + versionCode flutter.versionCode + versionName flutter.versionName + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + coreLibraryDesugaringEnabled true + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17 + } + + dependencies { + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4") + } + ndk { abiFilters "x86_64","armeabi-v7a", "arm64-v8a" } - externalNativeBuild { - cmake { - arguments "-DANDROID_STL=c++_shared", '-DBUILD_TESTING=OFF', "-DANDROID_TOOLCHAIN=clang -v" - cppFlags "-frtti -fexceptions -v -DANDROID -std=c++17" -// cppFlags "-std=c++11" - version "3.10.2" - } - } +// externalNativeBuild { +// cmake { +// arguments "-DANDROID_STL=c++_shared", '-DBUILD_TESTING=OFF', "-DANDROID_TOOLCHAIN=clang -v" +// cppFlags "-frtti -fexceptions -v -DANDROID -std=c++17" +//// cppFlags "-std=c++11" +// version "3.10.2" +// } +// } } signingConfigs { @@ -99,15 +83,18 @@ android { 'proguard-rules.pro' } } + + // not required. Just info + tasks.whenTaskAdded { task -> + if (task.name == 'assembleDebug') { + task.doFirst { + println "The compileSdkVersion is $flutter.compileSdkVersion" + println "The targetSdkVersion is $flutter.targetSdkVersion" + } + } + } } flutter { source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' -} +} \ No newline at end of file diff --git a/scripts/app_config/templates/android/app/src/main/AndroidManifest.xml b/scripts/app_config/templates/android/app/src/main/AndroidManifest.xml index 2ae40734c..78a92aa5c 100644 --- a/scripts/app_config/templates/android/app/src/main/AndroidManifest.xml +++ b/scripts/app_config/templates/android/app/src/main/AndroidManifest.xml @@ -74,4 +74,14 @@ + + + + + + + \ No newline at end of file diff --git a/scripts/app_config/templates/ios/Runner.xcodeproj/project.pbxproj b/scripts/app_config/templates/ios/Runner.xcodeproj/project.pbxproj index 4b521cca2..c89885057 100644 --- a/scripts/app_config/templates/ios/Runner.xcodeproj/project.pbxproj +++ b/scripts/app_config/templates/ios/Runner.xcodeproj/project.pbxproj @@ -18,8 +18,6 @@ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; B49D91439948369648AB0603 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51604430FD0FD1FA5C4767A0 /* Pods_Runner.framework */; }; - CEFE41202C20387E00086DB4 /* WowneroWallet.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE4F88332C202CF4007A8C67 /* WowneroWallet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - CEFE41212C20387E00086DB4 /* MoneroWallet.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE4F88302C202CEE007A8C67 /* MoneroWallet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -34,17 +32,6 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; - CE6B5DEA2BF26A3300CF1F44 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - CEFE41202C20387E00086DB4 /* WowneroWallet.framework in CopyFiles */, - CEFE41212C20387E00086DB4 /* MoneroWallet.framework in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -192,9 +179,9 @@ 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, - CE6B5DEA2BF26A3300CF1F44 /* CopyFiles */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, FD1CA371131604E6658D4146 /* [CP] Embed Pods Frameworks */, + 4823D12ED8C268BC85F26ABD /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -271,6 +258,24 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n"; }; + 4823D12ED8C268BC85F26ABD /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/permission_handler_apple/permission_handler_apple_privacy.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/permission_handler_apple_privacy.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -325,10 +330,12 @@ "${BUILT_PRODUCTS_DIR}/barcode_scan2/barcode_scan2.framework", "${BUILT_PRODUCTS_DIR}/coinlib_flutter/secp256k1.framework", "${BUILT_PRODUCTS_DIR}/connectivity_plus/connectivity_plus.framework", + "${PODS_ROOT}/../.symlinks/plugins/cs_monero_flutter_libs/ios/Frameworks/MoneroWallet.framework", + "${PODS_ROOT}/../.symlinks/plugins/cs_monero_flutter_libs/ios/Frameworks/WowneroWallet.framework", + "${BUILT_PRODUCTS_DIR}/cs_monero_flutter_libs/cs_monero_flutter_libs.framework", "${BUILT_PRODUCTS_DIR}/device_info_plus/device_info_plus.framework", "${BUILT_PRODUCTS_DIR}/devicelocale/devicelocale.framework", "${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework", - "${BUILT_PRODUCTS_DIR}/flutter_libmonero/flutter_libmonero.framework", "${PODS_ROOT}/../.symlinks/plugins/flutter_libsparkmobile/ios/flutter_libsparkmobile.framework", "${BUILT_PRODUCTS_DIR}/flutter_local_notifications/flutter_local_notifications.framework", "${BUILT_PRODUCTS_DIR}/flutter_native_splash/flutter_native_splash.framework", @@ -337,7 +344,7 @@ "${BUILT_PRODUCTS_DIR}/integration_test/integration_test.framework", "${BUILT_PRODUCTS_DIR}/isar_flutter_libs/isar_flutter_libs.framework", "${BUILT_PRODUCTS_DIR}/lelantus/lelantus.framework", - "${BUILT_PRODUCTS_DIR}/local_auth/local_auth.framework", + "${BUILT_PRODUCTS_DIR}/local_auth_darwin/local_auth_darwin.framework", "${BUILT_PRODUCTS_DIR}/package_info_plus/package_info_plus.framework", "${BUILT_PRODUCTS_DIR}/path_provider_foundation/path_provider_foundation.framework", "${BUILT_PRODUCTS_DIR}/share_plus/share_plus.framework", @@ -346,7 +353,7 @@ "${BUILT_PRODUCTS_DIR}/stack_wallet_backup/stack_wallet_backup.framework", "${BUILT_PRODUCTS_DIR}/tor_ffi_plugin/tor_ffi_plugin.framework", "${BUILT_PRODUCTS_DIR}/url_launcher_ios/url_launcher_ios.framework", - "${BUILT_PRODUCTS_DIR}/wakelock/wakelock.framework", + "${BUILT_PRODUCTS_DIR}/wakelock_plus/wakelock_plus.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( @@ -360,10 +367,12 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/barcode_scan2.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/secp256k1.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity_plus.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MoneroWallet.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WowneroWallet.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cs_monero_flutter_libs.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info_plus.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/devicelocale.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_libmonero.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_libsparkmobile.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_local_notifications.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_native_splash.framework", @@ -372,7 +381,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/integration_test.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/isar_flutter_libs.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/lelantus.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/local_auth.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/local_auth_darwin.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info_plus.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_foundation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share_plus.framework", @@ -381,7 +390,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/stack_wallet_backup.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/tor_ffi_plugin.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_ios.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/wakelock.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/wakelock_plus.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -489,7 +498,6 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/../crypto_plugins/flutter_liblelantus/scripts/ios/mobileliblelantus", - "$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios", "$(PROJECT_DIR)", ); HEADER_SEARCH_PATHS = ( @@ -501,8 +509,6 @@ "\"${PODS_CONFIGURATION_BUILD_DIR}/barcode_scan2/barcode_scan2.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/connectivity/connectivity.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/cryptography_flutter/cryptography_flutter.framework/Headers\"", - "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_monero/cw_monero.framework/Headers\"", - "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_shared_external/cw_shared_external.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/devicelocale/devicelocale.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libepiccash/flutter_libepiccash.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libmonero/flutter_libmonero.framework/Headers\"", @@ -520,12 +526,6 @@ "\"${PODS_CONFIGURATION_BUILD_DIR}/stack_wallet_backup/stack_wallet_backup.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/url_launcher_ios/url_launcher_ios.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/wakelock/wakelock.framework/Headers\"", - "${PODS_ROOT}/cw_monero/Classes/*.h", - "${PODS_ROOT}/cw_monero/Classes/*.h", - "${PODS_ROOT}/cw_monero/External/ios/include/**", - "${PODS_ROOT}/cw_monero/Classes/*.h", - "${PODS_ROOT}/cw_monero/External/ios/include", - "${PODS_ROOT}/cw_shared_external/External/ios/include/**", "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/include", ); INFOPLIST_FILE = Runner/Info.plist; @@ -537,7 +537,6 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", - "$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**", "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs", ); MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)"; @@ -681,7 +680,6 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/../crypto_plugins/flutter_liblelantus/scripts/ios/mobileliblelantus", - "$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**", "$(PROJECT_DIR)", ); HEADER_SEARCH_PATHS = ( @@ -693,8 +691,6 @@ "\"${PODS_CONFIGURATION_BUILD_DIR}/barcode_scan2/barcode_scan2.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/connectivity/connectivity.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/cryptography_flutter/cryptography_flutter.framework/Headers\"", - "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_monero/cw_monero.framework/Headers\"", - "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_shared_external/cw_shared_external.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/devicelocale/devicelocale.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libepiccash/flutter_libepiccash.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libmonero/flutter_libmonero.framework/Headers\"", @@ -712,12 +708,6 @@ "\"${PODS_CONFIGURATION_BUILD_DIR}/stack_wallet_backup/stack_wallet_backup.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/url_launcher_ios/url_launcher_ios.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/wakelock/wakelock.framework/Headers\"", - "${PODS_ROOT}/cw_monero/Classes/*.h", - "${PODS_ROOT}/cw_monero/Classes/*.h", - "${PODS_ROOT}/cw_monero/External/ios/include/**", - "${PODS_ROOT}/cw_monero/Classes/*.h", - "${PODS_ROOT}/cw_monero/External/ios/include", - "${PODS_ROOT}/cw_shared_external/External/ios/include/**", "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/include", ); INFOPLIST_FILE = Runner/Info.plist; @@ -729,7 +719,6 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", - "$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**", "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs", ); MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)"; @@ -765,7 +754,6 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/../crypto_plugins/flutter_liblelantus/scripts/ios/mobileliblelantus/**", - "$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios", "$(PROJECT_DIR)", ); HEADER_SEARCH_PATHS = ( @@ -777,8 +765,6 @@ "\"${PODS_CONFIGURATION_BUILD_DIR}/barcode_scan2/barcode_scan2.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/connectivity/connectivity.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/cryptography_flutter/cryptography_flutter.framework/Headers\"", - "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_monero/cw_monero.framework/Headers\"", - "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_shared_external/cw_shared_external.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/devicelocale/devicelocale.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libepiccash/flutter_libepiccash.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libmonero/flutter_libmonero.framework/Headers\"", @@ -796,12 +782,6 @@ "\"${PODS_CONFIGURATION_BUILD_DIR}/stack_wallet_backup/stack_wallet_backup.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/url_launcher_ios/url_launcher_ios.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/wakelock/wakelock.framework/Headers\"", - "${PODS_ROOT}/cw_monero/Classes/*.h", - "${PODS_ROOT}/cw_monero/Classes/*.h", - "${PODS_ROOT}/cw_monero/External/ios/include/**", - "${PODS_ROOT}/cw_monero/Classes/*.h", - "${PODS_ROOT}/cw_monero/External/ios/include", - "${PODS_ROOT}/cw_shared_external/External/ios/include/**", "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/include", ); INFOPLIST_FILE = Runner/Info.plist; @@ -813,7 +793,6 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", - "$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**", "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs", ); MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)"; diff --git a/scripts/app_config/templates/linux/CMakeLists.txt b/scripts/app_config/templates/linux/CMakeLists.txt index a13bec888..25750ef4f 100644 --- a/scripts/app_config/templates/linux/CMakeLists.txt +++ b/scripts/app_config/templates/linux/CMakeLists.txt @@ -134,12 +134,6 @@ install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR} install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/monero/x86_64-linux-gnu_libwallet2_api_c.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "monero_libwallet2_api_c.so" - COMPONENT Runtime) - -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-linux-gnu_libwallet2_api_c.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "wownero_libwallet2_api_c.so" - COMPONENT Runtime) - install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libepiccash/scripts/linux/build/rust/target/x86_64-unknown-linux-gnu/release/libepic_cash_wallet.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) diff --git a/scripts/app_config/templates/macos/Runner.xcodeproj/project.pbxproj b/scripts/app_config/templates/macos/Runner.xcodeproj/project.pbxproj index 7049193bb..b0e43be79 100644 --- a/scripts/app_config/templates/macos/Runner.xcodeproj/project.pbxproj +++ b/scripts/app_config/templates/macos/Runner.xcodeproj/project.pbxproj @@ -31,8 +31,6 @@ B98151822A67402A009D013C /* mobileliblelantus.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = B98151802A674022009D013C /* mobileliblelantus.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; B98151842A674143009D013C /* libsqlite3.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = B98151832A674143009D013C /* libsqlite3.0.tbd */; }; BFD0376C00E1FFD46376BB9D /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9206484E84CB0AD93E3E68CA /* Pods_RunnerTests.framework */; }; - CEA2021D2BDD4D7100FE1D27 /* wownero_libwallet2_api_c.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = CEA2021C2BDD4D7100FE1D27 /* wownero_libwallet2_api_c.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - CEA202202BDD4F0B00FE1D27 /* monero_libwallet2_api_c.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = CEA2021F2BDD4F0B00FE1D27 /* monero_libwallet2_api_c.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; F1FA2C4E2BA4B49F00BDA1BB /* frostdart.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F1FA2C4D2BA4B49F00BDA1BB /* frostdart.dylib */; settings = {ATTRIBUTES = (Weak, ); }; }; F1FA2C502BA4B4CA00BDA1BB /* frostdart.dylib in Resources */ = {isa = PBXBuildFile; fileRef = F1FA2C4F2BA4B4CA00BDA1BB /* frostdart.dylib */; }; F1FA2C512BA4B51E00BDA1BB /* frostdart.dylib in Bundle Framework */ = {isa = PBXBuildFile; fileRef = F1FA2C4D2BA4B49F00BDA1BB /* frostdart.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; @@ -69,17 +67,6 @@ name = "Bundle Framework"; runOnlyForDeploymentPostprocessing = 0; }; - CEA202192BDD4C3500FE1D27 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 12; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - CEA202202BDD4F0B00FE1D27 /* monero_libwallet2_api_c.dylib in CopyFiles */, - CEA2021D2BDD4D7100FE1D27 /* wownero_libwallet2_api_c.dylib in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -276,7 +263,6 @@ 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, 529691D83C3BADE14E2EAC03 /* [CP] Embed Pods Frameworks */, - CEA202192BDD4C3500FE1D27 /* CopyFiles */, ); buildRules = ( ); diff --git a/scripts/app_config/templates/pubspec.template b/scripts/app_config/templates/pubspec.template index a784dbabd..862731c6e 100644 --- a/scripts/app_config/templates/pubspec.template +++ b/scripts/app_config/templates/pubspec.template @@ -14,15 +14,15 @@ description: PLACEHOLDER version: PLACEHOLDER_V+PLACEHOLDER_B environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ^3.19.6 + sdk: ">=3.5.3 <4.0.0" + flutter: ^3.24.3 dependencies: flutter: sdk: flutter ffi: ^2.0.1 mutex: ^3.0.0 - web_socket_channel: ^2.4.5 + web_socket_channel: ^2.4.0 lelantus: path: ./crypto_plugins/flutter_liblelantus @@ -33,24 +33,14 @@ dependencies: flutter_libsparkmobile: git: url: https://github.com/cypherstack/flutter_libsparkmobile.git - ref: 7a11d0cadf8c7a6a5d5144dab18cef9536aa5943 + ref: 9318bdd8e76e4dc8a49e3d64e8851c85e017eff3 - flutter_libmonero: - path: ./crypto_plugins/flutter_libmonero - - cw_monero: - path: ./crypto_plugins/flutter_libmonero/cw_monero - - cw_wownero: - path: ./crypto_plugins/flutter_libmonero/cw_wownero - - cw_core: - path: ./crypto_plugins/flutter_libmonero/cw_core - - monero: + # cs_monero compat (unpublished) + compat: git: - url: https://github.com/mrcyjanek/monero.dart - ref: d46753eca865e9e56c2f0ef6fe485c42e11982c5 + url: https://github.com/cypherstack/cs_monero + path: compat + ref: b2b12cfeaf15d057ea18b14c196e04f3002de2da flutter_libepiccash: path: ./crypto_plugins/flutter_libepiccash @@ -63,7 +53,7 @@ dependencies: stack_wallet_backup: git: url: https://github.com/cypherstack/stack_wallet_backup.git - ref: ee1da8a9ba1cbeb50c5b354ea1fd5a25b7c5a3ed + ref: 5efe8f8f259317d32b6f037cf91f62e06125c040 bip47: git: @@ -78,13 +68,13 @@ dependencies: fusiondart: git: url: https://github.com/cypherstack/fusiondart.git - ref: 7dd8ff0dc9cb0caaac795fa44841a26437edfec3 + ref: 9dc883f4432c8db4ec44cb8cc836963295d63952 # Utility plugins http: ^0.13.0 - local_auth: ^1.1.10 + local_auth: ^2.3.0 permission_handler: ^11.0.0 - flutter_local_notifications: ^17.0.0 + flutter_local_notifications: ^17.2.2 rxdart: ^0.27.3 zxcvbn: ^1.0.0 dart_numerics: ^0.0.6 @@ -130,11 +120,11 @@ dependencies: event_bus: ^2.0.0 uuid: ^3.0.5 crypto: ^3.0.2 - barcode_scan2: ^4.2.3 - wakelock: ^0.6.2 + barcode_scan2: ^4.3.3 + wakelock_plus: ^1.2.8 intl: ^0.17.0 - devicelocale: ^0.6.0 - device_info_plus: ^9.0.2 + devicelocale: ^0.7.1 + device_info_plus: ^10.1.2 keyboard_dismisser: ^3.0.0 another_flushbar: ^1.10.28 tuple: ^2.0.0 @@ -143,12 +133,16 @@ dependencies: share_plus: ^7.0.2 emojis: ^0.9.9 pointycastle: ^3.6.0 - package_info_plus: ^4.0.2 + package_info_plus: ^8.0.2 lottie: ^2.3.2 file_picker: ^8.0.3 connectivity_plus: ^4.0.1 - isar: 3.0.5 - isar_flutter_libs: 3.0.5 # contains the binaries + isar: + version: 3.1.8 + hosted: https://pub.isar-community.dev/ + isar_flutter_libs: # contains Isar Core + version: 3.1.8 + hosted: https://pub.isar-community.dev/ dropdown_button2: ^2.1.3 string_validator: ^0.3.0 equatable: ^2.0.5 @@ -156,8 +150,8 @@ dependencies: dart_bs58: ^1.0.1 dart_bs58check: ^3.0.2 hex: ^0.2.0 - archive: ^3.3.2 - desktop_drop: ^0.4.1 + archive: ^3.6.1 + desktop_drop: ^0.4.4 nanodart: ^2.0.0 basic_utils: ^5.5.4 stellar_flutter_sdk: ^1.7.8 @@ -166,16 +160,16 @@ dependencies: tezart: git: url: https://github.com/cypherstack/tezart.git - ref: 13fa937ea9a9fc34caf047e068df9535f65c27ad - socks5_proxy: ^1.0.3+dev.3 + ref: d000cc245e51d3ff50e6467960fb3d9159d5b2a9 + socks5_proxy: 1.0.3+dev.3 convert: ^3.1.1 flutter_hooks: ^0.20.3 meta: ^1.9.1 # coinlib_flutter: ^2.1.0-rc.1 coinlib_flutter: git: - url: https://github.com/peercoin/coinlib.git - ref: b88e68e2e10ffe54d802deeed6b9e83da7721a1f + url: https://github.com/julian-CStack/coinlib.git + ref: 0acacfd17eacf72135c693a7b862bd9b7cc56739 path: coinlib_flutter electrum_adapter: git: @@ -185,11 +179,24 @@ dependencies: solana: git: # TODO [prio=low]: Revert to official package once Tor support is merged upstream. url: https://github.com/cypherstack/espresso-cash-public.git - ref: 706be5f166d31736c443cca4cd311b7fcfc2a9bf + ref: dea799c20bc917f72b18c916ca96bc99fb1bd1c5 path: packages/solana calendar_date_picker2: ^1.0.2 - sqlite3: ^2.4.3 - sqlite3_flutter_libs: ^0.5.22 + sqlite3: 2.4.3 + sqlite3_flutter_libs: 0.5.22 + camera_linux: ^0.0.8 + zxing2: ^0.2.3 + camera_windows: + git: # TODO [prio=low]: Revert to official after https://github.com/flutter/packages/pull/7067. + url: https://github.com/cypherstack/packages.git + path: packages/camera/camera_windows + camera_platform_interface: ^2.8.0 + camera_macos: ^0.0.8 + blockchain_utils: ^3.3.0 + on_chain: ^4.0.1 + cbor: ^6.3.3 + cs_monero: 1.0.0-pre + cs_monero_flutter_libs: 1.0.0-pre.0 dev_dependencies: flutter_test: @@ -204,10 +211,12 @@ dev_dependencies: mockito: ^5.4.1 mockingjay: ^0.2.0 # lint: ^1.10.0 - analyzer: ^5.13.0 + analyzer: ^6.7.0 import_sorter: ^4.6.0 flutter_lints: ^3.0.1 - isar_generator: 3.0.5 + isar_generator: + version: 3.1.8 + hosted: https://pub.isar-community.dev/ flutter_native_splash: image: assets/icon/splash.png @@ -216,29 +225,27 @@ flutter_native_splash: dependency_overrides: + # needed for dart 3.5+ (at least for now) + win32: ^5.5.4 + # coin lib git for testing while waiting for publishing coinlib: git: - url: https://github.com/peercoin/coinlib.git - ref: b88e68e2e10ffe54d802deeed6b9e83da7721a1f + url: https://github.com/julian-CStack/coinlib.git + ref: 0acacfd17eacf72135c693a7b862bd9b7cc56739 path: coinlib coinlib_flutter: git: - url: https://github.com/peercoin/coinlib.git - ref: b88e68e2e10ffe54d802deeed6b9e83da7721a1f + url: https://github.com/julian-CStack/coinlib.git + ref: 0acacfd17eacf72135c693a7b862bd9b7cc56739 path: coinlib_flutter # adding here due to pure laziness tor_ffi_plugin: git: url: https://github.com/cypherstack/tor.git - ref: 647cadc3c82c276dc07915b02d24538fd610f220 - - monero: - git: - url: https://github.com/mrcyjanek/monero.dart - ref: d46753eca865e9e56c2f0ef6fe485c42e11982c5 + ref: 534ec251b339199446b723c01a25d324ae7bb974 bip47: git: @@ -252,12 +259,6 @@ dependency_overrides: ref: 2a9bca63a540771f241d688562351482b2cf234c path: wakelock_windows - # required for libmonero - can remove once libmonero has been updated, PR has been merged in swb - stack_wallet_backup: - git: - url: https://github.com/cypherstack/stack_wallet_backup.git - ref: ee1da8a9ba1cbeb50c5b354ea1fd5a25b7c5a3ed - # required override for nanodart bip39: git: @@ -271,8 +272,8 @@ dependency_overrides: ref: 9abc0930081c9859884e073bd25ab88b2114d9e7 crypto: 3.0.2 - analyzer: ^5.2.0 - pinenacl: ^0.3.3 + analyzer: ^6.7.0 + pinenacl: ^0.6.0 http: ^0.13.0 # For information on the generic Dart part of this file, see the @@ -322,30 +323,6 @@ flutter: # default themes_testing - assets/default_themes/ - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages - import_sorter: comments: false # Optional, defaults to true ignored_files: # Optional, defaults to [] diff --git a/scripts/app_config/templates/windows/CMakeLists.txt b/scripts/app_config/templates/windows/CMakeLists.txt index 52af5ebd2..b9add856d 100644 --- a/scripts/app_config/templates/windows/CMakeLists.txt +++ b/scripts/app_config/templates/windows/CMakeLists.txt @@ -86,27 +86,6 @@ install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libepiccash install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_liblelantus/scripts/windows/build/libmobileliblelantus.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/monero/x86_64-w64-mingw32_libwallet2_api_c.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "monero_libwallet2_api_c.dll" - COMPONENT Runtime) - -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-w64-mingw32_libwallet2_api_c.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "wownero_libwallet2_api_c.dll" - COMPONENT Runtime) - -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-w64-mingw32_libgcc_s_seh-1.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "libgcc_s_seh-1.dll" - COMPONENT Runtime) - -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-w64-mingw32_libpolyseed.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "libpolyseed.dll" - COMPONENT Runtime) - -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-w64-mingw32_libssp-0.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "libssp-0.dll" - COMPONENT Runtime) - -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-w64-mingw32_libstdc++-6.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "libstdc++-6.dll" - COMPONENT Runtime) - -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-w64-mingw32_libwinpthread-1.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "libwinpthread-1.dll" - COMPONENT Runtime) - if(PLUGIN_BUNDLED_LIBRARIES) install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" diff --git a/scripts/ios/build_all.sh b/scripts/ios/build_all.sh index b34724561..bcb03e991 100755 --- a/scripts/ios/build_all.sh +++ b/scripts/ios/build_all.sh @@ -16,7 +16,6 @@ rustup target add x86_64-apple-ios (cd ../../crypto_plugins/flutter_liblelantus/scripts/ios && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/ios && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/ios/ && ./build_all.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/ios && ./build_all.sh ) diff --git a/scripts/ios/build_all_campfire.sh b/scripts/ios/build_all_campfire.sh index b34724561..bcb03e991 100755 --- a/scripts/ios/build_all_campfire.sh +++ b/scripts/ios/build_all_campfire.sh @@ -16,7 +16,6 @@ rustup target add x86_64-apple-ios (cd ../../crypto_plugins/flutter_liblelantus/scripts/ios && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/ios && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/ios/ && ./build_all.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/ios && ./build_all.sh ) diff --git a/scripts/ios/build_all_duo.sh b/scripts/ios/build_all_duo.sh index 387f85f81..89e6f4641 100755 --- a/scripts/ios/build_all_duo.sh +++ b/scripts/ios/build_all_duo.sh @@ -18,7 +18,6 @@ rustup target add x86_64-apple-ios (cd ../../crypto_plugins/flutter_liblelantus/scripts/ios && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/ios && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/ios/ && ./build_all.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/ios && ./build_all.sh ) diff --git a/scripts/linux/build_all.sh b/scripts/linux/build_all.sh index c9542798e..423646185 100755 --- a/scripts/linux/build_all.sh +++ b/scripts/linux/build_all.sh @@ -14,7 +14,6 @@ mkdir -p build ./build_secure_storage_deps.sh (cd ../../crypto_plugins/flutter_liblelantus/scripts/linux && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/linux && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/linux && ./build_all.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/linux && ./build_all.sh ) diff --git a/scripts/linux/build_all_campfire.sh b/scripts/linux/build_all_campfire.sh index c9542798e..423646185 100755 --- a/scripts/linux/build_all_campfire.sh +++ b/scripts/linux/build_all_campfire.sh @@ -14,7 +14,6 @@ mkdir -p build ./build_secure_storage_deps.sh (cd ../../crypto_plugins/flutter_liblelantus/scripts/linux && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/linux && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/linux && ./build_all.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/linux && ./build_all.sh ) diff --git a/scripts/linux/build_all_duo.sh b/scripts/linux/build_all_duo.sh index e7397bdc5..78067b478 100755 --- a/scripts/linux/build_all_duo.sh +++ b/scripts/linux/build_all_duo.sh @@ -16,7 +16,6 @@ mkdir -p build ./build_secure_storage_deps.sh & (cd ../../crypto_plugins/flutter_liblelantus/scripts/linux && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/linux && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/linux && ./build_all.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/linux && ./build_all.sh ) diff --git a/scripts/linux/build_secp256k1.sh b/scripts/linux/build_secp256k1.sh index 6fdd9f58c..f00b6b82b 100755 --- a/scripts/linux/build_secp256k1.sh +++ b/scripts/linux/build_secp256k1.sh @@ -1,10 +1,14 @@ mkdir -p build cd build -git clone https://github.com/bitcoin-core/secp256k1 +if [ ! -d "secp256k1" ]; then + git clone https://github.com/bitcoin-core/secp256k1 +fi cd secp256k1 +git checkout 68b55209f1ba3e6c0417789598f5f75649e9c14c +git reset --hard mkdir -p build && cd build cmake .. cmake --build . mkdir -p ../../../../../build -cp src/libsecp256k1.so.2.2.1 "../../../../../build/libsecp256k1.so" +cp lib/libsecp256k1.so.2.2.2 "../../../../../build/libsecp256k1.so" cd ../../../ diff --git a/scripts/linux/build_secure_storage_deps.sh b/scripts/linux/build_secure_storage_deps.sh index aff3097dc..9956d286c 100755 --- a/scripts/linux/build_secure_storage_deps.sh +++ b/scripts/linux/build_secure_storage_deps.sh @@ -32,7 +32,7 @@ if ! [ -x "$(command -v meson)" ]; then echo 'Error: meson is not installed.' >&2 exit 1 fi -meson _build +meson _build -Dmanpage=false -Dgtk_doc=false if ! [ -x "$(command -v ninja)" ]; then echo 'Error: ninja is not installed.' >&2 exit 1 diff --git a/scripts/macos/build_all.sh b/scripts/macos/build_all.sh index e3a58b45f..af608846f 100755 --- a/scripts/macos/build_all.sh +++ b/scripts/macos/build_all.sh @@ -8,7 +8,6 @@ set_rust_to_1671 (cd ../../crypto_plugins/flutter_liblelantus/scripts/macos && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/macos && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/macos/ && ./build_all.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/macos && ./build_all.sh ) diff --git a/scripts/macos/build_all_campfire.sh b/scripts/macos/build_all_campfire.sh index e3a58b45f..af608846f 100755 --- a/scripts/macos/build_all_campfire.sh +++ b/scripts/macos/build_all_campfire.sh @@ -8,7 +8,6 @@ set_rust_to_1671 (cd ../../crypto_plugins/flutter_liblelantus/scripts/macos && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/macos && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/macos/ && ./build_all.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/macos && ./build_all.sh ) diff --git a/scripts/macos/build_all_duo.sh b/scripts/macos/build_all_duo.sh index 6f70f4371..8a53e5801 100755 --- a/scripts/macos/build_all_duo.sh +++ b/scripts/macos/build_all_duo.sh @@ -10,7 +10,6 @@ set_rust_to_1671 (cd ../../crypto_plugins/flutter_liblelantus/scripts/macos && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/macos && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/macos/ && ./build_all.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/macos && ./build_all.sh ) diff --git a/scripts/windows/build_all.sh b/scripts/windows/build_all.sh index fc6ac37b1..191a46cc0 100755 --- a/scripts/windows/build_all.sh +++ b/scripts/windows/build_all.sh @@ -9,7 +9,6 @@ set_rust_to_1671 mkdir -p build (cd ../../crypto_plugins/flutter_libepiccash/scripts/windows && ./build_all.sh ) (cd ../../crypto_plugins/flutter_liblelantus/scripts/windows && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/windows && ./build_all.sh) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/windows && ./build_all.sh ) diff --git a/scripts/windows/build_all_campfire.sh b/scripts/windows/build_all_campfire.sh index fc6ac37b1..191a46cc0 100755 --- a/scripts/windows/build_all_campfire.sh +++ b/scripts/windows/build_all_campfire.sh @@ -9,7 +9,6 @@ set_rust_to_1671 mkdir -p build (cd ../../crypto_plugins/flutter_libepiccash/scripts/windows && ./build_all.sh ) (cd ../../crypto_plugins/flutter_liblelantus/scripts/windows && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/windows && ./build_all.sh) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/windows && ./build_all.sh ) diff --git a/scripts/windows/build_all_duo.sh b/scripts/windows/build_all_duo.sh index 80303f28b..3e27eff02 100755 --- a/scripts/windows/build_all_duo.sh +++ b/scripts/windows/build_all_duo.sh @@ -11,7 +11,6 @@ set_rust_to_1671 mkdir -p build (cd ../../crypto_plugins/flutter_libepiccash/scripts/windows && ./build_all.sh ) (cd ../../crypto_plugins/flutter_liblelantus/scripts/windows && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/windows && ./build_all.sh) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/windows && ./build_all.sh ) diff --git a/scripts/windows/build_secp256k1.bat b/scripts/windows/build_secp256k1.bat index 9e7433032..f5777b974 100644 --- a/scripts/windows/build_secp256k1.bat +++ b/scripts/windows/build_secp256k1.bat @@ -1,10 +1,12 @@ if not exist "build" mkdir "build" cd build -rem git clone https://github.com/bitcoin-core/secp256k1 +git clone https://github.com/bitcoin-core/secp256k1 cd secp256k1 -rem cmake -G "Visual Studio 17 2022" -A x64 -S . -B build +git checkout 68b55209f1ba3e6c0417789598f5f75649e9c14c +git reset --hard +cmake -G "Visual Studio 17 2022" -A x64 -S . -B build cd build -rem cmake --build . +cmake --build . if not exist "..\..\..\..\build\" mkdir "..\..\..\..\build\" -xcopy src\Debug\libsecp256k1-2.dll "..\..\..\..\build\secp256k1.dll" /Y +xcopy bin\Debug\libsecp256k1-2.dll "..\..\..\..\build\secp256k1.dll" /Y cd ..\..\..\ diff --git a/scripts/windows/build_secp256k1_wsl.sh b/scripts/windows/build_secp256k1_wsl.sh index b5d2e281f..a39cd3bee 100644 --- a/scripts/windows/build_secp256k1_wsl.sh +++ b/scripts/windows/build_secp256k1_wsl.sh @@ -1,10 +1,14 @@ mkdir -p build cd build -git clone https://github.com/bitcoin-core/secp256k1 +if [ ! -d "secp256k1" ]; then + git clone https://github.com/bitcoin-core/secp256k1 +fi cd secp256k1 +git checkout 68b55209f1ba3e6c0417789598f5f75649e9c14c +git reset --hard mkdir -p build && cd build cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/x86_64-w64-mingw32.toolchain.cmake cmake --build . mkdir -p ../../../../../build -cp src/libsecp256k1-2.dll "../../../../../build/secp256k1.dll" +cp bin/libsecp256k1-2.dll "../../../../../build/secp256k1.dll" cd ../../../ diff --git a/scripts/windows/deps.sh b/scripts/windows/deps.sh index 802ca8f8a..c9975b457 100644 --- a/scripts/windows/deps.sh +++ b/scripts/windows/deps.sh @@ -1,8 +1,9 @@ #!/bin/bash -(cd ../../crypto_plugins/flutter_libepiccash/scripts/windows && ./deps.sh ) -(cd ../../crypto_plugins/flutter_liblelantus/scripts/windows && ./mxedeps.sh ) -# (cd ../../crypto_plugins/flutter_libmonero/scripts/windows && ./monerodeps.sh && ./mxedeps.sh) & +cd ../../crypto_plugins/flutter_libepiccash/scripts/windows && ./deps.sh +cd ../../crypto_plugins/flutter_liblelantus/scripts/windows && ./mxedeps.sh +# cd ../../crypto_plugins/flutter_libmonero/scripts/windows && ./monerodeps.sh && ./mxedeps.sh +sudo apt install libgtk2.0-dev wait echo "Done building" diff --git a/test/address_utils_test.dart b/test/address_utils_test.dart index 560962256..599380c31 100644 --- a/test/address_utils_test.dart +++ b/test/address_utils_test.dart @@ -12,52 +12,49 @@ void main() { test("parse a valid uri string A", () { const uri = "dogecoin:$firoAddress?amount=50&label=eggs"; - final result = AddressUtils.parseUri(uri); - expect(result, { - "scheme": "dogecoin", - "address": firoAddress, - "amount": "50", - "label": "eggs", - }); + final result = AddressUtils.parsePaymentUri(uri); + expect(result, isNotNull); + expect(result!.scheme, "dogecoin"); + expect(result.address, firoAddress); + expect(result.amount, "50"); + expect(result.label, "eggs"); }); test("parse a valid uri string B", () { const uri = "firo:$firoAddress?amount=50&message=eggs+are+good"; - final result = AddressUtils.parseUri(uri); - expect(result, { - "scheme": "firo", - "address": firoAddress, - "amount": "50", - "message": "eggs are good", - }); + final result = AddressUtils.parsePaymentUri(uri); + expect(result, isNotNull); + expect(result!.scheme, "firo"); + expect(result.address, firoAddress); + expect(result.amount, "50"); + expect(result.message, "eggs are good"); }); test("parse a valid uri string C", () { const uri = "bitcoin:$firoAddress?amount=50.1&message=eggs%20are%20good%21"; - final result = AddressUtils.parseUri(uri); - expect(result, { - "scheme": "bitcoin", - "address": firoAddress, - "amount": "50.1", - "message": "eggs are good!", - }); + final result = AddressUtils.parsePaymentUri(uri); + expect(result, isNotNull); + expect(result!.scheme, "bitcoin"); + expect(result.address, firoAddress); + expect(result.amount, "50.1"); + expect(result.message, "eggs are good!"); }); test("parse an invalid uri string", () { const uri = "firo$firoAddress?amount=50&label=eggs"; - final result = AddressUtils.parseUri(uri); - expect(result, {}); + final result = AddressUtils.parsePaymentUri(uri); + expect(result, isNull); }); test("parse an invalid string", () { const uri = "$firoAddress?amount=50&label=eggs"; - final result = AddressUtils.parseUri(uri); - expect(result, {}); + final result = AddressUtils.parsePaymentUri(uri); + expect(result, isNull); }); test("parse an invalid uri string", () { const uri = "::: 8 \\ %23"; - expect(AddressUtils.parseUri(uri), {}); + expect(AddressUtils.parsePaymentUri(uri), isNull); }); test("encode a list of (mnemonic) words/strings as a json object", () { @@ -95,7 +92,7 @@ void main() { test("build a uri string with empty params", () { expect( AddressUtils.buildUriString( - Firo(CryptoCurrencyNetwork.main), firoAddress, {}), + Firo(CryptoCurrencyNetwork.main).uriScheme, firoAddress, {}), "firo:$firoAddress", ); }); @@ -103,7 +100,7 @@ void main() { test("build a uri string with one param", () { expect( AddressUtils.buildUriString( - Firo(CryptoCurrencyNetwork.main), + Firo(CryptoCurrencyNetwork.main).uriScheme, firoAddress, {"amount": "10.0123"}, ), @@ -114,7 +111,7 @@ void main() { test("build a uri string with some params", () { expect( AddressUtils.buildUriString( - Firo(CryptoCurrencyNetwork.main), + Firo(CryptoCurrencyNetwork.main).uriScheme, firoAddress, {"amount": "10.0123", "message": "Some kind of message!"}, ), diff --git a/test/cached_electrumx_test.dart b/test/cached_electrumx_test.dart index 26213175f..370e02972 100644 --- a/test/cached_electrumx_test.dart +++ b/test/cached_electrumx_test.dart @@ -168,6 +168,8 @@ void main() { name: "some name", id: "some ID", useSSL: true, + torEnabled: true, + clearnetEnabled: true, ); final client = diff --git a/test/cached_electrumx_test.mocks.dart b/test/cached_electrumx_test.mocks.dart index 8a0ea1c71..993f73f69 100644 --- a/test/cached_electrumx_test.mocks.dart +++ b/test/cached_electrumx_test.mocks.dart @@ -89,6 +89,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { Invocation.getter(#cryptoCurrency), ), ) as _i2.CryptoCurrency); + @override set failovers(List<_i5.ElectrumXNode>? _failovers) => super.noSuchMethod( Invocation.setter( @@ -97,11 +98,13 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { ), returnValueForMissingStub: null, ); + @override int get currentFailoverIndex => (super.noSuchMethod( Invocation.getter(#currentFailoverIndex), returnValue: 0, ) as int); + @override set currentFailoverIndex(int? _currentFailoverIndex) => super.noSuchMethod( Invocation.setter( @@ -110,6 +113,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { ), returnValueForMissingStub: null, ); + @override Duration get connectionTimeoutForSpecialCaseJsonRPCClients => (super.noSuchMethod( @@ -119,6 +123,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), ), ) as Duration); + @override String get host => (super.noSuchMethod( Invocation.getter(#host), @@ -127,16 +132,19 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { Invocation.getter(#host), ), ) as String); + @override int get port => (super.noSuchMethod( Invocation.getter(#port), returnValue: 0, ) as int); + @override bool get useSSL => (super.noSuchMethod( Invocation.getter(#useSSL), returnValue: false, ) as bool); + @override _i7.Future closeAdapter() => (super.noSuchMethod( Invocation.method( @@ -146,6 +154,17 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { returnValue: _i7.Future.value(), returnValueForMissingStub: _i7.Future.value(), ) as _i7.Future); + + @override + _i7.Future checkElectrumAdapter() => (super.noSuchMethod( + Invocation.method( + #checkElectrumAdapter, + [], + ), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value(), + ) as _i7.Future); + @override _i7.Future request({ required String? command, @@ -168,6 +187,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { ), returnValue: _i7.Future.value(), ) as _i7.Future); + @override _i7.Future> batchRequest({ required String? command, @@ -188,6 +208,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { ), returnValue: _i7.Future>.value([]), ) as _i7.Future>); + @override _i7.Future ping({ String? requestID, @@ -204,6 +225,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { ), returnValue: _i7.Future.value(false), ) as _i7.Future); + @override _i7.Future> getBlockHeadTip({String? requestID}) => (super.noSuchMethod( @@ -215,6 +237,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { returnValue: _i7.Future>.value({}), ) as _i7.Future>); + @override _i7.Future> getServerFeatures({String? requestID}) => (super.noSuchMethod( @@ -226,6 +249,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { returnValue: _i7.Future>.value({}), ) as _i7.Future>); + @override _i7.Future broadcastTransaction({ required String? rawTx, @@ -252,6 +276,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { ), )), ) as _i7.Future); + @override _i7.Future> getBalance({ required String? scripthash, @@ -269,6 +294,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { returnValue: _i7.Future>.value({}), ) as _i7.Future>); + @override _i7.Future>> getHistory({ required String? scripthash, @@ -286,6 +312,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { returnValue: _i7.Future>>.value( >[]), ) as _i7.Future>>); + @override _i7.Future>>> getBatchHistory( {required List? args}) => @@ -298,6 +325,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { returnValue: _i7.Future>>>.value( >>[]), ) as _i7.Future>>>); + @override _i7.Future>> getUTXOs({ required String? scripthash, @@ -315,6 +343,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { returnValue: _i7.Future>>.value( >[]), ) as _i7.Future>>); + @override _i7.Future>>> getBatchUTXOs( {required List? args}) => @@ -327,6 +356,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { returnValue: _i7.Future>>>.value( >>[]), ) as _i7.Future>>>); + @override _i7.Future> getTransaction({ required String? txHash, @@ -346,6 +376,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { returnValue: _i7.Future>.value({}), ) as _i7.Future>); + @override _i7.Future> getLelantusAnonymitySet({ String? groupId = r'1', @@ -365,6 +396,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { returnValue: _i7.Future>.value({}), ) as _i7.Future>); + @override _i7.Future getLelantusMintData({ dynamic mints, @@ -381,6 +413,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { ), returnValue: _i7.Future.value(), ) as _i7.Future); + @override _i7.Future> getLelantusUsedCoinSerials({ String? requestID, @@ -398,6 +431,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { returnValue: _i7.Future>.value({}), ) as _i7.Future>); + @override _i7.Future getLelantusLatestCoinId({String? requestID}) => (super.noSuchMethod( @@ -408,6 +442,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { ), returnValue: _i7.Future.value(0), ) as _i7.Future); + @override _i7.Future> getSparkAnonymitySet({ String? coinGroupId = r'1', @@ -427,6 +462,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { returnValue: _i7.Future>.value({}), ) as _i7.Future>); + @override _i7.Future>> getSparkMintMetaData({ String? requestID, @@ -444,6 +480,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { returnValue: _i7.Future>>.value( >[]), ) as _i7.Future>>); + @override _i7.Future getSparkLatestCoinId({String? requestID}) => (super.noSuchMethod( @@ -454,6 +491,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { ), returnValue: _i7.Future.value(0), ) as _i7.Future); + @override _i7.Future> getMempoolTxids({String? requestID}) => (super.noSuchMethod( @@ -464,8 +502,16 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { ), returnValue: _i7.Future>.value({}), ) as _i7.Future>); + @override - _i7.Future> getMempoolSparkData({ + _i7.Future< + List< + ({ + List coins, + List lTags, + List serialContext, + String txid + })>> getMempoolSparkData({ String? requestID, required List? txids, }) => @@ -478,9 +524,28 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { #txids: txids, }, ), - returnValue: - _i7.Future>.value({}), - ) as _i7.Future>); + returnValue: _i7.Future< + List< + ({ + List coins, + List lTags, + List serialContext, + String txid + })>>.value(<({ + List coins, + List lTags, + List serialContext, + String txid + })>[]), + ) as _i7.Future< + List< + ({ + List coins, + List lTags, + List serialContext, + String txid + })>>); + @override _i7.Future>> getSparkUnhashedUsedCoinsTagsWithTxHashes({ String? requestID, @@ -497,6 +562,26 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { ), returnValue: _i7.Future>>.value(>[]), ) as _i7.Future>>); + + @override + _i7.Future isMasterNodeCollateral({ + String? requestID, + required String? txid, + required int? index, + }) => + (super.noSuchMethod( + Invocation.method( + #isMasterNodeCollateral, + [], + { + #requestID: requestID, + #txid: txid, + #index: index, + }, + ), + returnValue: _i7.Future.value(false), + ) as _i7.Future); + @override _i7.Future> getFeeRate({String? requestID}) => (super.noSuchMethod( @@ -508,6 +593,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { returnValue: _i7.Future>.value({}), ) as _i7.Future>); + @override _i7.Future<_i3.Decimal> estimateFee({ String? requestID, @@ -534,6 +620,7 @@ class MockElectrumXClient extends _i1.Mock implements _i5.ElectrumXClient { ), )), ) as _i7.Future<_i3.Decimal>); + @override _i7.Future<_i3.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( Invocation.method( @@ -565,11 +652,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { Invocation.getter(#isInitialized), returnValue: false, ) as bool); + @override int get lastUnlockedTimeout => (super.noSuchMethod( Invocation.getter(#lastUnlockedTimeout), returnValue: 0, ) as int); + @override set lastUnlockedTimeout(int? lastUnlockedTimeout) => super.noSuchMethod( Invocation.setter( @@ -578,11 +667,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override int get lastUnlocked => (super.noSuchMethod( Invocation.getter(#lastUnlocked), returnValue: 0, ) as int); + @override set lastUnlocked(int? lastUnlocked) => super.noSuchMethod( Invocation.setter( @@ -591,16 +682,19 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override int get currentNotificationId => (super.noSuchMethod( Invocation.getter(#currentNotificationId), returnValue: 0, ) as int); + @override List get walletIdsSyncOnStartup => (super.noSuchMethod( Invocation.getter(#walletIdsSyncOnStartup), returnValue: [], ) as List); + @override set walletIdsSyncOnStartup(List? walletIdsSyncOnStartup) => super.noSuchMethod( @@ -610,11 +704,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override _i9.SyncingType get syncType => (super.noSuchMethod( Invocation.getter(#syncType), returnValue: _i9.SyncingType.currentWalletOnly, ) as _i9.SyncingType); + @override set syncType(_i9.SyncingType? syncType) => super.noSuchMethod( Invocation.setter( @@ -623,11 +719,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override bool get wifiOnly => (super.noSuchMethod( Invocation.getter(#wifiOnly), returnValue: false, ) as bool); + @override set wifiOnly(bool? wifiOnly) => super.noSuchMethod( Invocation.setter( @@ -636,11 +734,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override bool get showFavoriteWallets => (super.noSuchMethod( Invocation.getter(#showFavoriteWallets), returnValue: false, ) as bool); + @override set showFavoriteWallets(bool? showFavoriteWallets) => super.noSuchMethod( Invocation.setter( @@ -649,6 +749,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override String get language => (super.noSuchMethod( Invocation.getter(#language), @@ -657,6 +758,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { Invocation.getter(#language), ), ) as String); + @override set language(String? newLanguage) => super.noSuchMethod( Invocation.setter( @@ -665,6 +767,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override String get currency => (super.noSuchMethod( Invocation.getter(#currency), @@ -673,6 +776,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { Invocation.getter(#currency), ), ) as String); + @override set currency(String? newCurrency) => super.noSuchMethod( Invocation.setter( @@ -681,11 +785,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override bool get randomizePIN => (super.noSuchMethod( Invocation.getter(#randomizePIN), returnValue: false, ) as bool); + @override set randomizePIN(bool? randomizePIN) => super.noSuchMethod( Invocation.setter( @@ -694,11 +800,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override bool get useBiometrics => (super.noSuchMethod( Invocation.getter(#useBiometrics), returnValue: false, ) as bool); + @override set useBiometrics(bool? useBiometrics) => super.noSuchMethod( Invocation.setter( @@ -707,11 +815,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override bool get hasPin => (super.noSuchMethod( Invocation.getter(#hasPin), returnValue: false, ) as bool); + @override set hasPin(bool? hasPin) => super.noSuchMethod( Invocation.setter( @@ -720,11 +830,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override int get familiarity => (super.noSuchMethod( Invocation.getter(#familiarity), returnValue: 0, ) as int); + @override set familiarity(int? familiarity) => super.noSuchMethod( Invocation.setter( @@ -733,11 +845,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override bool get torKillSwitch => (super.noSuchMethod( Invocation.getter(#torKillSwitch), returnValue: false, ) as bool); + @override set torKillSwitch(bool? torKillswitch) => super.noSuchMethod( Invocation.setter( @@ -746,11 +860,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override bool get showTestNetCoins => (super.noSuchMethod( Invocation.getter(#showTestNetCoins), returnValue: false, ) as bool); + @override set showTestNetCoins(bool? showTestNetCoins) => super.noSuchMethod( Invocation.setter( @@ -759,11 +875,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override bool get isAutoBackupEnabled => (super.noSuchMethod( Invocation.getter(#isAutoBackupEnabled), returnValue: false, ) as bool); + @override set isAutoBackupEnabled(bool? isAutoBackupEnabled) => super.noSuchMethod( Invocation.setter( @@ -772,6 +890,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override set autoBackupLocation(String? autoBackupLocation) => super.noSuchMethod( Invocation.setter( @@ -780,11 +899,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override _i10.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( Invocation.getter(#backupFrequencyType), returnValue: _i10.BackupFrequencyType.everyTenMinutes, ) as _i10.BackupFrequencyType); + @override set backupFrequencyType(_i10.BackupFrequencyType? backupFrequencyType) => super.noSuchMethod( @@ -794,6 +915,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override set lastAutoBackup(DateTime? lastAutoBackup) => super.noSuchMethod( Invocation.setter( @@ -802,11 +924,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override bool get hideBlockExplorerWarning => (super.noSuchMethod( Invocation.getter(#hideBlockExplorerWarning), returnValue: false, ) as bool); + @override set hideBlockExplorerWarning(bool? hideBlockExplorerWarning) => super.noSuchMethod( @@ -816,11 +940,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override bool get gotoWalletOnStartup => (super.noSuchMethod( Invocation.getter(#gotoWalletOnStartup), returnValue: false, ) as bool); + @override set gotoWalletOnStartup(bool? gotoWalletOnStartup) => super.noSuchMethod( Invocation.setter( @@ -829,6 +955,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override set startupWalletId(String? startupWalletId) => super.noSuchMethod( Invocation.setter( @@ -837,11 +964,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override bool get externalCalls => (super.noSuchMethod( Invocation.getter(#externalCalls), returnValue: false, ) as bool); + @override set externalCalls(bool? externalCalls) => super.noSuchMethod( Invocation.setter( @@ -850,11 +979,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override bool get enableCoinControl => (super.noSuchMethod( Invocation.getter(#enableCoinControl), returnValue: false, ) as bool); + @override set enableCoinControl(bool? enableCoinControl) => super.noSuchMethod( Invocation.setter( @@ -863,11 +994,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override bool get enableSystemBrightness => (super.noSuchMethod( Invocation.getter(#enableSystemBrightness), returnValue: false, ) as bool); + @override set enableSystemBrightness(bool? enableSystemBrightness) => super.noSuchMethod( @@ -877,6 +1010,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override String get themeId => (super.noSuchMethod( Invocation.getter(#themeId), @@ -885,6 +1019,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { Invocation.getter(#themeId), ), ) as String); + @override set themeId(String? themeId) => super.noSuchMethod( Invocation.setter( @@ -893,6 +1028,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override String get systemBrightnessLightThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessLightThemeId), @@ -901,6 +1037,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { Invocation.getter(#systemBrightnessLightThemeId), ), ) as String); + @override set systemBrightnessLightThemeId(String? systemBrightnessLightThemeId) => super.noSuchMethod( @@ -910,6 +1047,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override String get systemBrightnessDarkThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessDarkThemeId), @@ -918,6 +1056,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { Invocation.getter(#systemBrightnessDarkThemeId), ), ) as String); + @override set systemBrightnessDarkThemeId(String? systemBrightnessDarkThemeId) => super.noSuchMethod( @@ -927,11 +1066,13 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override bool get useTor => (super.noSuchMethod( Invocation.getter(#useTor), returnValue: false, ) as bool); + @override set useTor(bool? useTor) => super.noSuchMethod( Invocation.setter( @@ -940,11 +1081,43 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + + @override + bool get autoPin => (super.noSuchMethod( + Invocation.getter(#autoPin), + returnValue: false, + ) as bool); + + @override + set autoPin(bool? autoPin) => super.noSuchMethod( + Invocation.setter( + #autoPin, + autoPin, + ), + returnValueForMissingStub: null, + ); + + @override + bool get enableExchange => (super.noSuchMethod( + Invocation.getter(#enableExchange), + returnValue: false, + ) as bool); + + @override + set enableExchange(bool? showExchange) => super.noSuchMethod( + Invocation.setter( + #enableExchange, + showExchange, + ), + returnValueForMissingStub: null, + ); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i7.Future init() => (super.noSuchMethod( Invocation.method( @@ -954,6 +1127,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { returnValue: _i7.Future.value(), returnValueForMissingStub: _i7.Future.value(), ) as _i7.Future); + @override _i7.Future incrementCurrentNotificationIndex() => (super.noSuchMethod( Invocation.method( @@ -963,6 +1137,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { returnValue: _i7.Future.value(), returnValueForMissingStub: _i7.Future.value(), ) as _i7.Future); + @override _i7.Future isExternalCallsSet() => (super.noSuchMethod( Invocation.method( @@ -971,6 +1146,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValue: _i7.Future.value(false), ) as _i7.Future); + @override _i7.Future saveUserID(String? userId) => (super.noSuchMethod( Invocation.method( @@ -980,6 +1156,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { returnValue: _i7.Future.value(), returnValueForMissingStub: _i7.Future.value(), ) as _i7.Future); + @override _i7.Future saveSignupEpoch(int? signupEpoch) => (super.noSuchMethod( Invocation.method( @@ -989,6 +1166,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { returnValue: _i7.Future.value(), returnValueForMissingStub: _i7.Future.value(), ) as _i7.Future); + @override _i11.AmountUnit amountUnit(_i2.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( @@ -997,6 +1175,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValue: _i11.AmountUnit.normal, ) as _i11.AmountUnit); + @override void updateAmountUnit({ required _i2.CryptoCurrency? coin, @@ -1013,6 +1192,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override int maxDecimals(_i2.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( @@ -1021,6 +1201,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValue: 0, ) as int); + @override void updateMaxDecimals({ required _i2.CryptoCurrency? coin, @@ -1037,6 +1218,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override _i4.FusionInfo getFusionServerInfo(_i2.CryptoCurrency? coin) => (super.noSuchMethod( @@ -1052,6 +1234,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), ), ) as _i4.FusionInfo); + @override void setFusionServerInfo( _i2.CryptoCurrency? coin, @@ -1067,6 +1250,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override void addListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -1075,6 +1259,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override void removeListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -1083,6 +1268,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -1091,6 +1277,7 @@ class MockPrefs extends _i1.Mock implements _i8.Prefs { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/flutter_secure_storage_interface_test.mocks.dart b/test/flutter_secure_storage_interface_test.mocks.dart index 8779d5147..e99c6b1a7 100644 --- a/test/flutter_secure_storage_interface_test.mocks.dart +++ b/test/flutter_secure_storage_interface_test.mocks.dart @@ -100,6 +100,7 @@ class MockFlutterSecureStorage extends _i1.Mock Invocation.getter(#iOptions), ), ) as _i2.IOSOptions); + @override _i2.AndroidOptions get aOptions => (super.noSuchMethod( Invocation.getter(#aOptions), @@ -108,6 +109,7 @@ class MockFlutterSecureStorage extends _i1.Mock Invocation.getter(#aOptions), ), ) as _i2.AndroidOptions); + @override _i2.LinuxOptions get lOptions => (super.noSuchMethod( Invocation.getter(#lOptions), @@ -116,6 +118,7 @@ class MockFlutterSecureStorage extends _i1.Mock Invocation.getter(#lOptions), ), ) as _i2.LinuxOptions); + @override _i2.WindowsOptions get wOptions => (super.noSuchMethod( Invocation.getter(#wOptions), @@ -124,6 +127,7 @@ class MockFlutterSecureStorage extends _i1.Mock Invocation.getter(#wOptions), ), ) as _i2.WindowsOptions); + @override _i2.WebOptions get webOptions => (super.noSuchMethod( Invocation.getter(#webOptions), @@ -132,6 +136,7 @@ class MockFlutterSecureStorage extends _i1.Mock Invocation.getter(#webOptions), ), ) as _i2.WebOptions); + @override _i2.MacOsOptions get mOptions => (super.noSuchMethod( Invocation.getter(#mOptions), @@ -140,6 +145,7 @@ class MockFlutterSecureStorage extends _i1.Mock Invocation.getter(#mOptions), ), ) as _i2.MacOsOptions); + @override _i3.Future write({ required String? key, @@ -169,6 +175,7 @@ class MockFlutterSecureStorage extends _i1.Mock returnValue: _i3.Future.value(), returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); + @override _i3.Future read({ required String? key, @@ -195,6 +202,7 @@ class MockFlutterSecureStorage extends _i1.Mock ), returnValue: _i3.Future.value(), ) as _i3.Future); + @override _i3.Future containsKey({ required String? key, @@ -221,6 +229,7 @@ class MockFlutterSecureStorage extends _i1.Mock ), returnValue: _i3.Future.value(false), ) as _i3.Future); + @override _i3.Future delete({ required String? key, @@ -248,6 +257,7 @@ class MockFlutterSecureStorage extends _i1.Mock returnValue: _i3.Future.value(), returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); + @override _i3.Future> readAll({ _i2.IOSOptions? iOptions, @@ -272,6 +282,7 @@ class MockFlutterSecureStorage extends _i1.Mock ), returnValue: _i3.Future>.value({}), ) as _i3.Future>); + @override _i3.Future deleteAll({ _i2.IOSOptions? iOptions, diff --git a/test/models/type_adapter_tests/lelantus_coin_adapter_test.mocks.dart b/test/models/type_adapter_tests/lelantus_coin_adapter_test.mocks.dart index 13aeb65c7..140c686d5 100644 --- a/test/models/type_adapter_tests/lelantus_coin_adapter_test.mocks.dart +++ b/test/models/type_adapter_tests/lelantus_coin_adapter_test.mocks.dart @@ -48,11 +48,13 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { Invocation.getter(#availableBytes), returnValue: 0, ) as int); + @override int get usedBytes => (super.noSuchMethod( Invocation.getter(#usedBytes), returnValue: 0, ) as int); + @override void skip(int? bytes) => super.noSuchMethod( Invocation.method( @@ -61,6 +63,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValueForMissingStub: null, ); + @override int readByte() => (super.noSuchMethod( Invocation.method( @@ -69,6 +72,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: 0, ) as int); + @override _i4.Uint8List viewBytes(int? bytes) => (super.noSuchMethod( Invocation.method( @@ -77,6 +81,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: _i4.Uint8List(0), ) as _i4.Uint8List); + @override _i4.Uint8List peekBytes(int? bytes) => (super.noSuchMethod( Invocation.method( @@ -85,6 +90,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: _i4.Uint8List(0), ) as _i4.Uint8List); + @override int readWord() => (super.noSuchMethod( Invocation.method( @@ -93,6 +99,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: 0, ) as int); + @override int readInt32() => (super.noSuchMethod( Invocation.method( @@ -101,6 +108,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: 0, ) as int); + @override int readUint32() => (super.noSuchMethod( Invocation.method( @@ -109,6 +117,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: 0, ) as int); + @override int readInt() => (super.noSuchMethod( Invocation.method( @@ -117,6 +126,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: 0, ) as int); + @override double readDouble() => (super.noSuchMethod( Invocation.method( @@ -125,6 +135,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: 0.0, ) as double); + @override bool readBool() => (super.noSuchMethod( Invocation.method( @@ -133,6 +144,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: false, ) as bool); + @override String readString([ int? byteCount, @@ -157,6 +169,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), ), ) as String); + @override _i4.Uint8List readByteList([int? length]) => (super.noSuchMethod( Invocation.method( @@ -165,6 +178,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: _i4.Uint8List(0), ) as _i4.Uint8List); + @override List readIntList([int? length]) => (super.noSuchMethod( Invocation.method( @@ -173,6 +187,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: [], ) as List); + @override List readDoubleList([int? length]) => (super.noSuchMethod( Invocation.method( @@ -181,6 +196,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: [], ) as List); + @override List readBoolList([int? length]) => (super.noSuchMethod( Invocation.method( @@ -189,6 +205,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: [], ) as List); + @override List readStringList([ int? length, @@ -204,6 +221,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: [], ) as List); + @override List readList([int? length]) => (super.noSuchMethod( Invocation.method( @@ -212,6 +230,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: [], ) as List); + @override Map readMap([int? length]) => (super.noSuchMethod( Invocation.method( @@ -220,6 +239,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: {}, ) as Map); + @override _i3.HiveList<_i1.HiveObjectMixin> readHiveList([int? length]) => (super.noSuchMethod( @@ -253,6 +273,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeWord(int? value) => super.noSuchMethod( Invocation.method( @@ -261,6 +282,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeInt32(int? value) => super.noSuchMethod( Invocation.method( @@ -269,6 +291,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeUint32(int? value) => super.noSuchMethod( Invocation.method( @@ -277,6 +300,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeInt(int? value) => super.noSuchMethod( Invocation.method( @@ -285,6 +309,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeDouble(double? value) => super.noSuchMethod( Invocation.method( @@ -293,6 +318,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeBool(bool? value) => super.noSuchMethod( Invocation.method( @@ -301,6 +327,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeString( String? value, { @@ -318,6 +345,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeByteList( List? bytes, { @@ -331,6 +359,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeIntList( List? list, { @@ -344,6 +373,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeDoubleList( List? list, { @@ -357,6 +387,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeBoolList( List? list, { @@ -370,6 +401,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeStringList( List? list, { @@ -387,6 +419,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeList( List? list, { @@ -400,6 +433,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeMap( Map? map, { @@ -413,6 +447,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeHiveList( _i3.HiveList<_i1.HiveObjectMixin>? list, { @@ -426,6 +461,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void write( T? value, { diff --git a/test/models/type_adapter_tests/transactions_model_adapter_test.mocks.dart b/test/models/type_adapter_tests/transactions_model_adapter_test.mocks.dart index 53dd08255..ad9f995db 100644 --- a/test/models/type_adapter_tests/transactions_model_adapter_test.mocks.dart +++ b/test/models/type_adapter_tests/transactions_model_adapter_test.mocks.dart @@ -48,11 +48,13 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { Invocation.getter(#availableBytes), returnValue: 0, ) as int); + @override int get usedBytes => (super.noSuchMethod( Invocation.getter(#usedBytes), returnValue: 0, ) as int); + @override void skip(int? bytes) => super.noSuchMethod( Invocation.method( @@ -61,6 +63,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValueForMissingStub: null, ); + @override int readByte() => (super.noSuchMethod( Invocation.method( @@ -69,6 +72,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: 0, ) as int); + @override _i4.Uint8List viewBytes(int? bytes) => (super.noSuchMethod( Invocation.method( @@ -77,6 +81,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: _i4.Uint8List(0), ) as _i4.Uint8List); + @override _i4.Uint8List peekBytes(int? bytes) => (super.noSuchMethod( Invocation.method( @@ -85,6 +90,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: _i4.Uint8List(0), ) as _i4.Uint8List); + @override int readWord() => (super.noSuchMethod( Invocation.method( @@ -93,6 +99,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: 0, ) as int); + @override int readInt32() => (super.noSuchMethod( Invocation.method( @@ -101,6 +108,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: 0, ) as int); + @override int readUint32() => (super.noSuchMethod( Invocation.method( @@ -109,6 +117,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: 0, ) as int); + @override int readInt() => (super.noSuchMethod( Invocation.method( @@ -117,6 +126,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: 0, ) as int); + @override double readDouble() => (super.noSuchMethod( Invocation.method( @@ -125,6 +135,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: 0.0, ) as double); + @override bool readBool() => (super.noSuchMethod( Invocation.method( @@ -133,6 +144,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: false, ) as bool); + @override String readString([ int? byteCount, @@ -157,6 +169,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), ), ) as String); + @override _i4.Uint8List readByteList([int? length]) => (super.noSuchMethod( Invocation.method( @@ -165,6 +178,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: _i4.Uint8List(0), ) as _i4.Uint8List); + @override List readIntList([int? length]) => (super.noSuchMethod( Invocation.method( @@ -173,6 +187,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: [], ) as List); + @override List readDoubleList([int? length]) => (super.noSuchMethod( Invocation.method( @@ -181,6 +196,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: [], ) as List); + @override List readBoolList([int? length]) => (super.noSuchMethod( Invocation.method( @@ -189,6 +205,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: [], ) as List); + @override List readStringList([ int? length, @@ -204,6 +221,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: [], ) as List); + @override List readList([int? length]) => (super.noSuchMethod( Invocation.method( @@ -212,6 +230,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: [], ) as List); + @override Map readMap([int? length]) => (super.noSuchMethod( Invocation.method( @@ -220,6 +239,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: {}, ) as Map); + @override _i3.HiveList<_i1.HiveObjectMixin> readHiveList([int? length]) => (super.noSuchMethod( @@ -253,6 +273,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeWord(int? value) => super.noSuchMethod( Invocation.method( @@ -261,6 +282,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeInt32(int? value) => super.noSuchMethod( Invocation.method( @@ -269,6 +291,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeUint32(int? value) => super.noSuchMethod( Invocation.method( @@ -277,6 +300,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeInt(int? value) => super.noSuchMethod( Invocation.method( @@ -285,6 +309,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeDouble(double? value) => super.noSuchMethod( Invocation.method( @@ -293,6 +318,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeBool(bool? value) => super.noSuchMethod( Invocation.method( @@ -301,6 +327,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeString( String? value, { @@ -318,6 +345,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeByteList( List? bytes, { @@ -331,6 +359,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeIntList( List? list, { @@ -344,6 +373,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeDoubleList( List? list, { @@ -357,6 +387,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeBoolList( List? list, { @@ -370,6 +401,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeStringList( List? list, { @@ -387,6 +419,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeList( List? list, { @@ -400,6 +433,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeMap( Map? map, { @@ -413,6 +447,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeHiveList( _i3.HiveList<_i1.HiveObjectMixin>? list, { @@ -426,6 +461,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void write( T? value, { diff --git a/test/models/type_adapter_tests/utxo_model_adapter_test.mocks.dart b/test/models/type_adapter_tests/utxo_model_adapter_test.mocks.dart index f2750cf2a..983ffb136 100644 --- a/test/models/type_adapter_tests/utxo_model_adapter_test.mocks.dart +++ b/test/models/type_adapter_tests/utxo_model_adapter_test.mocks.dart @@ -48,11 +48,13 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { Invocation.getter(#availableBytes), returnValue: 0, ) as int); + @override int get usedBytes => (super.noSuchMethod( Invocation.getter(#usedBytes), returnValue: 0, ) as int); + @override void skip(int? bytes) => super.noSuchMethod( Invocation.method( @@ -61,6 +63,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValueForMissingStub: null, ); + @override int readByte() => (super.noSuchMethod( Invocation.method( @@ -69,6 +72,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: 0, ) as int); + @override _i4.Uint8List viewBytes(int? bytes) => (super.noSuchMethod( Invocation.method( @@ -77,6 +81,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: _i4.Uint8List(0), ) as _i4.Uint8List); + @override _i4.Uint8List peekBytes(int? bytes) => (super.noSuchMethod( Invocation.method( @@ -85,6 +90,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: _i4.Uint8List(0), ) as _i4.Uint8List); + @override int readWord() => (super.noSuchMethod( Invocation.method( @@ -93,6 +99,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: 0, ) as int); + @override int readInt32() => (super.noSuchMethod( Invocation.method( @@ -101,6 +108,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: 0, ) as int); + @override int readUint32() => (super.noSuchMethod( Invocation.method( @@ -109,6 +117,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: 0, ) as int); + @override int readInt() => (super.noSuchMethod( Invocation.method( @@ -117,6 +126,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: 0, ) as int); + @override double readDouble() => (super.noSuchMethod( Invocation.method( @@ -125,6 +135,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: 0.0, ) as double); + @override bool readBool() => (super.noSuchMethod( Invocation.method( @@ -133,6 +144,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: false, ) as bool); + @override String readString([ int? byteCount, @@ -157,6 +169,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), ), ) as String); + @override _i4.Uint8List readByteList([int? length]) => (super.noSuchMethod( Invocation.method( @@ -165,6 +178,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: _i4.Uint8List(0), ) as _i4.Uint8List); + @override List readIntList([int? length]) => (super.noSuchMethod( Invocation.method( @@ -173,6 +187,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: [], ) as List); + @override List readDoubleList([int? length]) => (super.noSuchMethod( Invocation.method( @@ -181,6 +196,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: [], ) as List); + @override List readBoolList([int? length]) => (super.noSuchMethod( Invocation.method( @@ -189,6 +205,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: [], ) as List); + @override List readStringList([ int? length, @@ -204,6 +221,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: [], ) as List); + @override List readList([int? length]) => (super.noSuchMethod( Invocation.method( @@ -212,6 +230,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: [], ) as List); + @override Map readMap([int? length]) => (super.noSuchMethod( Invocation.method( @@ -220,6 +239,7 @@ class MockBinaryReader extends _i2.Mock implements _i3.BinaryReader { ), returnValue: {}, ) as Map); + @override _i3.HiveList<_i1.HiveObjectMixin> readHiveList([int? length]) => (super.noSuchMethod( @@ -253,6 +273,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeWord(int? value) => super.noSuchMethod( Invocation.method( @@ -261,6 +282,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeInt32(int? value) => super.noSuchMethod( Invocation.method( @@ -269,6 +291,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeUint32(int? value) => super.noSuchMethod( Invocation.method( @@ -277,6 +300,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeInt(int? value) => super.noSuchMethod( Invocation.method( @@ -285,6 +309,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeDouble(double? value) => super.noSuchMethod( Invocation.method( @@ -293,6 +318,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeBool(bool? value) => super.noSuchMethod( Invocation.method( @@ -301,6 +327,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeString( String? value, { @@ -318,6 +345,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeByteList( List? bytes, { @@ -331,6 +359,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeIntList( List? list, { @@ -344,6 +373,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeDoubleList( List? list, { @@ -357,6 +387,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeBoolList( List? list, { @@ -370,6 +401,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeStringList( List? list, { @@ -387,6 +419,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeList( List? list, { @@ -400,6 +433,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeMap( Map? map, { @@ -413,6 +447,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void writeHiveList( _i3.HiveList<_i1.HiveObjectMixin>? list, { @@ -426,6 +461,7 @@ class MockBinaryWriter extends _i2.Mock implements _i3.BinaryWriter { ), returnValueForMissingStub: null, ); + @override void write( T? value, { diff --git a/test/notifications/notification_card_test.mocks.dart b/test/notifications/notification_card_test.mocks.dart index 52f5ee6db..edff5e5f4 100644 --- a/test/notifications/notification_card_test.mocks.dart +++ b/test/notifications/notification_card_test.mocks.dart @@ -61,6 +61,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { Invocation.getter(#client), ), ) as _i2.HTTP); + @override set client(_i2.HTTP? _client) => super.noSuchMethod( Invocation.setter( @@ -69,6 +70,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get db => (super.noSuchMethod( Invocation.getter(#db), @@ -77,11 +79,13 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { Invocation.getter(#db), ), ) as _i3.MainDB); + @override List<_i5.StackTheme> get installedThemes => (super.noSuchMethod( Invocation.getter(#installedThemes), returnValue: <_i5.StackTheme>[], ) as List<_i5.StackTheme>); + @override void init(_i3.MainDB? db) => super.noSuchMethod( Invocation.method( @@ -90,6 +94,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValueForMissingStub: null, ); + @override _i6.Future install({required _i7.Uint8List? themeArchiveData}) => (super.noSuchMethod( @@ -101,6 +106,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future remove({required String? themeId}) => (super.noSuchMethod( Invocation.method( @@ -111,6 +117,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future checkDefaultThemesOnStartup() => (super.noSuchMethod( Invocation.method( @@ -120,6 +127,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future verifyInstalled({required String? themeId}) => (super.noSuchMethod( @@ -130,6 +138,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValue: _i6.Future.value(false), ) as _i6.Future); + @override _i6.Future> fetchThemes() => (super.noSuchMethod( Invocation.method( @@ -139,6 +148,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future>.value( <_i4.StackThemeMetaData>[]), ) as _i6.Future>); + @override _i6.Future<_i7.Uint8List> fetchTheme( {required _i4.StackThemeMetaData? themeMetaData}) => @@ -150,6 +160,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValue: _i6.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), ) as _i6.Future<_i7.Uint8List>); + @override _i5.StackTheme? getTheme({required String? themeId}) => (super.noSuchMethod(Invocation.method( diff --git a/test/pages/send_view/send_view_test.mocks.dart b/test/pages/send_view/send_view_test.mocks.dart index 6a0bc127f..fee6a0d7f 100644 --- a/test/pages/send_view/send_view_test.mocks.dart +++ b/test/pages/send_view/send_view_test.mocks.dart @@ -121,6 +121,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { Invocation.getter(#nodeService), ), ) as _i2.NodeService); + @override set nodeService(_i2.NodeService? _nodeService) => super.noSuchMethod( Invocation.setter( @@ -129,6 +130,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get mainDB => (super.noSuchMethod( Invocation.getter(#mainDB), @@ -137,6 +139,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { Invocation.getter(#mainDB), ), ) as _i3.MainDB); + @override set mainDB(_i3.MainDB? _mainDB) => super.noSuchMethod( Invocation.setter( @@ -145,11 +148,13 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { ), returnValueForMissingStub: null, ); + @override List<_i5.Wallet<_i4.CryptoCurrency>> get wallets => (super.noSuchMethod( Invocation.getter(#wallets), returnValue: <_i5.Wallet<_i4.CryptoCurrency>>[], ) as List<_i5.Wallet<_i4.CryptoCurrency>>); + @override _i5.Wallet<_i4.CryptoCurrency> getWallet(String? walletId) => (super.noSuchMethod( @@ -165,6 +170,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { ), ), ) as _i5.Wallet<_i4.CryptoCurrency>); + @override void addWallet(_i5.Wallet<_i4.CryptoCurrency>? wallet) => super.noSuchMethod( Invocation.method( @@ -173,6 +179,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { ), returnValueForMissingStub: null, ); + @override _i10.Future deleteWallet( _i11.WalletInfo? info, @@ -189,6 +196,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future load( _i12.Prefs? prefs, @@ -205,6 +213,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future loadAfterStackRestore( _i12.Prefs? prefs, @@ -241,21 +250,25 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { Invocation.getter(#secureStorageInterface), ), ) as _i6.SecureStorageInterface); + @override List<_i13.NodeModel> get primaryNodes => (super.noSuchMethod( Invocation.getter(#primaryNodes), returnValue: <_i13.NodeModel>[], ) as List<_i13.NodeModel>); + @override List<_i13.NodeModel> get nodes => (super.noSuchMethod( Invocation.getter(#nodes), returnValue: <_i13.NodeModel>[], ) as List<_i13.NodeModel>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i10.Future updateDefaults() => (super.noSuchMethod( Invocation.method( @@ -265,6 +278,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future setPrimaryNodeFor({ required _i4.CryptoCurrency? coin, @@ -284,6 +298,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i13.NodeModel? getPrimaryNodeFor({required _i4.CryptoCurrency? currency}) => (super.noSuchMethod(Invocation.method( @@ -291,6 +306,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { [], {#currency: currency}, )) as _i13.NodeModel?); + @override List<_i13.NodeModel> getNodesFor(_i4.CryptoCurrency? coin) => (super.noSuchMethod( @@ -300,6 +316,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValue: <_i13.NodeModel>[], ) as List<_i13.NodeModel>); + @override _i13.NodeModel? getNodeById({required String? id}) => (super.noSuchMethod(Invocation.method( @@ -307,6 +324,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { [], {#id: id}, )) as _i13.NodeModel?); + @override List<_i13.NodeModel> failoverNodesFor( {required _i4.CryptoCurrency? currency}) => @@ -318,6 +336,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValue: <_i13.NodeModel>[], ) as List<_i13.NodeModel>); + @override _i10.Future add( _i13.NodeModel? node, @@ -336,6 +355,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future delete( String? id, @@ -352,6 +372,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future setEnabledState( String? id, @@ -370,6 +391,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future edit( _i13.NodeModel? editedNode, @@ -388,6 +410,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future updateCommunityNodes() => (super.noSuchMethod( Invocation.method( @@ -397,6 +420,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override void addListener(_i14.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -405,6 +429,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i14.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -413,6 +438,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -421,6 +447,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -447,11 +474,13 @@ class MockLocaleService extends _i1.Mock implements _i15.LocaleService { Invocation.getter(#locale), ), ) as String); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i10.Future loadLocale({bool? notify = true}) => (super.noSuchMethod( Invocation.method( @@ -462,6 +491,7 @@ class MockLocaleService extends _i1.Mock implements _i15.LocaleService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override void addListener(_i14.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -470,6 +500,7 @@ class MockLocaleService extends _i1.Mock implements _i15.LocaleService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i14.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -478,6 +509,7 @@ class MockLocaleService extends _i1.Mock implements _i15.LocaleService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -486,6 +518,7 @@ class MockLocaleService extends _i1.Mock implements _i15.LocaleService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -512,6 +545,7 @@ class MockThemeService extends _i1.Mock implements _i17.ThemeService { Invocation.getter(#client), ), ) as _i7.HTTP); + @override set client(_i7.HTTP? _client) => super.noSuchMethod( Invocation.setter( @@ -520,6 +554,7 @@ class MockThemeService extends _i1.Mock implements _i17.ThemeService { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get db => (super.noSuchMethod( Invocation.getter(#db), @@ -528,11 +563,13 @@ class MockThemeService extends _i1.Mock implements _i17.ThemeService { Invocation.getter(#db), ), ) as _i3.MainDB); + @override List<_i18.StackTheme> get installedThemes => (super.noSuchMethod( Invocation.getter(#installedThemes), returnValue: <_i18.StackTheme>[], ) as List<_i18.StackTheme>); + @override void init(_i3.MainDB? db) => super.noSuchMethod( Invocation.method( @@ -541,6 +578,7 @@ class MockThemeService extends _i1.Mock implements _i17.ThemeService { ), returnValueForMissingStub: null, ); + @override _i10.Future install({required _i19.Uint8List? themeArchiveData}) => (super.noSuchMethod( @@ -552,6 +590,7 @@ class MockThemeService extends _i1.Mock implements _i17.ThemeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future remove({required String? themeId}) => (super.noSuchMethod( Invocation.method( @@ -562,6 +601,7 @@ class MockThemeService extends _i1.Mock implements _i17.ThemeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future checkDefaultThemesOnStartup() => (super.noSuchMethod( Invocation.method( @@ -571,6 +611,7 @@ class MockThemeService extends _i1.Mock implements _i17.ThemeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future verifyInstalled({required String? themeId}) => (super.noSuchMethod( @@ -581,6 +622,7 @@ class MockThemeService extends _i1.Mock implements _i17.ThemeService { ), returnValue: _i10.Future.value(false), ) as _i10.Future); + @override _i10.Future> fetchThemes() => (super.noSuchMethod( @@ -591,6 +633,7 @@ class MockThemeService extends _i1.Mock implements _i17.ThemeService { returnValue: _i10.Future>.value( <_i17.StackThemeMetaData>[]), ) as _i10.Future>); + @override _i10.Future<_i19.Uint8List> fetchTheme( {required _i17.StackThemeMetaData? themeMetaData}) => @@ -602,6 +645,7 @@ class MockThemeService extends _i1.Mock implements _i17.ThemeService { ), returnValue: _i10.Future<_i19.Uint8List>.value(_i19.Uint8List(0)), ) as _i10.Future<_i19.Uint8List>); + @override _i18.StackTheme? getTheme({required String? themeId}) => (super.noSuchMethod(Invocation.method( @@ -624,11 +668,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { Invocation.getter(#isInitialized), returnValue: false, ) as bool); + @override int get lastUnlockedTimeout => (super.noSuchMethod( Invocation.getter(#lastUnlockedTimeout), returnValue: 0, ) as int); + @override set lastUnlockedTimeout(int? lastUnlockedTimeout) => super.noSuchMethod( Invocation.setter( @@ -637,11 +683,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override int get lastUnlocked => (super.noSuchMethod( Invocation.getter(#lastUnlocked), returnValue: 0, ) as int); + @override set lastUnlocked(int? lastUnlocked) => super.noSuchMethod( Invocation.setter( @@ -650,16 +698,19 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override int get currentNotificationId => (super.noSuchMethod( Invocation.getter(#currentNotificationId), returnValue: 0, ) as int); + @override List get walletIdsSyncOnStartup => (super.noSuchMethod( Invocation.getter(#walletIdsSyncOnStartup), returnValue: [], ) as List); + @override set walletIdsSyncOnStartup(List? walletIdsSyncOnStartup) => super.noSuchMethod( @@ -669,11 +720,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override _i20.SyncingType get syncType => (super.noSuchMethod( Invocation.getter(#syncType), returnValue: _i20.SyncingType.currentWalletOnly, ) as _i20.SyncingType); + @override set syncType(_i20.SyncingType? syncType) => super.noSuchMethod( Invocation.setter( @@ -682,11 +735,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get wifiOnly => (super.noSuchMethod( Invocation.getter(#wifiOnly), returnValue: false, ) as bool); + @override set wifiOnly(bool? wifiOnly) => super.noSuchMethod( Invocation.setter( @@ -695,11 +750,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get showFavoriteWallets => (super.noSuchMethod( Invocation.getter(#showFavoriteWallets), returnValue: false, ) as bool); + @override set showFavoriteWallets(bool? showFavoriteWallets) => super.noSuchMethod( Invocation.setter( @@ -708,6 +765,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override String get language => (super.noSuchMethod( Invocation.getter(#language), @@ -716,6 +774,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { Invocation.getter(#language), ), ) as String); + @override set language(String? newLanguage) => super.noSuchMethod( Invocation.setter( @@ -724,6 +783,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override String get currency => (super.noSuchMethod( Invocation.getter(#currency), @@ -732,6 +792,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { Invocation.getter(#currency), ), ) as String); + @override set currency(String? newCurrency) => super.noSuchMethod( Invocation.setter( @@ -740,11 +801,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get randomizePIN => (super.noSuchMethod( Invocation.getter(#randomizePIN), returnValue: false, ) as bool); + @override set randomizePIN(bool? randomizePIN) => super.noSuchMethod( Invocation.setter( @@ -753,11 +816,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get useBiometrics => (super.noSuchMethod( Invocation.getter(#useBiometrics), returnValue: false, ) as bool); + @override set useBiometrics(bool? useBiometrics) => super.noSuchMethod( Invocation.setter( @@ -766,11 +831,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get hasPin => (super.noSuchMethod( Invocation.getter(#hasPin), returnValue: false, ) as bool); + @override set hasPin(bool? hasPin) => super.noSuchMethod( Invocation.setter( @@ -779,11 +846,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override int get familiarity => (super.noSuchMethod( Invocation.getter(#familiarity), returnValue: 0, ) as int); + @override set familiarity(int? familiarity) => super.noSuchMethod( Invocation.setter( @@ -792,11 +861,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get torKillSwitch => (super.noSuchMethod( Invocation.getter(#torKillSwitch), returnValue: false, ) as bool); + @override set torKillSwitch(bool? torKillswitch) => super.noSuchMethod( Invocation.setter( @@ -805,11 +876,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get showTestNetCoins => (super.noSuchMethod( Invocation.getter(#showTestNetCoins), returnValue: false, ) as bool); + @override set showTestNetCoins(bool? showTestNetCoins) => super.noSuchMethod( Invocation.setter( @@ -818,11 +891,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get isAutoBackupEnabled => (super.noSuchMethod( Invocation.getter(#isAutoBackupEnabled), returnValue: false, ) as bool); + @override set isAutoBackupEnabled(bool? isAutoBackupEnabled) => super.noSuchMethod( Invocation.setter( @@ -831,6 +906,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override set autoBackupLocation(String? autoBackupLocation) => super.noSuchMethod( Invocation.setter( @@ -839,11 +915,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override _i21.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( Invocation.getter(#backupFrequencyType), returnValue: _i21.BackupFrequencyType.everyTenMinutes, ) as _i21.BackupFrequencyType); + @override set backupFrequencyType(_i21.BackupFrequencyType? backupFrequencyType) => super.noSuchMethod( @@ -853,6 +931,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override set lastAutoBackup(DateTime? lastAutoBackup) => super.noSuchMethod( Invocation.setter( @@ -861,11 +940,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get hideBlockExplorerWarning => (super.noSuchMethod( Invocation.getter(#hideBlockExplorerWarning), returnValue: false, ) as bool); + @override set hideBlockExplorerWarning(bool? hideBlockExplorerWarning) => super.noSuchMethod( @@ -875,11 +956,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get gotoWalletOnStartup => (super.noSuchMethod( Invocation.getter(#gotoWalletOnStartup), returnValue: false, ) as bool); + @override set gotoWalletOnStartup(bool? gotoWalletOnStartup) => super.noSuchMethod( Invocation.setter( @@ -888,6 +971,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override set startupWalletId(String? startupWalletId) => super.noSuchMethod( Invocation.setter( @@ -896,11 +980,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get externalCalls => (super.noSuchMethod( Invocation.getter(#externalCalls), returnValue: false, ) as bool); + @override set externalCalls(bool? externalCalls) => super.noSuchMethod( Invocation.setter( @@ -909,11 +995,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get enableCoinControl => (super.noSuchMethod( Invocation.getter(#enableCoinControl), returnValue: false, ) as bool); + @override set enableCoinControl(bool? enableCoinControl) => super.noSuchMethod( Invocation.setter( @@ -922,11 +1010,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get enableSystemBrightness => (super.noSuchMethod( Invocation.getter(#enableSystemBrightness), returnValue: false, ) as bool); + @override set enableSystemBrightness(bool? enableSystemBrightness) => super.noSuchMethod( @@ -936,6 +1026,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override String get themeId => (super.noSuchMethod( Invocation.getter(#themeId), @@ -944,6 +1035,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { Invocation.getter(#themeId), ), ) as String); + @override set themeId(String? themeId) => super.noSuchMethod( Invocation.setter( @@ -952,6 +1044,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override String get systemBrightnessLightThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessLightThemeId), @@ -960,6 +1053,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { Invocation.getter(#systemBrightnessLightThemeId), ), ) as String); + @override set systemBrightnessLightThemeId(String? systemBrightnessLightThemeId) => super.noSuchMethod( @@ -969,6 +1063,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override String get systemBrightnessDarkThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessDarkThemeId), @@ -977,6 +1072,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { Invocation.getter(#systemBrightnessDarkThemeId), ), ) as String); + @override set systemBrightnessDarkThemeId(String? systemBrightnessDarkThemeId) => super.noSuchMethod( @@ -986,11 +1082,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get useTor => (super.noSuchMethod( Invocation.getter(#useTor), returnValue: false, ) as bool); + @override set useTor(bool? useTor) => super.noSuchMethod( Invocation.setter( @@ -999,11 +1097,43 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + + @override + bool get autoPin => (super.noSuchMethod( + Invocation.getter(#autoPin), + returnValue: false, + ) as bool); + + @override + set autoPin(bool? autoPin) => super.noSuchMethod( + Invocation.setter( + #autoPin, + autoPin, + ), + returnValueForMissingStub: null, + ); + + @override + bool get enableExchange => (super.noSuchMethod( + Invocation.getter(#enableExchange), + returnValue: false, + ) as bool); + + @override + set enableExchange(bool? showExchange) => super.noSuchMethod( + Invocation.setter( + #enableExchange, + showExchange, + ), + returnValueForMissingStub: null, + ); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i10.Future init() => (super.noSuchMethod( Invocation.method( @@ -1013,6 +1143,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future incrementCurrentNotificationIndex() => (super.noSuchMethod( Invocation.method( @@ -1022,6 +1153,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future isExternalCallsSet() => (super.noSuchMethod( Invocation.method( @@ -1030,6 +1162,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValue: _i10.Future.value(false), ) as _i10.Future); + @override _i10.Future saveUserID(String? userId) => (super.noSuchMethod( Invocation.method( @@ -1039,6 +1172,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future saveSignupEpoch(int? signupEpoch) => (super.noSuchMethod( Invocation.method( @@ -1048,6 +1182,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i22.AmountUnit amountUnit(_i4.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( @@ -1056,6 +1191,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValue: _i22.AmountUnit.normal, ) as _i22.AmountUnit); + @override void updateAmountUnit({ required _i4.CryptoCurrency? coin, @@ -1072,6 +1208,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override int maxDecimals(_i4.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( @@ -1080,6 +1217,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValue: 0, ) as int); + @override void updateMaxDecimals({ required _i4.CryptoCurrency? coin, @@ -1096,6 +1234,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override _i8.FusionInfo getFusionServerInfo(_i4.CryptoCurrency? coin) => (super.noSuchMethod( @@ -1111,6 +1250,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), ), ) as _i8.FusionInfo); + @override void setFusionServerInfo( _i4.CryptoCurrency? coin, @@ -1126,6 +1266,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override void addListener(_i14.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -1134,6 +1275,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override void removeListener(_i14.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -1142,6 +1284,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -1150,6 +1293,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/price_test.mocks.dart b/test/price_test.mocks.dart index 8ba8c6d26..3689ebb89 100644 --- a/test/price_test.mocks.dart +++ b/test/price_test.mocks.dart @@ -70,6 +70,7 @@ class MockHTTP extends _i1.Mock implements _i2.HTTP { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> post({ required Uri? url, diff --git a/test/screen_tests/address_book_view/address_book_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/address_book_view_screen_test.mocks.dart index b7a88864d..dbbc39497 100644 --- a/test/screen_tests/address_book_view/address_book_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/address_book_view_screen_test.mocks.dart @@ -47,11 +47,13 @@ class MockAddressBookService extends _i1.Mock Invocation.getter(#contacts), returnValue: <_i2.ContactEntry>[], ) as List<_i2.ContactEntry>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i2.ContactEntry getContactById(String? id) => (super.noSuchMethod( Invocation.method( @@ -66,6 +68,7 @@ class MockAddressBookService extends _i1.Mock ), ), ) as _i2.ContactEntry); + @override _i4.Future> search(String? text) => (super.noSuchMethod( @@ -76,6 +79,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i4.Future>.value(<_i2.ContactEntry>[]), ) as _i4.Future>); + @override bool matches( String? term, @@ -91,6 +95,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: false, ) as bool); + @override _i4.Future addContact(_i2.ContactEntry? contact) => (super.noSuchMethod( Invocation.method( @@ -99,6 +104,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: _i4.Future.value(false), ) as _i4.Future); + @override _i4.Future editContact(_i2.ContactEntry? editedContact) => (super.noSuchMethod( @@ -108,6 +114,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: _i4.Future.value(false), ) as _i4.Future); + @override _i4.Future removeContact(String? id) => (super.noSuchMethod( Invocation.method( @@ -117,6 +124,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override void addListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -125,6 +133,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -133,6 +142,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -141,6 +151,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart index 63bbc41c5..25bfa08e4 100644 --- a/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart @@ -88,11 +88,13 @@ class MockAddressBookService extends _i1.Mock Invocation.getter(#contacts), returnValue: <_i3.ContactEntry>[], ) as List<_i3.ContactEntry>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i3.ContactEntry getContactById(String? id) => (super.noSuchMethod( Invocation.method( @@ -107,6 +109,7 @@ class MockAddressBookService extends _i1.Mock ), ), ) as _i3.ContactEntry); + @override _i5.Future> search(String? text) => (super.noSuchMethod( @@ -117,6 +120,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i5.Future>.value(<_i3.ContactEntry>[]), ) as _i5.Future>); + @override bool matches( String? term, @@ -132,6 +136,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: false, ) as bool); + @override _i5.Future addContact(_i3.ContactEntry? contact) => (super.noSuchMethod( Invocation.method( @@ -140,6 +145,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: _i5.Future.value(false), ) as _i5.Future); + @override _i5.Future editContact(_i3.ContactEntry? editedContact) => (super.noSuchMethod( @@ -149,6 +155,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: _i5.Future.value(false), ) as _i5.Future); + @override _i5.Future removeContact(String? id) => (super.noSuchMethod( Invocation.method( @@ -158,6 +165,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override void addListener(_i7.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -166,6 +174,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void removeListener(_i7.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -174,6 +183,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -182,6 +192,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart index 97a359919..b2f02c854 100644 --- a/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart @@ -49,11 +49,13 @@ class MockAddressBookService extends _i1.Mock Invocation.getter(#contacts), returnValue: <_i2.ContactEntry>[], ) as List<_i2.ContactEntry>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i2.ContactEntry getContactById(String? id) => (super.noSuchMethod( Invocation.method( @@ -68,6 +70,7 @@ class MockAddressBookService extends _i1.Mock ), ), ) as _i2.ContactEntry); + @override _i4.Future> search(String? text) => (super.noSuchMethod( @@ -78,6 +81,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i4.Future>.value(<_i2.ContactEntry>[]), ) as _i4.Future>); + @override bool matches( String? term, @@ -93,6 +97,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: false, ) as bool); + @override _i4.Future addContact(_i2.ContactEntry? contact) => (super.noSuchMethod( Invocation.method( @@ -101,6 +106,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: _i4.Future.value(false), ) as _i4.Future); + @override _i4.Future editContact(_i2.ContactEntry? editedContact) => (super.noSuchMethod( @@ -110,6 +116,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: _i4.Future.value(false), ) as _i4.Future); + @override _i4.Future removeContact(String? id) => (super.noSuchMethod( Invocation.method( @@ -119,6 +126,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override void addListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -127,6 +135,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -135,6 +144,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -143,6 +153,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -169,11 +180,13 @@ class MockLocaleService extends _i1.Mock implements _i6.LocaleService { Invocation.getter(#locale), ), ) as String); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i4.Future loadLocale({bool? notify = true}) => (super.noSuchMethod( Invocation.method( @@ -184,6 +197,7 @@ class MockLocaleService extends _i1.Mock implements _i6.LocaleService { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override void addListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -192,6 +206,7 @@ class MockLocaleService extends _i1.Mock implements _i6.LocaleService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -200,6 +215,7 @@ class MockLocaleService extends _i1.Mock implements _i6.LocaleService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -208,6 +224,7 @@ class MockLocaleService extends _i1.Mock implements _i6.LocaleService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart index 1f195d5c3..2c93e063c 100644 --- a/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart @@ -47,11 +47,13 @@ class MockAddressBookService extends _i1.Mock Invocation.getter(#contacts), returnValue: <_i2.ContactEntry>[], ) as List<_i2.ContactEntry>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i2.ContactEntry getContactById(String? id) => (super.noSuchMethod( Invocation.method( @@ -66,6 +68,7 @@ class MockAddressBookService extends _i1.Mock ), ), ) as _i2.ContactEntry); + @override _i4.Future> search(String? text) => (super.noSuchMethod( @@ -76,6 +79,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i4.Future>.value(<_i2.ContactEntry>[]), ) as _i4.Future>); + @override bool matches( String? term, @@ -91,6 +95,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: false, ) as bool); + @override _i4.Future addContact(_i2.ContactEntry? contact) => (super.noSuchMethod( Invocation.method( @@ -99,6 +104,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: _i4.Future.value(false), ) as _i4.Future); + @override _i4.Future editContact(_i2.ContactEntry? editedContact) => (super.noSuchMethod( @@ -108,6 +114,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: _i4.Future.value(false), ) as _i4.Future); + @override _i4.Future removeContact(String? id) => (super.noSuchMethod( Invocation.method( @@ -117,6 +124,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override void addListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -125,6 +133,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -133,6 +142,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -141,6 +151,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/screen_tests/exchange/exchange_view_test.mocks.dart b/test/screen_tests/exchange/exchange_view_test.mocks.dart index f00e95494..b7a8d49d5 100644 --- a/test/screen_tests/exchange/exchange_view_test.mocks.dart +++ b/test/screen_tests/exchange/exchange_view_test.mocks.dart @@ -97,11 +97,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { Invocation.getter(#isInitialized), returnValue: false, ) as bool); + @override int get lastUnlockedTimeout => (super.noSuchMethod( Invocation.getter(#lastUnlockedTimeout), returnValue: 0, ) as int); + @override set lastUnlockedTimeout(int? lastUnlockedTimeout) => super.noSuchMethod( Invocation.setter( @@ -110,11 +112,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override int get lastUnlocked => (super.noSuchMethod( Invocation.getter(#lastUnlocked), returnValue: 0, ) as int); + @override set lastUnlocked(int? lastUnlocked) => super.noSuchMethod( Invocation.setter( @@ -123,16 +127,19 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override int get currentNotificationId => (super.noSuchMethod( Invocation.getter(#currentNotificationId), returnValue: 0, ) as int); + @override List get walletIdsSyncOnStartup => (super.noSuchMethod( Invocation.getter(#walletIdsSyncOnStartup), returnValue: [], ) as List); + @override set walletIdsSyncOnStartup(List? walletIdsSyncOnStartup) => super.noSuchMethod( @@ -142,11 +149,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override _i6.SyncingType get syncType => (super.noSuchMethod( Invocation.getter(#syncType), returnValue: _i6.SyncingType.currentWalletOnly, ) as _i6.SyncingType); + @override set syncType(_i6.SyncingType? syncType) => super.noSuchMethod( Invocation.setter( @@ -155,11 +164,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override bool get wifiOnly => (super.noSuchMethod( Invocation.getter(#wifiOnly), returnValue: false, ) as bool); + @override set wifiOnly(bool? wifiOnly) => super.noSuchMethod( Invocation.setter( @@ -168,11 +179,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override bool get showFavoriteWallets => (super.noSuchMethod( Invocation.getter(#showFavoriteWallets), returnValue: false, ) as bool); + @override set showFavoriteWallets(bool? showFavoriteWallets) => super.noSuchMethod( Invocation.setter( @@ -181,6 +194,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override String get language => (super.noSuchMethod( Invocation.getter(#language), @@ -189,6 +203,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { Invocation.getter(#language), ), ) as String); + @override set language(String? newLanguage) => super.noSuchMethod( Invocation.setter( @@ -197,6 +212,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override String get currency => (super.noSuchMethod( Invocation.getter(#currency), @@ -205,6 +221,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { Invocation.getter(#currency), ), ) as String); + @override set currency(String? newCurrency) => super.noSuchMethod( Invocation.setter( @@ -213,11 +230,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override bool get randomizePIN => (super.noSuchMethod( Invocation.getter(#randomizePIN), returnValue: false, ) as bool); + @override set randomizePIN(bool? randomizePIN) => super.noSuchMethod( Invocation.setter( @@ -226,11 +245,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override bool get useBiometrics => (super.noSuchMethod( Invocation.getter(#useBiometrics), returnValue: false, ) as bool); + @override set useBiometrics(bool? useBiometrics) => super.noSuchMethod( Invocation.setter( @@ -239,11 +260,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override bool get hasPin => (super.noSuchMethod( Invocation.getter(#hasPin), returnValue: false, ) as bool); + @override set hasPin(bool? hasPin) => super.noSuchMethod( Invocation.setter( @@ -252,11 +275,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override int get familiarity => (super.noSuchMethod( Invocation.getter(#familiarity), returnValue: 0, ) as int); + @override set familiarity(int? familiarity) => super.noSuchMethod( Invocation.setter( @@ -265,11 +290,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override bool get torKillSwitch => (super.noSuchMethod( Invocation.getter(#torKillSwitch), returnValue: false, ) as bool); + @override set torKillSwitch(bool? torKillswitch) => super.noSuchMethod( Invocation.setter( @@ -278,11 +305,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override bool get showTestNetCoins => (super.noSuchMethod( Invocation.getter(#showTestNetCoins), returnValue: false, ) as bool); + @override set showTestNetCoins(bool? showTestNetCoins) => super.noSuchMethod( Invocation.setter( @@ -291,11 +320,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override bool get isAutoBackupEnabled => (super.noSuchMethod( Invocation.getter(#isAutoBackupEnabled), returnValue: false, ) as bool); + @override set isAutoBackupEnabled(bool? isAutoBackupEnabled) => super.noSuchMethod( Invocation.setter( @@ -304,6 +335,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override set autoBackupLocation(String? autoBackupLocation) => super.noSuchMethod( Invocation.setter( @@ -312,11 +344,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override _i8.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( Invocation.getter(#backupFrequencyType), returnValue: _i8.BackupFrequencyType.everyTenMinutes, ) as _i8.BackupFrequencyType); + @override set backupFrequencyType(_i8.BackupFrequencyType? backupFrequencyType) => super.noSuchMethod( @@ -326,6 +360,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override set lastAutoBackup(DateTime? lastAutoBackup) => super.noSuchMethod( Invocation.setter( @@ -334,11 +369,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override bool get hideBlockExplorerWarning => (super.noSuchMethod( Invocation.getter(#hideBlockExplorerWarning), returnValue: false, ) as bool); + @override set hideBlockExplorerWarning(bool? hideBlockExplorerWarning) => super.noSuchMethod( @@ -348,11 +385,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override bool get gotoWalletOnStartup => (super.noSuchMethod( Invocation.getter(#gotoWalletOnStartup), returnValue: false, ) as bool); + @override set gotoWalletOnStartup(bool? gotoWalletOnStartup) => super.noSuchMethod( Invocation.setter( @@ -361,6 +400,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override set startupWalletId(String? startupWalletId) => super.noSuchMethod( Invocation.setter( @@ -369,11 +409,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override bool get externalCalls => (super.noSuchMethod( Invocation.getter(#externalCalls), returnValue: false, ) as bool); + @override set externalCalls(bool? externalCalls) => super.noSuchMethod( Invocation.setter( @@ -382,11 +424,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override bool get enableCoinControl => (super.noSuchMethod( Invocation.getter(#enableCoinControl), returnValue: false, ) as bool); + @override set enableCoinControl(bool? enableCoinControl) => super.noSuchMethod( Invocation.setter( @@ -395,11 +439,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override bool get enableSystemBrightness => (super.noSuchMethod( Invocation.getter(#enableSystemBrightness), returnValue: false, ) as bool); + @override set enableSystemBrightness(bool? enableSystemBrightness) => super.noSuchMethod( @@ -409,6 +455,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override String get themeId => (super.noSuchMethod( Invocation.getter(#themeId), @@ -417,6 +464,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { Invocation.getter(#themeId), ), ) as String); + @override set themeId(String? themeId) => super.noSuchMethod( Invocation.setter( @@ -425,6 +473,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override String get systemBrightnessLightThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessLightThemeId), @@ -433,6 +482,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { Invocation.getter(#systemBrightnessLightThemeId), ), ) as String); + @override set systemBrightnessLightThemeId(String? systemBrightnessLightThemeId) => super.noSuchMethod( @@ -442,6 +492,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override String get systemBrightnessDarkThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessDarkThemeId), @@ -450,6 +501,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { Invocation.getter(#systemBrightnessDarkThemeId), ), ) as String); + @override set systemBrightnessDarkThemeId(String? systemBrightnessDarkThemeId) => super.noSuchMethod( @@ -459,11 +511,13 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override bool get useTor => (super.noSuchMethod( Invocation.getter(#useTor), returnValue: false, ) as bool); + @override set useTor(bool? useTor) => super.noSuchMethod( Invocation.setter( @@ -472,11 +526,43 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + + @override + bool get autoPin => (super.noSuchMethod( + Invocation.getter(#autoPin), + returnValue: false, + ) as bool); + + @override + set autoPin(bool? autoPin) => super.noSuchMethod( + Invocation.setter( + #autoPin, + autoPin, + ), + returnValueForMissingStub: null, + ); + + @override + bool get enableExchange => (super.noSuchMethod( + Invocation.getter(#enableExchange), + returnValue: false, + ) as bool); + + @override + set enableExchange(bool? showExchange) => super.noSuchMethod( + Invocation.setter( + #enableExchange, + showExchange, + ), + returnValueForMissingStub: null, + ); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i9.Future init() => (super.noSuchMethod( Invocation.method( @@ -486,6 +572,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future incrementCurrentNotificationIndex() => (super.noSuchMethod( Invocation.method( @@ -495,6 +582,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future isExternalCallsSet() => (super.noSuchMethod( Invocation.method( @@ -503,6 +591,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValue: _i9.Future.value(false), ) as _i9.Future); + @override _i9.Future saveUserID(String? userId) => (super.noSuchMethod( Invocation.method( @@ -512,6 +601,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future saveSignupEpoch(int? signupEpoch) => (super.noSuchMethod( Invocation.method( @@ -521,6 +611,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i10.AmountUnit amountUnit(_i11.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( @@ -529,6 +620,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValue: _i10.AmountUnit.normal, ) as _i10.AmountUnit); + @override void updateAmountUnit({ required _i11.CryptoCurrency? coin, @@ -545,6 +637,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override int maxDecimals(_i11.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( @@ -553,6 +646,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValue: 0, ) as int); + @override void updateMaxDecimals({ required _i11.CryptoCurrency? coin, @@ -569,6 +663,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override _i2.FusionInfo getFusionServerInfo(_i11.CryptoCurrency? coin) => (super.noSuchMethod( @@ -584,6 +679,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), ), ) as _i2.FusionInfo); + @override void setFusionServerInfo( _i11.CryptoCurrency? coin, @@ -599,6 +695,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override void addListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -607,6 +704,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override void removeListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -615,6 +713,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -623,6 +722,7 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -646,16 +746,19 @@ class MockTradesService extends _i1.Mock implements _i13.TradesService { Invocation.getter(#trades), returnValue: <_i14.Trade>[], ) as List<_i14.Trade>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i14.Trade? get(String? tradeId) => (super.noSuchMethod(Invocation.method( #get, [tradeId], )) as _i14.Trade?); + @override _i9.Future add({ required _i14.Trade? trade, @@ -673,6 +776,7 @@ class MockTradesService extends _i1.Mock implements _i13.TradesService { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future edit({ required _i14.Trade? trade, @@ -690,6 +794,7 @@ class MockTradesService extends _i1.Mock implements _i13.TradesService { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future delete({ required _i14.Trade? trade, @@ -707,6 +812,7 @@ class MockTradesService extends _i1.Mock implements _i13.TradesService { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future deleteByUuid({ required String? uuid, @@ -724,6 +830,7 @@ class MockTradesService extends _i1.Mock implements _i13.TradesService { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override void addListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -732,6 +839,7 @@ class MockTradesService extends _i1.Mock implements _i13.TradesService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -740,6 +848,7 @@ class MockTradesService extends _i1.Mock implements _i13.TradesService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -748,6 +857,7 @@ class MockTradesService extends _i1.Mock implements _i13.TradesService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -771,11 +881,13 @@ class MockTradeNotesService extends _i1.Mock implements _i15.TradeNotesService { Invocation.getter(#all), returnValue: {}, ) as Map); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override String getNote({required String? tradeId}) => (super.noSuchMethod( Invocation.method( @@ -792,6 +904,7 @@ class MockTradeNotesService extends _i1.Mock implements _i15.TradeNotesService { ), ), ) as String); + @override _i9.Future set({ required String? tradeId, @@ -809,6 +922,7 @@ class MockTradeNotesService extends _i1.Mock implements _i15.TradeNotesService { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future delete({required String? tradeId}) => (super.noSuchMethod( Invocation.method( @@ -819,6 +933,7 @@ class MockTradeNotesService extends _i1.Mock implements _i15.TradeNotesService { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override void addListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -827,6 +942,7 @@ class MockTradeNotesService extends _i1.Mock implements _i15.TradeNotesService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -835,6 +951,7 @@ class MockTradeNotesService extends _i1.Mock implements _i15.TradeNotesService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -843,6 +960,7 @@ class MockTradeNotesService extends _i1.Mock implements _i15.TradeNotesService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -869,6 +987,7 @@ class MockChangeNowAPI extends _i1.Mock implements _i16.ChangeNowAPI { Invocation.getter(#client), ), ) as _i3.HTTP); + @override _i9.Future<_i4.ExchangeResponse>> getAvailableCurrencies({ bool? fixedRate, @@ -897,6 +1016,7 @@ class MockChangeNowAPI extends _i1.Mock implements _i16.ChangeNowAPI { ), )), ) as _i9.Future<_i4.ExchangeResponse>>); + @override _i9.Future<_i4.ExchangeResponse>> getCurrenciesV2() => (super.noSuchMethod( @@ -914,6 +1034,7 @@ class MockChangeNowAPI extends _i1.Mock implements _i16.ChangeNowAPI { ), )), ) as _i9.Future<_i4.ExchangeResponse>>); + @override _i9.Future<_i4.ExchangeResponse>> getPairedCurrencies({ required String? ticker, @@ -942,6 +1063,7 @@ class MockChangeNowAPI extends _i1.Mock implements _i16.ChangeNowAPI { ), )), ) as _i9.Future<_i4.ExchangeResponse>>); + @override _i9.Future<_i4.ExchangeResponse<_i18.Decimal>> getMinimalExchangeAmount({ required String? fromTicker, @@ -972,6 +1094,7 @@ class MockChangeNowAPI extends _i1.Mock implements _i16.ChangeNowAPI { ), )), ) as _i9.Future<_i4.ExchangeResponse<_i18.Decimal>>); + @override _i9.Future<_i4.ExchangeResponse<_i19.Range>> getRange({ required String? fromTicker, @@ -1005,6 +1128,7 @@ class MockChangeNowAPI extends _i1.Mock implements _i16.ChangeNowAPI { ), )), ) as _i9.Future<_i4.ExchangeResponse<_i19.Range>>); + @override _i9.Future<_i4.ExchangeResponse<_i20.Estimate>> getEstimatedExchangeAmount({ required String? fromTicker, @@ -1038,6 +1162,7 @@ class MockChangeNowAPI extends _i1.Mock implements _i16.ChangeNowAPI { ), )), ) as _i9.Future<_i4.ExchangeResponse<_i20.Estimate>>); + @override _i9.Future<_i4.ExchangeResponse<_i20.Estimate>> getEstimatedExchangeAmountFixedRate({ @@ -1078,6 +1203,7 @@ class MockChangeNowAPI extends _i1.Mock implements _i16.ChangeNowAPI { ), )), ) as _i9.Future<_i4.ExchangeResponse<_i20.Estimate>>); + @override _i9.Future<_i4.ExchangeResponse<_i21.CNExchangeEstimate>> getEstimatedExchangeAmountV2({ @@ -1125,6 +1251,7 @@ class MockChangeNowAPI extends _i1.Mock implements _i16.ChangeNowAPI { ), )), ) as _i9.Future<_i4.ExchangeResponse<_i21.CNExchangeEstimate>>); + @override _i9.Future<_i4.ExchangeResponse>> getAvailableFixedRateMarkets({String? apiKey}) => (super.noSuchMethod( @@ -1144,6 +1271,7 @@ class MockChangeNowAPI extends _i1.Mock implements _i16.ChangeNowAPI { ), )), ) as _i9.Future<_i4.ExchangeResponse>>); + @override _i9.Future<_i4.ExchangeResponse<_i23.ExchangeTransaction>> createStandardExchangeTransaction({ @@ -1197,6 +1325,7 @@ class MockChangeNowAPI extends _i1.Mock implements _i16.ChangeNowAPI { ), )), ) as _i9.Future<_i4.ExchangeResponse<_i23.ExchangeTransaction>>); + @override _i9.Future<_i4.ExchangeResponse<_i23.ExchangeTransaction>> createFixedRateExchangeTransaction({ @@ -1256,6 +1385,7 @@ class MockChangeNowAPI extends _i1.Mock implements _i16.ChangeNowAPI { ), )), ) as _i9.Future<_i4.ExchangeResponse<_i23.ExchangeTransaction>>); + @override _i9.Future< _i4 @@ -1286,6 +1416,7 @@ class MockChangeNowAPI extends _i1.Mock implements _i16.ChangeNowAPI { ), )), ) as _i9.Future<_i4.ExchangeResponse<_i24.ExchangeTransactionStatus>>); + @override _i9.Future<_i4.ExchangeResponse>> getAvailableFloatingRatePairs({bool? includePartners = false}) => diff --git a/test/screen_tests/lockscreen_view_screen_test.mocks.dart b/test/screen_tests/lockscreen_view_screen_test.mocks.dart index 2a0844351..a3523a093 100644 --- a/test/screen_tests/lockscreen_view_screen_test.mocks.dart +++ b/test/screen_tests/lockscreen_view_screen_test.mocks.dart @@ -54,11 +54,13 @@ class MockWalletsService extends _i1.Mock implements _i3.WalletsService { returnValue: _i4.Future>.value( {}), ) as _i4.Future>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override void addListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -67,6 +69,7 @@ class MockWalletsService extends _i1.Mock implements _i3.WalletsService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -75,6 +78,7 @@ class MockWalletsService extends _i1.Mock implements _i3.WalletsService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -83,6 +87,7 @@ class MockWalletsService extends _i1.Mock implements _i3.WalletsService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -109,21 +114,25 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { 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 updateDefaults() => (super.noSuchMethod( Invocation.method( @@ -133,6 +142,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future setPrimaryNodeFor({ required _i8.CryptoCurrency? coin, @@ -152,6 +162,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i7.NodeModel? getPrimaryNodeFor({required _i8.CryptoCurrency? currency}) => (super.noSuchMethod(Invocation.method( @@ -159,6 +170,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { [], {#currency: currency}, )) as _i7.NodeModel?); + @override List<_i7.NodeModel> getNodesFor(_i8.CryptoCurrency? coin) => (super.noSuchMethod( @@ -168,6 +180,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { ), returnValue: <_i7.NodeModel>[], ) as List<_i7.NodeModel>); + @override _i7.NodeModel? getNodeById({required String? id}) => (super.noSuchMethod(Invocation.method( @@ -175,6 +188,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { [], {#id: id}, )) as _i7.NodeModel?); + @override List<_i7.NodeModel> failoverNodesFor( {required _i8.CryptoCurrency? currency}) => @@ -186,6 +200,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { ), returnValue: <_i7.NodeModel>[], ) as List<_i7.NodeModel>); + @override _i4.Future add( _i7.NodeModel? node, @@ -204,6 +219,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future delete( String? id, @@ -220,6 +236,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future setEnabledState( String? id, @@ -238,6 +255,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future edit( _i7.NodeModel? editedNode, @@ -256,6 +274,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future updateCommunityNodes() => (super.noSuchMethod( Invocation.method( @@ -265,6 +284,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override void addListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -273,6 +293,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -281,6 +302,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -289,6 +311,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart b/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart index 9860ea5e7..3d96a2dcf 100644 --- a/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart +++ b/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart @@ -39,11 +39,13 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { returnValue: _i3.Future>.value( {}), ) as _i3.Future>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override void addListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -52,6 +54,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -60,6 +63,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -68,6 +72,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -94,11 +99,13 @@ class MockLocaleService extends _i1.Mock implements _i5.LocaleService { Invocation.getter(#locale), ), ) as String); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i3.Future loadLocale({bool? notify = true}) => (super.noSuchMethod( Invocation.method( @@ -109,6 +116,7 @@ class MockLocaleService extends _i1.Mock implements _i5.LocaleService { returnValue: _i3.Future.value(), returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); + @override void addListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -117,6 +125,7 @@ class MockLocaleService extends _i1.Mock implements _i5.LocaleService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -125,6 +134,7 @@ class MockLocaleService extends _i1.Mock implements _i5.LocaleService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -133,6 +143,7 @@ class MockLocaleService extends _i1.Mock implements _i5.LocaleService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart b/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart index 2cc8ad83e..7cccdb73e 100644 --- a/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart +++ b/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart @@ -39,11 +39,13 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { returnValue: _i3.Future>.value( {}), ) as _i3.Future>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override void addListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -52,6 +54,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -60,6 +63,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -68,6 +72,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -94,11 +99,13 @@ class MockLocaleService extends _i1.Mock implements _i5.LocaleService { Invocation.getter(#locale), ), ) as String); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i3.Future loadLocale({bool? notify = true}) => (super.noSuchMethod( Invocation.method( @@ -109,6 +116,7 @@ class MockLocaleService extends _i1.Mock implements _i5.LocaleService { returnValue: _i3.Future.value(), returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); + @override void addListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -117,6 +125,7 @@ class MockLocaleService extends _i1.Mock implements _i5.LocaleService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -125,6 +134,7 @@ class MockLocaleService extends _i1.Mock implements _i5.LocaleService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -133,6 +143,7 @@ class MockLocaleService extends _i1.Mock implements _i5.LocaleService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart b/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart index 2da39262c..9362ee778 100644 --- a/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart +++ b/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart @@ -39,11 +39,13 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { returnValue: _i3.Future>.value( {}), ) as _i3.Future>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override void addListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -52,6 +54,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -60,6 +63,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -68,6 +72,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -94,11 +99,13 @@ class MockLocaleService extends _i1.Mock implements _i5.LocaleService { Invocation.getter(#locale), ), ) as String); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i3.Future loadLocale({bool? notify = true}) => (super.noSuchMethod( Invocation.method( @@ -109,6 +116,7 @@ class MockLocaleService extends _i1.Mock implements _i5.LocaleService { returnValue: _i3.Future.value(), returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); + @override void addListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -117,6 +125,7 @@ class MockLocaleService extends _i1.Mock implements _i5.LocaleService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -125,6 +134,7 @@ class MockLocaleService extends _i1.Mock implements _i5.LocaleService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -133,6 +143,7 @@ class MockLocaleService extends _i1.Mock implements _i5.LocaleService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart b/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart index 7544b767c..951e5306d 100644 --- a/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart @@ -37,11 +37,13 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { returnValue: _i3.Future>.value( {}), ) as _i3.Future>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override void addListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -50,6 +52,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -58,6 +61,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -66,6 +70,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( 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 index 5aed33644..4f9addbc7 100644 --- a/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart @@ -54,11 +54,13 @@ class MockWalletsService extends _i1.Mock implements _i3.WalletsService { returnValue: _i4.Future>.value( {}), ) as _i4.Future>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override void addListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -67,6 +69,7 @@ class MockWalletsService extends _i1.Mock implements _i3.WalletsService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -75,6 +78,7 @@ class MockWalletsService extends _i1.Mock implements _i3.WalletsService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -83,6 +87,7 @@ class MockWalletsService extends _i1.Mock implements _i3.WalletsService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -109,21 +114,25 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { 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 updateDefaults() => (super.noSuchMethod( Invocation.method( @@ -133,6 +142,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future setPrimaryNodeFor({ required _i8.CryptoCurrency? coin, @@ -152,6 +162,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i7.NodeModel? getPrimaryNodeFor({required _i8.CryptoCurrency? currency}) => (super.noSuchMethod(Invocation.method( @@ -159,6 +170,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { [], {#currency: currency}, )) as _i7.NodeModel?); + @override List<_i7.NodeModel> getNodesFor(_i8.CryptoCurrency? coin) => (super.noSuchMethod( @@ -168,6 +180,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { ), returnValue: <_i7.NodeModel>[], ) as List<_i7.NodeModel>); + @override _i7.NodeModel? getNodeById({required String? id}) => (super.noSuchMethod(Invocation.method( @@ -175,6 +188,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { [], {#id: id}, )) as _i7.NodeModel?); + @override List<_i7.NodeModel> failoverNodesFor( {required _i8.CryptoCurrency? currency}) => @@ -186,6 +200,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { ), returnValue: <_i7.NodeModel>[], ) as List<_i7.NodeModel>); + @override _i4.Future add( _i7.NodeModel? node, @@ -204,6 +219,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future delete( String? id, @@ -220,6 +236,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future setEnabledState( String? id, @@ -238,6 +255,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future edit( _i7.NodeModel? editedNode, @@ -256,6 +274,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future updateCommunityNodes() => (super.noSuchMethod( Invocation.method( @@ -265,6 +284,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override void addListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -273,6 +293,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -281,6 +302,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -289,6 +311,7 @@ class MockNodeService extends _i1.Mock implements _i6.NodeService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/screen_tests/onboarding/name_your_wallet_view_screen_test.mocks.dart b/test/screen_tests/onboarding/name_your_wallet_view_screen_test.mocks.dart index ea7cadff1..5983f2f41 100644 --- a/test/screen_tests/onboarding/name_your_wallet_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/name_your_wallet_view_screen_test.mocks.dart @@ -37,11 +37,13 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { returnValue: _i3.Future>.value( {}), ) as _i3.Future>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override void addListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -50,6 +52,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -58,6 +61,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -66,6 +70,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( 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 7ce8c0b95..0603d61be 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 @@ -95,11 +95,13 @@ class MockWalletsService extends _i1.Mock implements _i6.WalletsService { returnValue: _i5.Future>.value( {}), ) as _i5.Future>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override void addListener(_i7.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -108,6 +110,7 @@ class MockWalletsService extends _i1.Mock implements _i6.WalletsService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i7.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -116,6 +119,7 @@ class MockWalletsService extends _i1.Mock implements _i6.WalletsService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -124,6 +128,7 @@ class MockWalletsService extends _i1.Mock implements _i6.WalletsService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -150,21 +155,25 @@ class MockNodeService extends _i1.Mock implements _i8.NodeService { Invocation.getter(#secureStorageInterface), ), ) as _i3.SecureStorageInterface); + @override List<_i9.NodeModel> get primaryNodes => (super.noSuchMethod( Invocation.getter(#primaryNodes), returnValue: <_i9.NodeModel>[], ) as List<_i9.NodeModel>); + @override List<_i9.NodeModel> get nodes => (super.noSuchMethod( Invocation.getter(#nodes), returnValue: <_i9.NodeModel>[], ) as List<_i9.NodeModel>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i5.Future updateDefaults() => (super.noSuchMethod( Invocation.method( @@ -174,6 +183,7 @@ class MockNodeService extends _i1.Mock implements _i8.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future setPrimaryNodeFor({ required _i10.CryptoCurrency? coin, @@ -193,6 +203,7 @@ class MockNodeService extends _i1.Mock implements _i8.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i9.NodeModel? getPrimaryNodeFor({required _i10.CryptoCurrency? currency}) => (super.noSuchMethod(Invocation.method( @@ -200,6 +211,7 @@ class MockNodeService extends _i1.Mock implements _i8.NodeService { [], {#currency: currency}, )) as _i9.NodeModel?); + @override List<_i9.NodeModel> getNodesFor(_i10.CryptoCurrency? coin) => (super.noSuchMethod( @@ -209,6 +221,7 @@ class MockNodeService extends _i1.Mock implements _i8.NodeService { ), returnValue: <_i9.NodeModel>[], ) as List<_i9.NodeModel>); + @override _i9.NodeModel? getNodeById({required String? id}) => (super.noSuchMethod(Invocation.method( @@ -216,6 +229,7 @@ class MockNodeService extends _i1.Mock implements _i8.NodeService { [], {#id: id}, )) as _i9.NodeModel?); + @override List<_i9.NodeModel> failoverNodesFor( {required _i10.CryptoCurrency? currency}) => @@ -227,6 +241,7 @@ class MockNodeService extends _i1.Mock implements _i8.NodeService { ), returnValue: <_i9.NodeModel>[], ) as List<_i9.NodeModel>); + @override _i5.Future add( _i9.NodeModel? node, @@ -245,6 +260,7 @@ class MockNodeService extends _i1.Mock implements _i8.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future delete( String? id, @@ -261,6 +277,7 @@ class MockNodeService extends _i1.Mock implements _i8.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future setEnabledState( String? id, @@ -279,6 +296,7 @@ class MockNodeService extends _i1.Mock implements _i8.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future edit( _i9.NodeModel? editedNode, @@ -297,6 +315,7 @@ class MockNodeService extends _i1.Mock implements _i8.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future updateCommunityNodes() => (super.noSuchMethod( Invocation.method( @@ -306,6 +325,7 @@ class MockNodeService extends _i1.Mock implements _i8.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override void addListener(_i7.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -314,6 +334,7 @@ class MockNodeService extends _i1.Mock implements _i8.NodeService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i7.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -322,6 +343,7 @@ class MockNodeService extends _i1.Mock implements _i8.NodeService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -330,6 +352,7 @@ class MockNodeService extends _i1.Mock implements _i8.NodeService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( 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 6216d4e86..75a10f43e 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 @@ -54,21 +54,25 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { Invocation.getter(#secureStorageInterface), ), ) as _i2.SecureStorageInterface); + @override List<_i4.NodeModel> get primaryNodes => (super.noSuchMethod( Invocation.getter(#primaryNodes), returnValue: <_i4.NodeModel>[], ) as List<_i4.NodeModel>); + @override List<_i4.NodeModel> get nodes => (super.noSuchMethod( Invocation.getter(#nodes), returnValue: <_i4.NodeModel>[], ) as List<_i4.NodeModel>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i5.Future updateDefaults() => (super.noSuchMethod( Invocation.method( @@ -78,6 +82,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future setPrimaryNodeFor({ required _i6.CryptoCurrency? coin, @@ -97,6 +102,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i4.NodeModel? getPrimaryNodeFor({required _i6.CryptoCurrency? currency}) => (super.noSuchMethod(Invocation.method( @@ -104,6 +110,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { [], {#currency: currency}, )) as _i4.NodeModel?); + @override List<_i4.NodeModel> getNodesFor(_i6.CryptoCurrency? coin) => (super.noSuchMethod( @@ -113,6 +120,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValue: <_i4.NodeModel>[], ) as List<_i4.NodeModel>); + @override _i4.NodeModel? getNodeById({required String? id}) => (super.noSuchMethod(Invocation.method( @@ -120,6 +128,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { [], {#id: id}, )) as _i4.NodeModel?); + @override List<_i4.NodeModel> failoverNodesFor( {required _i6.CryptoCurrency? currency}) => @@ -131,6 +140,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValue: <_i4.NodeModel>[], ) as List<_i4.NodeModel>); + @override _i5.Future add( _i4.NodeModel? node, @@ -149,6 +159,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future delete( String? id, @@ -165,6 +176,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future setEnabledState( String? id, @@ -183,6 +195,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future edit( _i4.NodeModel? editedNode, @@ -201,6 +214,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future updateCommunityNodes() => (super.noSuchMethod( Invocation.method( @@ -210,6 +224,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override void addListener(_i7.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -218,6 +233,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i7.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -226,6 +242,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -234,6 +251,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( 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 f1948fa9a..fb159cea3 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 @@ -54,21 +54,25 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { Invocation.getter(#secureStorageInterface), ), ) as _i2.SecureStorageInterface); + @override List<_i4.NodeModel> get primaryNodes => (super.noSuchMethod( Invocation.getter(#primaryNodes), returnValue: <_i4.NodeModel>[], ) as List<_i4.NodeModel>); + @override List<_i4.NodeModel> get nodes => (super.noSuchMethod( Invocation.getter(#nodes), returnValue: <_i4.NodeModel>[], ) as List<_i4.NodeModel>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i5.Future updateDefaults() => (super.noSuchMethod( Invocation.method( @@ -78,6 +82,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future setPrimaryNodeFor({ required _i6.CryptoCurrency? coin, @@ -97,6 +102,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i4.NodeModel? getPrimaryNodeFor({required _i6.CryptoCurrency? currency}) => (super.noSuchMethod(Invocation.method( @@ -104,6 +110,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { [], {#currency: currency}, )) as _i4.NodeModel?); + @override List<_i4.NodeModel> getNodesFor(_i6.CryptoCurrency? coin) => (super.noSuchMethod( @@ -113,6 +120,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValue: <_i4.NodeModel>[], ) as List<_i4.NodeModel>); + @override _i4.NodeModel? getNodeById({required String? id}) => (super.noSuchMethod(Invocation.method( @@ -120,6 +128,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { [], {#id: id}, )) as _i4.NodeModel?); + @override List<_i4.NodeModel> failoverNodesFor( {required _i6.CryptoCurrency? currency}) => @@ -131,6 +140,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValue: <_i4.NodeModel>[], ) as List<_i4.NodeModel>); + @override _i5.Future add( _i4.NodeModel? node, @@ -149,6 +159,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future delete( String? id, @@ -165,6 +176,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future setEnabledState( String? id, @@ -183,6 +195,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future edit( _i4.NodeModel? editedNode, @@ -201,6 +214,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future updateCommunityNodes() => (super.noSuchMethod( Invocation.method( @@ -210,6 +224,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override void addListener(_i7.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -218,6 +233,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i7.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -226,6 +242,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -234,6 +251,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( 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 4adf647f0..c60df280d 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 @@ -54,21 +54,25 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { Invocation.getter(#secureStorageInterface), ), ) as _i2.SecureStorageInterface); + @override List<_i4.NodeModel> get primaryNodes => (super.noSuchMethod( Invocation.getter(#primaryNodes), returnValue: <_i4.NodeModel>[], ) as List<_i4.NodeModel>); + @override List<_i4.NodeModel> get nodes => (super.noSuchMethod( Invocation.getter(#nodes), returnValue: <_i4.NodeModel>[], ) as List<_i4.NodeModel>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i5.Future updateDefaults() => (super.noSuchMethod( Invocation.method( @@ -78,6 +82,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future setPrimaryNodeFor({ required _i6.CryptoCurrency? coin, @@ -97,6 +102,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i4.NodeModel? getPrimaryNodeFor({required _i6.CryptoCurrency? currency}) => (super.noSuchMethod(Invocation.method( @@ -104,6 +110,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { [], {#currency: currency}, )) as _i4.NodeModel?); + @override List<_i4.NodeModel> getNodesFor(_i6.CryptoCurrency? coin) => (super.noSuchMethod( @@ -113,6 +120,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValue: <_i4.NodeModel>[], ) as List<_i4.NodeModel>); + @override _i4.NodeModel? getNodeById({required String? id}) => (super.noSuchMethod(Invocation.method( @@ -120,6 +128,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { [], {#id: id}, )) as _i4.NodeModel?); + @override List<_i4.NodeModel> failoverNodesFor( {required _i6.CryptoCurrency? currency}) => @@ -131,6 +140,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValue: <_i4.NodeModel>[], ) as List<_i4.NodeModel>); + @override _i5.Future add( _i4.NodeModel? node, @@ -149,6 +159,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future delete( String? id, @@ -165,6 +176,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future setEnabledState( String? id, @@ -183,6 +195,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future edit( _i4.NodeModel? editedNode, @@ -201,6 +214,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future updateCommunityNodes() => (super.noSuchMethod( Invocation.method( @@ -210,6 +224,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override void addListener(_i7.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -218,6 +233,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i7.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -226,6 +242,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -234,6 +251,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/change_pin_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/change_pin_view_screen_test.mocks.dart index b9014e5fe..93c8ef6ec 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/change_pin_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/change_pin_view_screen_test.mocks.dart @@ -37,11 +37,13 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { returnValue: _i3.Future>.value( {}), ) as _i3.Future>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override void addListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -50,6 +52,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -58,6 +61,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -66,6 +70,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rename_wallet_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rename_wallet_view_screen_test.mocks.dart index e35762cce..2803819b6 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rename_wallet_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rename_wallet_view_screen_test.mocks.dart @@ -37,11 +37,13 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { returnValue: _i3.Future>.value( {}), ) as _i3.Future>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override void addListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -50,6 +52,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -58,6 +61,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -66,6 +70,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart index 7855c17d8..63ddd796c 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart @@ -37,11 +37,13 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { returnValue: _i3.Future>.value( {}), ) as _i3.Future>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override void addListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -50,6 +52,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -58,6 +61,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -66,6 +70,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( 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 e4c45b863..40a295640 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 @@ -4,16 +4,18 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i4; -import 'dart:ui' as _i11; +import 'dart:ui' as _i13; -import 'package:local_auth/auth_strings.dart' as _i8; import 'package:local_auth/local_auth.dart' as _i7; +import 'package:local_auth_android/local_auth_android.dart' as _i8; +import 'package:local_auth_darwin/local_auth_darwin.dart' as _i9; +import 'package:local_auth_windows/local_auth_windows.dart' as _i10; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i6; import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart' as _i3; import 'package:stackwallet/electrumx_rpc/electrumx_client.dart' as _i2; -import 'package:stackwallet/services/wallets_service.dart' as _i10; -import 'package:stackwallet/utilities/biometrics.dart' as _i9; +import 'package:stackwallet/services/wallets_service.dart' as _i12; +import 'package:stackwallet/utilities/biometrics.dart' as _i11; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart' as _i5; @@ -58,6 +60,7 @@ class MockCachedElectrumXClient extends _i1.Mock Invocation.getter(#electrumXClient), ), ) as _i2.ElectrumXClient); + @override _i4.Future> getAnonymitySet({ required String? groupId, @@ -77,6 +80,7 @@ class MockCachedElectrumXClient extends _i1.Mock returnValue: _i4.Future>.value({}), ) as _i4.Future>); + @override String base64ToHex(String? source) => (super.noSuchMethod( Invocation.method( @@ -91,6 +95,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), ), ) as String); + @override String base64ToReverseHex(String? source) => (super.noSuchMethod( Invocation.method( @@ -105,6 +110,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), ), ) as String); + @override _i4.Future> getTransaction({ required String? txHash, @@ -124,6 +130,7 @@ class MockCachedElectrumXClient extends _i1.Mock returnValue: _i4.Future>.value({}), ) as _i4.Future>); + @override _i4.Future> getUsedCoinSerials({ required _i5.CryptoCurrency? cryptoCurrency, @@ -140,6 +147,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), returnValue: _i4.Future>.value([]), ) as _i4.Future>); + @override _i4.Future clearSharedTransactionCache( {required _i5.CryptoCurrency? cryptoCurrency}) => @@ -168,41 +176,16 @@ class MockLocalAuthentication extends _i1.Mock Invocation.getter(#canCheckBiometrics), returnValue: _i4.Future.value(false), ) as _i4.Future); - @override - _i4.Future authenticateWithBiometrics({ - required String? localizedReason, - bool? useErrorDialogs = true, - bool? stickyAuth = false, - _i8.AndroidAuthMessages? androidAuthStrings = - const _i8.AndroidAuthMessages(), - _i8.IOSAuthMessages? iOSAuthStrings = const _i8.IOSAuthMessages(), - bool? sensitiveTransaction = true, - }) => - (super.noSuchMethod( - Invocation.method( - #authenticateWithBiometrics, - [], - { - #localizedReason: localizedReason, - #useErrorDialogs: useErrorDialogs, - #stickyAuth: stickyAuth, - #androidAuthStrings: androidAuthStrings, - #iOSAuthStrings: iOSAuthStrings, - #sensitiveTransaction: sensitiveTransaction, - }, - ), - returnValue: _i4.Future.value(false), - ) as _i4.Future); + @override _i4.Future authenticate({ required String? localizedReason, - bool? useErrorDialogs = true, - bool? stickyAuth = false, - _i8.AndroidAuthMessages? androidAuthStrings = - const _i8.AndroidAuthMessages(), - _i8.IOSAuthMessages? iOSAuthStrings = const _i8.IOSAuthMessages(), - bool? sensitiveTransaction = true, - bool? biometricOnly = false, + Iterable<_i8.AuthMessages>? authMessages = const [ + _i9.IOSAuthMessages(), + _i8.AndroidAuthMessages(), + _i10.WindowsAuthMessages(), + ], + _i8.AuthenticationOptions? options = const _i8.AuthenticationOptions(), }) => (super.noSuchMethod( Invocation.method( @@ -210,16 +193,13 @@ class MockLocalAuthentication extends _i1.Mock [], { #localizedReason: localizedReason, - #useErrorDialogs: useErrorDialogs, - #stickyAuth: stickyAuth, - #androidAuthStrings: androidAuthStrings, - #iOSAuthStrings: iOSAuthStrings, - #sensitiveTransaction: sensitiveTransaction, - #biometricOnly: biometricOnly, + #authMessages: authMessages, + #options: options, }, ), returnValue: _i4.Future.value(false), ) as _i4.Future); + @override _i4.Future stopAuthentication() => (super.noSuchMethod( Invocation.method( @@ -228,6 +208,7 @@ class MockLocalAuthentication extends _i1.Mock ), returnValue: _i4.Future.value(false), ) as _i4.Future); + @override _i4.Future isDeviceSupported() => (super.noSuchMethod( Invocation.method( @@ -236,22 +217,23 @@ class MockLocalAuthentication extends _i1.Mock ), returnValue: _i4.Future.value(false), ) as _i4.Future); + @override - _i4.Future> getAvailableBiometrics() => + _i4.Future> getAvailableBiometrics() => (super.noSuchMethod( Invocation.method( #getAvailableBiometrics, [], ), returnValue: - _i4.Future>.value(<_i7.BiometricType>[]), - ) as _i4.Future>); + _i4.Future>.value(<_i8.BiometricType>[]), + ) as _i4.Future>); } /// A class which mocks [Biometrics]. /// /// See the documentation for Mockito's code generation for more information. -class MockBiometrics extends _i1.Mock implements _i9.Biometrics { +class MockBiometrics extends _i1.Mock implements _i11.Biometrics { MockBiometrics() { _i1.throwOnMissingStub(this); } @@ -279,39 +261,43 @@ class MockBiometrics extends _i1.Mock implements _i9.Biometrics { /// A class which mocks [WalletsService]. /// /// See the documentation for Mockito's code generation for more information. -class MockWalletsService extends _i1.Mock implements _i10.WalletsService { +class MockWalletsService extends _i1.Mock implements _i12.WalletsService { MockWalletsService() { _i1.throwOnMissingStub(this); } @override - _i4.Future> get walletNames => + _i4.Future> get walletNames => (super.noSuchMethod( Invocation.getter(#walletNames), - returnValue: _i4.Future>.value( - {}), - ) as _i4.Future>); + returnValue: _i4.Future>.value( + {}), + ) as _i4.Future>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override - void addListener(_i11.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i13.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], ), returnValueForMissingStub: null, ); + @override - void removeListener(_i11.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i13.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -320,6 +306,7 @@ class MockWalletsService extends _i1.Mock implements _i10.WalletsService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart index 21c907eb7..2f8346004 100644 --- a/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart @@ -37,11 +37,13 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { returnValue: _i3.Future>.value( {}), ) as _i3.Future>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override void addListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -50,6 +52,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i4.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -58,6 +61,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -66,6 +70,7 @@ class MockWalletsService extends _i1.Mock implements _i2.WalletsService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.mocks.dart b/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.mocks.dart index 7c8355a96..531417e9d 100644 --- a/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.mocks.dart +++ b/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.mocks.dart @@ -49,11 +49,13 @@ class MockAddressBookService extends _i1.Mock Invocation.getter(#contacts), returnValue: <_i2.ContactEntry>[], ) as List<_i2.ContactEntry>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i2.ContactEntry getContactById(String? id) => (super.noSuchMethod( Invocation.method( @@ -68,6 +70,7 @@ class MockAddressBookService extends _i1.Mock ), ), ) as _i2.ContactEntry); + @override _i4.Future> search(String? text) => (super.noSuchMethod( @@ -78,6 +81,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i4.Future>.value(<_i2.ContactEntry>[]), ) as _i4.Future>); + @override bool matches( String? term, @@ -93,6 +97,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: false, ) as bool); + @override _i4.Future addContact(_i2.ContactEntry? contact) => (super.noSuchMethod( Invocation.method( @@ -101,6 +106,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: _i4.Future.value(false), ) as _i4.Future); + @override _i4.Future editContact(_i2.ContactEntry? editedContact) => (super.noSuchMethod( @@ -110,6 +116,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: _i4.Future.value(false), ) as _i4.Future); + @override _i4.Future removeContact(String? id) => (super.noSuchMethod( Invocation.method( @@ -119,6 +126,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override void addListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -127,6 +135,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -135,6 +144,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -143,6 +153,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -169,11 +180,13 @@ class MockLocaleService extends _i1.Mock implements _i6.LocaleService { Invocation.getter(#locale), ), ) as String); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i4.Future loadLocale({bool? notify = true}) => (super.noSuchMethod( Invocation.method( @@ -184,6 +197,7 @@ class MockLocaleService extends _i1.Mock implements _i6.LocaleService { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override void addListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -192,6 +206,7 @@ class MockLocaleService extends _i1.Mock implements _i6.LocaleService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -200,6 +215,7 @@ class MockLocaleService extends _i1.Mock implements _i6.LocaleService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -208,6 +224,7 @@ class MockLocaleService extends _i1.Mock implements _i6.LocaleService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart b/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart index c4aad0a14..ff1557434 100644 --- a/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart +++ b/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart @@ -39,11 +39,13 @@ class MockLocaleService extends _i1.Mock implements _i2.LocaleService { Invocation.getter(#locale), ), ) as String); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i4.Future loadLocale({bool? notify = true}) => (super.noSuchMethod( Invocation.method( @@ -54,6 +56,7 @@ class MockLocaleService extends _i1.Mock implements _i2.LocaleService { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override void addListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -62,6 +65,7 @@ class MockLocaleService extends _i1.Mock implements _i2.LocaleService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -70,6 +74,7 @@ class MockLocaleService extends _i1.Mock implements _i2.LocaleService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -78,6 +83,7 @@ class MockLocaleService extends _i1.Mock implements _i2.LocaleService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.mocks.dart b/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.mocks.dart index 6fd708af7..a236a0b41 100644 --- a/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.mocks.dart +++ b/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.mocks.dart @@ -47,11 +47,13 @@ class MockAddressBookService extends _i1.Mock Invocation.getter(#contacts), returnValue: <_i2.ContactEntry>[], ) as List<_i2.ContactEntry>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i2.ContactEntry getContactById(String? id) => (super.noSuchMethod( Invocation.method( @@ -66,6 +68,7 @@ class MockAddressBookService extends _i1.Mock ), ), ) as _i2.ContactEntry); + @override _i4.Future> search(String? text) => (super.noSuchMethod( @@ -76,6 +79,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i4.Future>.value(<_i2.ContactEntry>[]), ) as _i4.Future>); + @override bool matches( String? term, @@ -91,6 +95,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: false, ) as bool); + @override _i4.Future addContact(_i2.ContactEntry? contact) => (super.noSuchMethod( Invocation.method( @@ -99,6 +104,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: _i4.Future.value(false), ) as _i4.Future); + @override _i4.Future editContact(_i2.ContactEntry? editedContact) => (super.noSuchMethod( @@ -108,6 +114,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: _i4.Future.value(false), ) as _i4.Future); + @override _i4.Future removeContact(String? id) => (super.noSuchMethod( Invocation.method( @@ -117,6 +124,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override void addListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -125,6 +133,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -133,6 +142,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -141,6 +151,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart b/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart index b85201783..cf011b7c6 100644 --- a/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart +++ b/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart @@ -39,11 +39,13 @@ class MockLocaleService extends _i1.Mock implements _i2.LocaleService { Invocation.getter(#locale), ), ) as String); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i4.Future loadLocale({bool? notify = true}) => (super.noSuchMethod( Invocation.method( @@ -54,6 +56,7 @@ class MockLocaleService extends _i1.Mock implements _i2.LocaleService { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override void addListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -62,6 +65,7 @@ class MockLocaleService extends _i1.Mock implements _i2.LocaleService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -70,6 +74,7 @@ class MockLocaleService extends _i1.Mock implements _i2.LocaleService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -78,6 +83,7 @@ class MockLocaleService extends _i1.Mock implements _i2.LocaleService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/services/change_now/change_now_test.mocks.dart b/test/services/change_now/change_now_test.mocks.dart index 1ee496d32..8134b6449 100644 --- a/test/services/change_now/change_now_test.mocks.dart +++ b/test/services/change_now/change_now_test.mocks.dart @@ -70,6 +70,7 @@ class MockHTTP extends _i1.Mock implements _i2.HTTP { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> post({ required Uri? url, diff --git a/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart b/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart index 078584849..e38af7ce9 100644 --- a/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart +++ b/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart @@ -86,6 +86,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { Invocation.getter(#cryptoCurrency), ), ) as _i2.CryptoCurrency); + @override set failovers(List<_i4.ElectrumXNode>? _failovers) => super.noSuchMethod( Invocation.setter( @@ -94,11 +95,13 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValueForMissingStub: null, ); + @override int get currentFailoverIndex => (super.noSuchMethod( Invocation.getter(#currentFailoverIndex), returnValue: 0, ) as int); + @override set currentFailoverIndex(int? _currentFailoverIndex) => super.noSuchMethod( Invocation.setter( @@ -107,6 +110,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValueForMissingStub: null, ); + @override Duration get connectionTimeoutForSpecialCaseJsonRPCClients => (super.noSuchMethod( @@ -116,6 +120,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), ), ) as Duration); + @override String get host => (super.noSuchMethod( Invocation.getter(#host), @@ -124,16 +129,19 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { Invocation.getter(#host), ), ) as String); + @override int get port => (super.noSuchMethod( Invocation.getter(#port), returnValue: 0, ) as int); + @override bool get useSSL => (super.noSuchMethod( Invocation.getter(#useSSL), returnValue: false, ) as bool); + @override _i6.Future closeAdapter() => (super.noSuchMethod( Invocation.method( @@ -143,6 +151,17 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + + @override + _i6.Future checkElectrumAdapter() => (super.noSuchMethod( + Invocation.method( + #checkElectrumAdapter, + [], + ), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); + @override _i6.Future request({ required String? command, @@ -165,6 +184,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future> batchRequest({ required String? command, @@ -185,6 +205,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future>.value([]), ) as _i6.Future>); + @override _i6.Future ping({ String? requestID, @@ -201,6 +222,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(false), ) as _i6.Future); + @override _i6.Future> getBlockHeadTip({String? requestID}) => (super.noSuchMethod( @@ -212,6 +234,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future> getServerFeatures({String? requestID}) => (super.noSuchMethod( @@ -223,6 +246,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future broadcastTransaction({ required String? rawTx, @@ -249,6 +273,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), )), ) as _i6.Future); + @override _i6.Future> getBalance({ required String? scripthash, @@ -266,6 +291,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future>> getHistory({ required String? scripthash, @@ -283,6 +309,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>.value( >[]), ) as _i6.Future>>); + @override _i6.Future>>> getBatchHistory( {required List? args}) => @@ -295,6 +322,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>>.value( >>[]), ) as _i6.Future>>>); + @override _i6.Future>> getUTXOs({ required String? scripthash, @@ -312,6 +340,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>.value( >[]), ) as _i6.Future>>); + @override _i6.Future>>> getBatchUTXOs( {required List? args}) => @@ -324,6 +353,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>>.value( >>[]), ) as _i6.Future>>>); + @override _i6.Future> getTransaction({ required String? txHash, @@ -343,6 +373,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future> getLelantusAnonymitySet({ String? groupId = r'1', @@ -362,6 +393,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future getLelantusMintData({ dynamic mints, @@ -378,6 +410,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future> getLelantusUsedCoinSerials({ String? requestID, @@ -395,6 +428,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future getLelantusLatestCoinId({String? requestID}) => (super.noSuchMethod( @@ -405,6 +439,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(0), ) as _i6.Future); + @override _i6.Future> getSparkAnonymitySet({ String? coinGroupId = r'1', @@ -424,6 +459,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future>> getSparkMintMetaData({ String? requestID, @@ -441,6 +477,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>.value( >[]), ) as _i6.Future>>); + @override _i6.Future getSparkLatestCoinId({String? requestID}) => (super.noSuchMethod( @@ -451,6 +488,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(0), ) as _i6.Future); + @override _i6.Future> getMempoolTxids({String? requestID}) => (super.noSuchMethod( @@ -461,8 +499,16 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override - _i6.Future> getMempoolSparkData({ + _i6.Future< + List< + ({ + List coins, + List lTags, + List serialContext, + String txid + })>> getMempoolSparkData({ String? requestID, required List? txids, }) => @@ -475,9 +521,28 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #txids: txids, }, ), - returnValue: - _i6.Future>.value({}), - ) as _i6.Future>); + returnValue: _i6.Future< + List< + ({ + List coins, + List lTags, + List serialContext, + String txid + })>>.value(<({ + List coins, + List lTags, + List serialContext, + String txid + })>[]), + ) as _i6.Future< + List< + ({ + List coins, + List lTags, + List serialContext, + String txid + })>>); + @override _i6.Future>> getSparkUnhashedUsedCoinsTagsWithTxHashes({ String? requestID, @@ -494,6 +559,26 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future>>.value(>[]), ) as _i6.Future>>); + + @override + _i6.Future isMasterNodeCollateral({ + String? requestID, + required String? txid, + required int? index, + }) => + (super.noSuchMethod( + Invocation.method( + #isMasterNodeCollateral, + [], + { + #requestID: requestID, + #txid: txid, + #index: index, + }, + ), + returnValue: _i6.Future.value(false), + ) as _i6.Future); + @override _i6.Future> getFeeRate({String? requestID}) => (super.noSuchMethod( @@ -505,6 +590,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future<_i3.Decimal> estimateFee({ String? requestID, @@ -531,6 +617,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), )), ) as _i6.Future<_i3.Decimal>); + @override _i6.Future<_i3.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( Invocation.method( @@ -566,6 +653,7 @@ class MockCachedElectrumXClient extends _i1.Mock Invocation.getter(#electrumXClient), ), ) as _i4.ElectrumXClient); + @override _i6.Future> getAnonymitySet({ required String? groupId, @@ -585,6 +673,7 @@ class MockCachedElectrumXClient extends _i1.Mock returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override String base64ToHex(String? source) => (super.noSuchMethod( Invocation.method( @@ -599,6 +688,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), ), ) as String); + @override String base64ToReverseHex(String? source) => (super.noSuchMethod( Invocation.method( @@ -613,6 +703,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), ), ) as String); + @override _i6.Future> getTransaction({ required String? txHash, @@ -632,6 +723,7 @@ class MockCachedElectrumXClient extends _i1.Mock returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future> getUsedCoinSerials({ required _i2.CryptoCurrency? cryptoCurrency, @@ -648,6 +740,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), returnValue: _i6.Future>.value([]), ) as _i6.Future>); + @override _i6.Future clearSharedTransactionCache( {required _i2.CryptoCurrency? cryptoCurrency}) => @@ -679,16 +772,19 @@ class MockTransactionNotificationTracker extends _i1.Mock Invocation.getter(#walletId), ), ) as String); + @override List get pendings => (super.noSuchMethod( Invocation.getter(#pendings), returnValue: [], ) as List); + @override List get confirmeds => (super.noSuchMethod( Invocation.getter(#confirmeds), returnValue: [], ) as List); + @override bool wasNotifiedPending(String? txid) => (super.noSuchMethod( Invocation.method( @@ -697,6 +793,7 @@ class MockTransactionNotificationTracker extends _i1.Mock ), returnValue: false, ) as bool); + @override _i6.Future addNotifiedPending(String? txid) => (super.noSuchMethod( Invocation.method( @@ -706,6 +803,7 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override bool wasNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( @@ -714,6 +812,7 @@ class MockTransactionNotificationTracker extends _i1.Mock ), returnValue: false, ) as bool); + @override _i6.Future addNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( @@ -723,6 +822,7 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future deleteTransaction(String? txid) => (super.noSuchMethod( Invocation.method( diff --git a/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart b/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart index 25ae15056..edbb3c632 100644 --- a/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart +++ b/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart @@ -86,6 +86,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { Invocation.getter(#cryptoCurrency), ), ) as _i2.CryptoCurrency); + @override set failovers(List<_i4.ElectrumXNode>? _failovers) => super.noSuchMethod( Invocation.setter( @@ -94,11 +95,13 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValueForMissingStub: null, ); + @override int get currentFailoverIndex => (super.noSuchMethod( Invocation.getter(#currentFailoverIndex), returnValue: 0, ) as int); + @override set currentFailoverIndex(int? _currentFailoverIndex) => super.noSuchMethod( Invocation.setter( @@ -107,6 +110,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValueForMissingStub: null, ); + @override Duration get connectionTimeoutForSpecialCaseJsonRPCClients => (super.noSuchMethod( @@ -116,6 +120,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), ), ) as Duration); + @override String get host => (super.noSuchMethod( Invocation.getter(#host), @@ -124,16 +129,19 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { Invocation.getter(#host), ), ) as String); + @override int get port => (super.noSuchMethod( Invocation.getter(#port), returnValue: 0, ) as int); + @override bool get useSSL => (super.noSuchMethod( Invocation.getter(#useSSL), returnValue: false, ) as bool); + @override _i6.Future closeAdapter() => (super.noSuchMethod( Invocation.method( @@ -143,6 +151,17 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + + @override + _i6.Future checkElectrumAdapter() => (super.noSuchMethod( + Invocation.method( + #checkElectrumAdapter, + [], + ), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); + @override _i6.Future request({ required String? command, @@ -165,6 +184,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future> batchRequest({ required String? command, @@ -185,6 +205,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future>.value([]), ) as _i6.Future>); + @override _i6.Future ping({ String? requestID, @@ -201,6 +222,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(false), ) as _i6.Future); + @override _i6.Future> getBlockHeadTip({String? requestID}) => (super.noSuchMethod( @@ -212,6 +234,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future> getServerFeatures({String? requestID}) => (super.noSuchMethod( @@ -223,6 +246,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future broadcastTransaction({ required String? rawTx, @@ -249,6 +273,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), )), ) as _i6.Future); + @override _i6.Future> getBalance({ required String? scripthash, @@ -266,6 +291,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future>> getHistory({ required String? scripthash, @@ -283,6 +309,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>.value( >[]), ) as _i6.Future>>); + @override _i6.Future>>> getBatchHistory( {required List? args}) => @@ -295,6 +322,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>>.value( >>[]), ) as _i6.Future>>>); + @override _i6.Future>> getUTXOs({ required String? scripthash, @@ -312,6 +340,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>.value( >[]), ) as _i6.Future>>); + @override _i6.Future>>> getBatchUTXOs( {required List? args}) => @@ -324,6 +353,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>>.value( >>[]), ) as _i6.Future>>>); + @override _i6.Future> getTransaction({ required String? txHash, @@ -343,6 +373,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future> getLelantusAnonymitySet({ String? groupId = r'1', @@ -362,6 +393,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future getLelantusMintData({ dynamic mints, @@ -378,6 +410,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future> getLelantusUsedCoinSerials({ String? requestID, @@ -395,6 +428,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future getLelantusLatestCoinId({String? requestID}) => (super.noSuchMethod( @@ -405,6 +439,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(0), ) as _i6.Future); + @override _i6.Future> getSparkAnonymitySet({ String? coinGroupId = r'1', @@ -424,6 +459,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future>> getSparkMintMetaData({ String? requestID, @@ -441,6 +477,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>.value( >[]), ) as _i6.Future>>); + @override _i6.Future getSparkLatestCoinId({String? requestID}) => (super.noSuchMethod( @@ -451,6 +488,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(0), ) as _i6.Future); + @override _i6.Future> getMempoolTxids({String? requestID}) => (super.noSuchMethod( @@ -461,8 +499,16 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override - _i6.Future> getMempoolSparkData({ + _i6.Future< + List< + ({ + List coins, + List lTags, + List serialContext, + String txid + })>> getMempoolSparkData({ String? requestID, required List? txids, }) => @@ -475,9 +521,28 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #txids: txids, }, ), - returnValue: - _i6.Future>.value({}), - ) as _i6.Future>); + returnValue: _i6.Future< + List< + ({ + List coins, + List lTags, + List serialContext, + String txid + })>>.value(<({ + List coins, + List lTags, + List serialContext, + String txid + })>[]), + ) as _i6.Future< + List< + ({ + List coins, + List lTags, + List serialContext, + String txid + })>>); + @override _i6.Future>> getSparkUnhashedUsedCoinsTagsWithTxHashes({ String? requestID, @@ -494,6 +559,26 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future>>.value(>[]), ) as _i6.Future>>); + + @override + _i6.Future isMasterNodeCollateral({ + String? requestID, + required String? txid, + required int? index, + }) => + (super.noSuchMethod( + Invocation.method( + #isMasterNodeCollateral, + [], + { + #requestID: requestID, + #txid: txid, + #index: index, + }, + ), + returnValue: _i6.Future.value(false), + ) as _i6.Future); + @override _i6.Future> getFeeRate({String? requestID}) => (super.noSuchMethod( @@ -505,6 +590,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future<_i3.Decimal> estimateFee({ String? requestID, @@ -531,6 +617,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), )), ) as _i6.Future<_i3.Decimal>); + @override _i6.Future<_i3.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( Invocation.method( @@ -566,6 +653,7 @@ class MockCachedElectrumXClient extends _i1.Mock Invocation.getter(#electrumXClient), ), ) as _i4.ElectrumXClient); + @override _i6.Future> getAnonymitySet({ required String? groupId, @@ -585,6 +673,7 @@ class MockCachedElectrumXClient extends _i1.Mock returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override String base64ToHex(String? source) => (super.noSuchMethod( Invocation.method( @@ -599,6 +688,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), ), ) as String); + @override String base64ToReverseHex(String? source) => (super.noSuchMethod( Invocation.method( @@ -613,6 +703,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), ), ) as String); + @override _i6.Future> getTransaction({ required String? txHash, @@ -632,6 +723,7 @@ class MockCachedElectrumXClient extends _i1.Mock returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future> getUsedCoinSerials({ required _i2.CryptoCurrency? cryptoCurrency, @@ -648,6 +740,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), returnValue: _i6.Future>.value([]), ) as _i6.Future>); + @override _i6.Future clearSharedTransactionCache( {required _i2.CryptoCurrency? cryptoCurrency}) => @@ -679,16 +772,19 @@ class MockTransactionNotificationTracker extends _i1.Mock Invocation.getter(#walletId), ), ) as String); + @override List get pendings => (super.noSuchMethod( Invocation.getter(#pendings), returnValue: [], ) as List); + @override List get confirmeds => (super.noSuchMethod( Invocation.getter(#confirmeds), returnValue: [], ) as List); + @override bool wasNotifiedPending(String? txid) => (super.noSuchMethod( Invocation.method( @@ -697,6 +793,7 @@ class MockTransactionNotificationTracker extends _i1.Mock ), returnValue: false, ) as bool); + @override _i6.Future addNotifiedPending(String? txid) => (super.noSuchMethod( Invocation.method( @@ -706,6 +803,7 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override bool wasNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( @@ -714,6 +812,7 @@ class MockTransactionNotificationTracker extends _i1.Mock ), returnValue: false, ) as bool); + @override _i6.Future addNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( @@ -723,6 +822,7 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future deleteTransaction(String? txid) => (super.noSuchMethod( Invocation.method( diff --git a/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart b/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart index 24b7bd226..c5c167366 100644 --- a/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart +++ b/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart @@ -86,6 +86,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { Invocation.getter(#cryptoCurrency), ), ) as _i2.CryptoCurrency); + @override set failovers(List<_i4.ElectrumXNode>? _failovers) => super.noSuchMethod( Invocation.setter( @@ -94,11 +95,13 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValueForMissingStub: null, ); + @override int get currentFailoverIndex => (super.noSuchMethod( Invocation.getter(#currentFailoverIndex), returnValue: 0, ) as int); + @override set currentFailoverIndex(int? _currentFailoverIndex) => super.noSuchMethod( Invocation.setter( @@ -107,6 +110,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValueForMissingStub: null, ); + @override Duration get connectionTimeoutForSpecialCaseJsonRPCClients => (super.noSuchMethod( @@ -116,6 +120,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), ), ) as Duration); + @override String get host => (super.noSuchMethod( Invocation.getter(#host), @@ -124,16 +129,19 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { Invocation.getter(#host), ), ) as String); + @override int get port => (super.noSuchMethod( Invocation.getter(#port), returnValue: 0, ) as int); + @override bool get useSSL => (super.noSuchMethod( Invocation.getter(#useSSL), returnValue: false, ) as bool); + @override _i6.Future closeAdapter() => (super.noSuchMethod( Invocation.method( @@ -143,6 +151,17 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + + @override + _i6.Future checkElectrumAdapter() => (super.noSuchMethod( + Invocation.method( + #checkElectrumAdapter, + [], + ), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); + @override _i6.Future request({ required String? command, @@ -165,6 +184,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future> batchRequest({ required String? command, @@ -185,6 +205,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future>.value([]), ) as _i6.Future>); + @override _i6.Future ping({ String? requestID, @@ -201,6 +222,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(false), ) as _i6.Future); + @override _i6.Future> getBlockHeadTip({String? requestID}) => (super.noSuchMethod( @@ -212,6 +234,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future> getServerFeatures({String? requestID}) => (super.noSuchMethod( @@ -223,6 +246,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future broadcastTransaction({ required String? rawTx, @@ -249,6 +273,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), )), ) as _i6.Future); + @override _i6.Future> getBalance({ required String? scripthash, @@ -266,6 +291,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future>> getHistory({ required String? scripthash, @@ -283,6 +309,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>.value( >[]), ) as _i6.Future>>); + @override _i6.Future>>> getBatchHistory( {required List? args}) => @@ -295,6 +322,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>>.value( >>[]), ) as _i6.Future>>>); + @override _i6.Future>> getUTXOs({ required String? scripthash, @@ -312,6 +340,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>.value( >[]), ) as _i6.Future>>); + @override _i6.Future>>> getBatchUTXOs( {required List? args}) => @@ -324,6 +353,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>>.value( >>[]), ) as _i6.Future>>>); + @override _i6.Future> getTransaction({ required String? txHash, @@ -343,6 +373,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future> getLelantusAnonymitySet({ String? groupId = r'1', @@ -362,6 +393,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future getLelantusMintData({ dynamic mints, @@ -378,6 +410,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future> getLelantusUsedCoinSerials({ String? requestID, @@ -395,6 +428,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future getLelantusLatestCoinId({String? requestID}) => (super.noSuchMethod( @@ -405,6 +439,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(0), ) as _i6.Future); + @override _i6.Future> getSparkAnonymitySet({ String? coinGroupId = r'1', @@ -424,6 +459,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future>> getSparkMintMetaData({ String? requestID, @@ -441,6 +477,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>.value( >[]), ) as _i6.Future>>); + @override _i6.Future getSparkLatestCoinId({String? requestID}) => (super.noSuchMethod( @@ -451,6 +488,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(0), ) as _i6.Future); + @override _i6.Future> getMempoolTxids({String? requestID}) => (super.noSuchMethod( @@ -461,8 +499,16 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override - _i6.Future> getMempoolSparkData({ + _i6.Future< + List< + ({ + List coins, + List lTags, + List serialContext, + String txid + })>> getMempoolSparkData({ String? requestID, required List? txids, }) => @@ -475,9 +521,28 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #txids: txids, }, ), - returnValue: - _i6.Future>.value({}), - ) as _i6.Future>); + returnValue: _i6.Future< + List< + ({ + List coins, + List lTags, + List serialContext, + String txid + })>>.value(<({ + List coins, + List lTags, + List serialContext, + String txid + })>[]), + ) as _i6.Future< + List< + ({ + List coins, + List lTags, + List serialContext, + String txid + })>>); + @override _i6.Future>> getSparkUnhashedUsedCoinsTagsWithTxHashes({ String? requestID, @@ -494,6 +559,26 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future>>.value(>[]), ) as _i6.Future>>); + + @override + _i6.Future isMasterNodeCollateral({ + String? requestID, + required String? txid, + required int? index, + }) => + (super.noSuchMethod( + Invocation.method( + #isMasterNodeCollateral, + [], + { + #requestID: requestID, + #txid: txid, + #index: index, + }, + ), + returnValue: _i6.Future.value(false), + ) as _i6.Future); + @override _i6.Future> getFeeRate({String? requestID}) => (super.noSuchMethod( @@ -505,6 +590,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future<_i3.Decimal> estimateFee({ String? requestID, @@ -531,6 +617,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), )), ) as _i6.Future<_i3.Decimal>); + @override _i6.Future<_i3.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( Invocation.method( @@ -566,6 +653,7 @@ class MockCachedElectrumXClient extends _i1.Mock Invocation.getter(#electrumXClient), ), ) as _i4.ElectrumXClient); + @override _i6.Future> getAnonymitySet({ required String? groupId, @@ -585,6 +673,7 @@ class MockCachedElectrumXClient extends _i1.Mock returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override String base64ToHex(String? source) => (super.noSuchMethod( Invocation.method( @@ -599,6 +688,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), ), ) as String); + @override String base64ToReverseHex(String? source) => (super.noSuchMethod( Invocation.method( @@ -613,6 +703,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), ), ) as String); + @override _i6.Future> getTransaction({ required String? txHash, @@ -632,6 +723,7 @@ class MockCachedElectrumXClient extends _i1.Mock returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future> getUsedCoinSerials({ required _i2.CryptoCurrency? cryptoCurrency, @@ -648,6 +740,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), returnValue: _i6.Future>.value([]), ) as _i6.Future>); + @override _i6.Future clearSharedTransactionCache( {required _i2.CryptoCurrency? cryptoCurrency}) => @@ -679,16 +772,19 @@ class MockTransactionNotificationTracker extends _i1.Mock Invocation.getter(#walletId), ), ) as String); + @override List get pendings => (super.noSuchMethod( Invocation.getter(#pendings), returnValue: [], ) as List); + @override List get confirmeds => (super.noSuchMethod( Invocation.getter(#confirmeds), returnValue: [], ) as List); + @override bool wasNotifiedPending(String? txid) => (super.noSuchMethod( Invocation.method( @@ -697,6 +793,7 @@ class MockTransactionNotificationTracker extends _i1.Mock ), returnValue: false, ) as bool); + @override _i6.Future addNotifiedPending(String? txid) => (super.noSuchMethod( Invocation.method( @@ -706,6 +803,7 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override bool wasNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( @@ -714,6 +812,7 @@ class MockTransactionNotificationTracker extends _i1.Mock ), returnValue: false, ) as bool); + @override _i6.Future addNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( @@ -723,6 +822,7 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future deleteTransaction(String? txid) => (super.noSuchMethod( Invocation.method( diff --git a/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart b/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart index e8927e3e7..80e8c8922 100644 --- a/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart +++ b/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart @@ -86,6 +86,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { Invocation.getter(#cryptoCurrency), ), ) as _i2.CryptoCurrency); + @override set failovers(List<_i4.ElectrumXNode>? _failovers) => super.noSuchMethod( Invocation.setter( @@ -94,11 +95,13 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValueForMissingStub: null, ); + @override int get currentFailoverIndex => (super.noSuchMethod( Invocation.getter(#currentFailoverIndex), returnValue: 0, ) as int); + @override set currentFailoverIndex(int? _currentFailoverIndex) => super.noSuchMethod( Invocation.setter( @@ -107,6 +110,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValueForMissingStub: null, ); + @override Duration get connectionTimeoutForSpecialCaseJsonRPCClients => (super.noSuchMethod( @@ -116,6 +120,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), ), ) as Duration); + @override String get host => (super.noSuchMethod( Invocation.getter(#host), @@ -124,16 +129,19 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { Invocation.getter(#host), ), ) as String); + @override int get port => (super.noSuchMethod( Invocation.getter(#port), returnValue: 0, ) as int); + @override bool get useSSL => (super.noSuchMethod( Invocation.getter(#useSSL), returnValue: false, ) as bool); + @override _i6.Future closeAdapter() => (super.noSuchMethod( Invocation.method( @@ -143,6 +151,17 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + + @override + _i6.Future checkElectrumAdapter() => (super.noSuchMethod( + Invocation.method( + #checkElectrumAdapter, + [], + ), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); + @override _i6.Future request({ required String? command, @@ -165,6 +184,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future> batchRequest({ required String? command, @@ -185,6 +205,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future>.value([]), ) as _i6.Future>); + @override _i6.Future ping({ String? requestID, @@ -201,6 +222,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(false), ) as _i6.Future); + @override _i6.Future> getBlockHeadTip({String? requestID}) => (super.noSuchMethod( @@ -212,6 +234,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future> getServerFeatures({String? requestID}) => (super.noSuchMethod( @@ -223,6 +246,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future broadcastTransaction({ required String? rawTx, @@ -249,6 +273,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), )), ) as _i6.Future); + @override _i6.Future> getBalance({ required String? scripthash, @@ -266,6 +291,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future>> getHistory({ required String? scripthash, @@ -283,6 +309,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>.value( >[]), ) as _i6.Future>>); + @override _i6.Future>>> getBatchHistory( {required List? args}) => @@ -295,6 +322,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>>.value( >>[]), ) as _i6.Future>>>); + @override _i6.Future>> getUTXOs({ required String? scripthash, @@ -312,6 +340,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>.value( >[]), ) as _i6.Future>>); + @override _i6.Future>>> getBatchUTXOs( {required List? args}) => @@ -324,6 +353,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>>.value( >>[]), ) as _i6.Future>>>); + @override _i6.Future> getTransaction({ required String? txHash, @@ -343,6 +373,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future> getLelantusAnonymitySet({ String? groupId = r'1', @@ -362,6 +393,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future getLelantusMintData({ dynamic mints, @@ -378,6 +410,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future> getLelantusUsedCoinSerials({ String? requestID, @@ -395,6 +428,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future getLelantusLatestCoinId({String? requestID}) => (super.noSuchMethod( @@ -405,6 +439,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(0), ) as _i6.Future); + @override _i6.Future> getSparkAnonymitySet({ String? coinGroupId = r'1', @@ -424,6 +459,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future>> getSparkMintMetaData({ String? requestID, @@ -441,6 +477,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>.value( >[]), ) as _i6.Future>>); + @override _i6.Future getSparkLatestCoinId({String? requestID}) => (super.noSuchMethod( @@ -451,6 +488,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(0), ) as _i6.Future); + @override _i6.Future> getMempoolTxids({String? requestID}) => (super.noSuchMethod( @@ -461,8 +499,16 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override - _i6.Future> getMempoolSparkData({ + _i6.Future< + List< + ({ + List coins, + List lTags, + List serialContext, + String txid + })>> getMempoolSparkData({ String? requestID, required List? txids, }) => @@ -475,9 +521,28 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #txids: txids, }, ), - returnValue: - _i6.Future>.value({}), - ) as _i6.Future>); + returnValue: _i6.Future< + List< + ({ + List coins, + List lTags, + List serialContext, + String txid + })>>.value(<({ + List coins, + List lTags, + List serialContext, + String txid + })>[]), + ) as _i6.Future< + List< + ({ + List coins, + List lTags, + List serialContext, + String txid + })>>); + @override _i6.Future>> getSparkUnhashedUsedCoinsTagsWithTxHashes({ String? requestID, @@ -494,6 +559,26 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future>>.value(>[]), ) as _i6.Future>>); + + @override + _i6.Future isMasterNodeCollateral({ + String? requestID, + required String? txid, + required int? index, + }) => + (super.noSuchMethod( + Invocation.method( + #isMasterNodeCollateral, + [], + { + #requestID: requestID, + #txid: txid, + #index: index, + }, + ), + returnValue: _i6.Future.value(false), + ) as _i6.Future); + @override _i6.Future> getFeeRate({String? requestID}) => (super.noSuchMethod( @@ -505,6 +590,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future<_i3.Decimal> estimateFee({ String? requestID, @@ -531,6 +617,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), )), ) as _i6.Future<_i3.Decimal>); + @override _i6.Future<_i3.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( Invocation.method( @@ -566,6 +653,7 @@ class MockCachedElectrumXClient extends _i1.Mock Invocation.getter(#electrumXClient), ), ) as _i4.ElectrumXClient); + @override _i6.Future> getAnonymitySet({ required String? groupId, @@ -585,6 +673,7 @@ class MockCachedElectrumXClient extends _i1.Mock returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override String base64ToHex(String? source) => (super.noSuchMethod( Invocation.method( @@ -599,6 +688,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), ), ) as String); + @override String base64ToReverseHex(String? source) => (super.noSuchMethod( Invocation.method( @@ -613,6 +703,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), ), ) as String); + @override _i6.Future> getTransaction({ required String? txHash, @@ -632,6 +723,7 @@ class MockCachedElectrumXClient extends _i1.Mock returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future> getUsedCoinSerials({ required _i2.CryptoCurrency? cryptoCurrency, @@ -648,6 +740,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), returnValue: _i6.Future>.value([]), ) as _i6.Future>); + @override _i6.Future clearSharedTransactionCache( {required _i2.CryptoCurrency? cryptoCurrency}) => @@ -679,16 +772,19 @@ class MockTransactionNotificationTracker extends _i1.Mock Invocation.getter(#walletId), ), ) as String); + @override List get pendings => (super.noSuchMethod( Invocation.getter(#pendings), returnValue: [], ) as List); + @override List get confirmeds => (super.noSuchMethod( Invocation.getter(#confirmeds), returnValue: [], ) as List); + @override bool wasNotifiedPending(String? txid) => (super.noSuchMethod( Invocation.method( @@ -697,6 +793,7 @@ class MockTransactionNotificationTracker extends _i1.Mock ), returnValue: false, ) as bool); + @override _i6.Future addNotifiedPending(String? txid) => (super.noSuchMethod( Invocation.method( @@ -706,6 +803,7 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override bool wasNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( @@ -714,6 +812,7 @@ class MockTransactionNotificationTracker extends _i1.Mock ), returnValue: false, ) as bool); + @override _i6.Future addNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( @@ -723,6 +822,7 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future deleteTransaction(String? txid) => (super.noSuchMethod( Invocation.method( diff --git a/test/services/coins/particl/particl_wallet_test.mocks.dart b/test/services/coins/particl/particl_wallet_test.mocks.dart index 3490fc59f..0fde0b645 100644 --- a/test/services/coins/particl/particl_wallet_test.mocks.dart +++ b/test/services/coins/particl/particl_wallet_test.mocks.dart @@ -86,6 +86,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { Invocation.getter(#cryptoCurrency), ), ) as _i2.CryptoCurrency); + @override set failovers(List<_i4.ElectrumXNode>? _failovers) => super.noSuchMethod( Invocation.setter( @@ -94,11 +95,13 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValueForMissingStub: null, ); + @override int get currentFailoverIndex => (super.noSuchMethod( Invocation.getter(#currentFailoverIndex), returnValue: 0, ) as int); + @override set currentFailoverIndex(int? _currentFailoverIndex) => super.noSuchMethod( Invocation.setter( @@ -107,6 +110,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValueForMissingStub: null, ); + @override Duration get connectionTimeoutForSpecialCaseJsonRPCClients => (super.noSuchMethod( @@ -116,6 +120,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), ), ) as Duration); + @override String get host => (super.noSuchMethod( Invocation.getter(#host), @@ -124,16 +129,19 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { Invocation.getter(#host), ), ) as String); + @override int get port => (super.noSuchMethod( Invocation.getter(#port), returnValue: 0, ) as int); + @override bool get useSSL => (super.noSuchMethod( Invocation.getter(#useSSL), returnValue: false, ) as bool); + @override _i6.Future closeAdapter() => (super.noSuchMethod( Invocation.method( @@ -143,6 +151,17 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + + @override + _i6.Future checkElectrumAdapter() => (super.noSuchMethod( + Invocation.method( + #checkElectrumAdapter, + [], + ), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); + @override _i6.Future request({ required String? command, @@ -165,6 +184,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future> batchRequest({ required String? command, @@ -185,6 +205,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future>.value([]), ) as _i6.Future>); + @override _i6.Future ping({ String? requestID, @@ -201,6 +222,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(false), ) as _i6.Future); + @override _i6.Future> getBlockHeadTip({String? requestID}) => (super.noSuchMethod( @@ -212,6 +234,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future> getServerFeatures({String? requestID}) => (super.noSuchMethod( @@ -223,6 +246,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future broadcastTransaction({ required String? rawTx, @@ -249,6 +273,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), )), ) as _i6.Future); + @override _i6.Future> getBalance({ required String? scripthash, @@ -266,6 +291,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future>> getHistory({ required String? scripthash, @@ -283,6 +309,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>.value( >[]), ) as _i6.Future>>); + @override _i6.Future>>> getBatchHistory( {required List? args}) => @@ -295,6 +322,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>>.value( >>[]), ) as _i6.Future>>>); + @override _i6.Future>> getUTXOs({ required String? scripthash, @@ -312,6 +340,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>.value( >[]), ) as _i6.Future>>); + @override _i6.Future>>> getBatchUTXOs( {required List? args}) => @@ -324,6 +353,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>>.value( >>[]), ) as _i6.Future>>>); + @override _i6.Future> getTransaction({ required String? txHash, @@ -343,6 +373,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future> getLelantusAnonymitySet({ String? groupId = r'1', @@ -362,6 +393,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future getLelantusMintData({ dynamic mints, @@ -378,6 +410,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future> getLelantusUsedCoinSerials({ String? requestID, @@ -395,6 +428,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future getLelantusLatestCoinId({String? requestID}) => (super.noSuchMethod( @@ -405,6 +439,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(0), ) as _i6.Future); + @override _i6.Future> getSparkAnonymitySet({ String? coinGroupId = r'1', @@ -424,6 +459,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future>> getSparkMintMetaData({ String? requestID, @@ -441,6 +477,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>>.value( >[]), ) as _i6.Future>>); + @override _i6.Future getSparkLatestCoinId({String? requestID}) => (super.noSuchMethod( @@ -451,6 +488,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future.value(0), ) as _i6.Future); + @override _i6.Future> getMempoolTxids({String? requestID}) => (super.noSuchMethod( @@ -461,8 +499,16 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override - _i6.Future> getMempoolSparkData({ + _i6.Future< + List< + ({ + List coins, + List lTags, + List serialContext, + String txid + })>> getMempoolSparkData({ String? requestID, required List? txids, }) => @@ -475,9 +521,28 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { #txids: txids, }, ), - returnValue: - _i6.Future>.value({}), - ) as _i6.Future>); + returnValue: _i6.Future< + List< + ({ + List coins, + List lTags, + List serialContext, + String txid + })>>.value(<({ + List coins, + List lTags, + List serialContext, + String txid + })>[]), + ) as _i6.Future< + List< + ({ + List coins, + List lTags, + List serialContext, + String txid + })>>); + @override _i6.Future>> getSparkUnhashedUsedCoinsTagsWithTxHashes({ String? requestID, @@ -494,6 +559,26 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), returnValue: _i6.Future>>.value(>[]), ) as _i6.Future>>); + + @override + _i6.Future isMasterNodeCollateral({ + String? requestID, + required String? txid, + required int? index, + }) => + (super.noSuchMethod( + Invocation.method( + #isMasterNodeCollateral, + [], + { + #requestID: requestID, + #txid: txid, + #index: index, + }, + ), + returnValue: _i6.Future.value(false), + ) as _i6.Future); + @override _i6.Future> getFeeRate({String? requestID}) => (super.noSuchMethod( @@ -505,6 +590,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future<_i3.Decimal> estimateFee({ String? requestID, @@ -531,6 +617,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient { ), )), ) as _i6.Future<_i3.Decimal>); + @override _i6.Future<_i3.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( Invocation.method( @@ -566,6 +653,7 @@ class MockCachedElectrumXClient extends _i1.Mock Invocation.getter(#electrumXClient), ), ) as _i4.ElectrumXClient); + @override _i6.Future> getAnonymitySet({ required String? groupId, @@ -585,6 +673,7 @@ class MockCachedElectrumXClient extends _i1.Mock returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override String base64ToHex(String? source) => (super.noSuchMethod( Invocation.method( @@ -599,6 +688,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), ), ) as String); + @override String base64ToReverseHex(String? source) => (super.noSuchMethod( Invocation.method( @@ -613,6 +703,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), ), ) as String); + @override _i6.Future> getTransaction({ required String? txHash, @@ -632,6 +723,7 @@ class MockCachedElectrumXClient extends _i1.Mock returnValue: _i6.Future>.value({}), ) as _i6.Future>); + @override _i6.Future> getUsedCoinSerials({ required _i2.CryptoCurrency? cryptoCurrency, @@ -648,6 +740,7 @@ class MockCachedElectrumXClient extends _i1.Mock ), returnValue: _i6.Future>.value([]), ) as _i6.Future>); + @override _i6.Future clearSharedTransactionCache( {required _i2.CryptoCurrency? cryptoCurrency}) => @@ -679,16 +772,19 @@ class MockTransactionNotificationTracker extends _i1.Mock Invocation.getter(#walletId), ), ) as String); + @override List get pendings => (super.noSuchMethod( Invocation.getter(#pendings), returnValue: [], ) as List); + @override List get confirmeds => (super.noSuchMethod( Invocation.getter(#confirmeds), returnValue: [], ) as List); + @override bool wasNotifiedPending(String? txid) => (super.noSuchMethod( Invocation.method( @@ -697,6 +793,7 @@ class MockTransactionNotificationTracker extends _i1.Mock ), returnValue: false, ) as bool); + @override _i6.Future addNotifiedPending(String? txid) => (super.noSuchMethod( Invocation.method( @@ -706,6 +803,7 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override bool wasNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( @@ -714,6 +812,7 @@ class MockTransactionNotificationTracker extends _i1.Mock ), returnValue: false, ) as bool); + @override _i6.Future addNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( @@ -723,6 +822,7 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future deleteTransaction(String? txid) => (super.noSuchMethod( Invocation.method( diff --git a/test/services/node_service_test.dart b/test/services/node_service_test.dart index 5b4f1e635..cb402deed 100644 --- a/test/services/node_service_test.dart +++ b/test/services/node_service_test.dart @@ -1,10 +1,10 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:hive/hive.dart'; import 'package:hive_test/hive_test.dart'; +import 'package:stackwallet/app_config.dart'; import 'package:stackwallet/db/hive/db.dart'; import 'package:stackwallet/models/node_model.dart'; import 'package:stackwallet/services/node_service.dart'; -import 'package:stackwallet/app_config.dart'; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; @@ -48,6 +48,8 @@ void main() { coinName: "bitcoin", isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); await service.setPrimaryNodeFor( coin: Bitcoin(CryptoCurrencyNetwork.main), @@ -129,6 +131,8 @@ void main() { coinName: "bitcoin", isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); final nodeB = NodeModel( host: "host2", @@ -140,6 +144,8 @@ void main() { coinName: "monero", isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); final nodeC = NodeModel( host: "host3", @@ -151,6 +157,8 @@ void main() { coinName: "epicCash", isFailover: true, isDown: false, + torEnabled: true, + clearnetEnabled: true, ); setUp(() async { diff --git a/test/widget_tests/address_book_card_test.mocks.dart b/test/widget_tests/address_book_card_test.mocks.dart index cfb056577..220d5cd66 100644 --- a/test/widget_tests/address_book_card_test.mocks.dart +++ b/test/widget_tests/address_book_card_test.mocks.dart @@ -47,11 +47,13 @@ class MockAddressBookService extends _i1.Mock Invocation.getter(#contacts), returnValue: <_i2.ContactEntry>[], ) as List<_i2.ContactEntry>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i2.ContactEntry getContactById(String? id) => (super.noSuchMethod( Invocation.method( @@ -66,6 +68,7 @@ class MockAddressBookService extends _i1.Mock ), ), ) as _i2.ContactEntry); + @override _i4.Future> search(String? text) => (super.noSuchMethod( @@ -76,6 +79,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i4.Future>.value(<_i2.ContactEntry>[]), ) as _i4.Future>); + @override bool matches( String? term, @@ -91,6 +95,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: false, ) as bool); + @override _i4.Future addContact(_i2.ContactEntry? contact) => (super.noSuchMethod( Invocation.method( @@ -99,6 +104,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: _i4.Future.value(false), ) as _i4.Future); + @override _i4.Future editContact(_i2.ContactEntry? editedContact) => (super.noSuchMethod( @@ -108,6 +114,7 @@ class MockAddressBookService extends _i1.Mock ), returnValue: _i4.Future.value(false), ) as _i4.Future); + @override _i4.Future removeContact(String? id) => (super.noSuchMethod( Invocation.method( @@ -117,6 +124,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override void addListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -125,6 +133,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void removeListener(_i5.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -133,6 +142,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -141,6 +151,7 @@ class MockAddressBookService extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/widget_tests/custom_buttons/favorite_toggle_test.mocks.dart b/test/widget_tests/custom_buttons/favorite_toggle_test.mocks.dart index 9463dccb8..a7d302709 100644 --- a/test/widget_tests/custom_buttons/favorite_toggle_test.mocks.dart +++ b/test/widget_tests/custom_buttons/favorite_toggle_test.mocks.dart @@ -61,6 +61,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { Invocation.getter(#client), ), ) as _i2.HTTP); + @override set client(_i2.HTTP? _client) => super.noSuchMethod( Invocation.setter( @@ -69,6 +70,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get db => (super.noSuchMethod( Invocation.getter(#db), @@ -77,11 +79,13 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { Invocation.getter(#db), ), ) as _i3.MainDB); + @override List<_i5.StackTheme> get installedThemes => (super.noSuchMethod( Invocation.getter(#installedThemes), returnValue: <_i5.StackTheme>[], ) as List<_i5.StackTheme>); + @override void init(_i3.MainDB? db) => super.noSuchMethod( Invocation.method( @@ -90,6 +94,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValueForMissingStub: null, ); + @override _i6.Future install({required _i7.Uint8List? themeArchiveData}) => (super.noSuchMethod( @@ -101,6 +106,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future remove({required String? themeId}) => (super.noSuchMethod( Invocation.method( @@ -111,6 +117,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future checkDefaultThemesOnStartup() => (super.noSuchMethod( Invocation.method( @@ -120,6 +127,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future verifyInstalled({required String? themeId}) => (super.noSuchMethod( @@ -130,6 +138,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValue: _i6.Future.value(false), ) as _i6.Future); + @override _i6.Future> fetchThemes() => (super.noSuchMethod( Invocation.method( @@ -139,6 +148,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future>.value( <_i4.StackThemeMetaData>[]), ) as _i6.Future>); + @override _i6.Future<_i7.Uint8List> fetchTheme( {required _i4.StackThemeMetaData? themeMetaData}) => @@ -150,6 +160,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValue: _i6.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), ) as _i6.Future<_i7.Uint8List>); + @override _i5.StackTheme? getTheme({required String? themeId}) => (super.noSuchMethod(Invocation.method( diff --git a/test/widget_tests/custom_loading_overlay_test.mocks.dart b/test/widget_tests/custom_loading_overlay_test.mocks.dart index 6848da7ee..996bcf94c 100644 --- a/test/widget_tests/custom_loading_overlay_test.mocks.dart +++ b/test/widget_tests/custom_loading_overlay_test.mocks.dart @@ -61,6 +61,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { Invocation.getter(#client), ), ) as _i2.HTTP); + @override set client(_i2.HTTP? _client) => super.noSuchMethod( Invocation.setter( @@ -69,6 +70,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get db => (super.noSuchMethod( Invocation.getter(#db), @@ -77,11 +79,13 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { Invocation.getter(#db), ), ) as _i3.MainDB); + @override List<_i5.StackTheme> get installedThemes => (super.noSuchMethod( Invocation.getter(#installedThemes), returnValue: <_i5.StackTheme>[], ) as List<_i5.StackTheme>); + @override void init(_i3.MainDB? db) => super.noSuchMethod( Invocation.method( @@ -90,6 +94,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValueForMissingStub: null, ); + @override _i6.Future install({required _i7.Uint8List? themeArchiveData}) => (super.noSuchMethod( @@ -101,6 +106,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future remove({required String? themeId}) => (super.noSuchMethod( Invocation.method( @@ -111,6 +117,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future checkDefaultThemesOnStartup() => (super.noSuchMethod( Invocation.method( @@ -120,6 +127,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future verifyInstalled({required String? themeId}) => (super.noSuchMethod( @@ -130,6 +138,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValue: _i6.Future.value(false), ) as _i6.Future); + @override _i6.Future> fetchThemes() => (super.noSuchMethod( Invocation.method( @@ -139,6 +148,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future>.value( <_i4.StackThemeMetaData>[]), ) as _i6.Future>); + @override _i6.Future<_i7.Uint8List> fetchTheme( {required _i4.StackThemeMetaData? themeMetaData}) => @@ -150,6 +160,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValue: _i6.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), ) as _i6.Future<_i7.Uint8List>); + @override _i5.StackTheme? getTheme({required String? themeId}) => (super.noSuchMethod(Invocation.method( diff --git a/test/widget_tests/desktop/desktop_scaffold_test.mocks.dart b/test/widget_tests/desktop/desktop_scaffold_test.mocks.dart index 046da5ebf..802731670 100644 --- a/test/widget_tests/desktop/desktop_scaffold_test.mocks.dart +++ b/test/widget_tests/desktop/desktop_scaffold_test.mocks.dart @@ -61,6 +61,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { Invocation.getter(#client), ), ) as _i2.HTTP); + @override set client(_i2.HTTP? _client) => super.noSuchMethod( Invocation.setter( @@ -69,6 +70,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get db => (super.noSuchMethod( Invocation.getter(#db), @@ -77,11 +79,13 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { Invocation.getter(#db), ), ) as _i3.MainDB); + @override List<_i5.StackTheme> get installedThemes => (super.noSuchMethod( Invocation.getter(#installedThemes), returnValue: <_i5.StackTheme>[], ) as List<_i5.StackTheme>); + @override void init(_i3.MainDB? db) => super.noSuchMethod( Invocation.method( @@ -90,6 +94,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValueForMissingStub: null, ); + @override _i6.Future install({required _i7.Uint8List? themeArchiveData}) => (super.noSuchMethod( @@ -101,6 +106,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future remove({required String? themeId}) => (super.noSuchMethod( Invocation.method( @@ -111,6 +117,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future checkDefaultThemesOnStartup() => (super.noSuchMethod( Invocation.method( @@ -120,6 +127,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future verifyInstalled({required String? themeId}) => (super.noSuchMethod( @@ -130,6 +138,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValue: _i6.Future.value(false), ) as _i6.Future); + @override _i6.Future> fetchThemes() => (super.noSuchMethod( Invocation.method( @@ -139,6 +148,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future>.value( <_i4.StackThemeMetaData>[]), ) as _i6.Future>); + @override _i6.Future<_i7.Uint8List> fetchTheme( {required _i4.StackThemeMetaData? themeMetaData}) => @@ -150,6 +160,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValue: _i6.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), ) as _i6.Future<_i7.Uint8List>); + @override _i5.StackTheme? getTheme({required String? themeId}) => (super.noSuchMethod(Invocation.method( diff --git a/test/widget_tests/managed_favorite_test.mocks.dart b/test/widget_tests/managed_favorite_test.mocks.dart index dad98a259..c2bc00d2c 100644 --- a/test/widget_tests/managed_favorite_test.mocks.dart +++ b/test/widget_tests/managed_favorite_test.mocks.dart @@ -121,6 +121,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { Invocation.getter(#nodeService), ), ) as _i2.NodeService); + @override set nodeService(_i2.NodeService? _nodeService) => super.noSuchMethod( Invocation.setter( @@ -129,6 +130,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get mainDB => (super.noSuchMethod( Invocation.getter(#mainDB), @@ -137,6 +139,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { Invocation.getter(#mainDB), ), ) as _i3.MainDB); + @override set mainDB(_i3.MainDB? _mainDB) => super.noSuchMethod( Invocation.setter( @@ -145,11 +148,13 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { ), returnValueForMissingStub: null, ); + @override List<_i5.Wallet<_i4.CryptoCurrency>> get wallets => (super.noSuchMethod( Invocation.getter(#wallets), returnValue: <_i5.Wallet<_i4.CryptoCurrency>>[], ) as List<_i5.Wallet<_i4.CryptoCurrency>>); + @override _i5.Wallet<_i4.CryptoCurrency> getWallet(String? walletId) => (super.noSuchMethod( @@ -165,6 +170,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { ), ), ) as _i5.Wallet<_i4.CryptoCurrency>); + @override void addWallet(_i5.Wallet<_i4.CryptoCurrency>? wallet) => super.noSuchMethod( Invocation.method( @@ -173,6 +179,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { ), returnValueForMissingStub: null, ); + @override _i10.Future deleteWallet( _i11.WalletInfo? info, @@ -189,6 +196,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future load( _i12.Prefs? prefs, @@ -205,6 +213,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future loadAfterStackRestore( _i12.Prefs? prefs, @@ -241,6 +250,7 @@ class MockThemeService extends _i1.Mock implements _i13.ThemeService { Invocation.getter(#client), ), ) as _i6.HTTP); + @override set client(_i6.HTTP? _client) => super.noSuchMethod( Invocation.setter( @@ -249,6 +259,7 @@ class MockThemeService extends _i1.Mock implements _i13.ThemeService { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get db => (super.noSuchMethod( Invocation.getter(#db), @@ -257,11 +268,13 @@ class MockThemeService extends _i1.Mock implements _i13.ThemeService { Invocation.getter(#db), ), ) as _i3.MainDB); + @override List<_i14.StackTheme> get installedThemes => (super.noSuchMethod( Invocation.getter(#installedThemes), returnValue: <_i14.StackTheme>[], ) as List<_i14.StackTheme>); + @override void init(_i3.MainDB? db) => super.noSuchMethod( Invocation.method( @@ -270,6 +283,7 @@ class MockThemeService extends _i1.Mock implements _i13.ThemeService { ), returnValueForMissingStub: null, ); + @override _i10.Future install({required _i15.Uint8List? themeArchiveData}) => (super.noSuchMethod( @@ -281,6 +295,7 @@ class MockThemeService extends _i1.Mock implements _i13.ThemeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future remove({required String? themeId}) => (super.noSuchMethod( Invocation.method( @@ -291,6 +306,7 @@ class MockThemeService extends _i1.Mock implements _i13.ThemeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future checkDefaultThemesOnStartup() => (super.noSuchMethod( Invocation.method( @@ -300,6 +316,7 @@ class MockThemeService extends _i1.Mock implements _i13.ThemeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future verifyInstalled({required String? themeId}) => (super.noSuchMethod( @@ -310,6 +327,7 @@ class MockThemeService extends _i1.Mock implements _i13.ThemeService { ), returnValue: _i10.Future.value(false), ) as _i10.Future); + @override _i10.Future> fetchThemes() => (super.noSuchMethod( @@ -320,6 +338,7 @@ class MockThemeService extends _i1.Mock implements _i13.ThemeService { returnValue: _i10.Future>.value( <_i13.StackThemeMetaData>[]), ) as _i10.Future>); + @override _i10.Future<_i15.Uint8List> fetchTheme( {required _i13.StackThemeMetaData? themeMetaData}) => @@ -331,6 +350,7 @@ class MockThemeService extends _i1.Mock implements _i13.ThemeService { ), returnValue: _i10.Future<_i15.Uint8List>.value(_i15.Uint8List(0)), ) as _i10.Future<_i15.Uint8List>); + @override _i14.StackTheme? getTheme({required String? themeId}) => (super.noSuchMethod(Invocation.method( @@ -353,11 +373,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { Invocation.getter(#isInitialized), returnValue: false, ) as bool); + @override int get lastUnlockedTimeout => (super.noSuchMethod( Invocation.getter(#lastUnlockedTimeout), returnValue: 0, ) as int); + @override set lastUnlockedTimeout(int? lastUnlockedTimeout) => super.noSuchMethod( Invocation.setter( @@ -366,11 +388,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override int get lastUnlocked => (super.noSuchMethod( Invocation.getter(#lastUnlocked), returnValue: 0, ) as int); + @override set lastUnlocked(int? lastUnlocked) => super.noSuchMethod( Invocation.setter( @@ -379,16 +403,19 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override int get currentNotificationId => (super.noSuchMethod( Invocation.getter(#currentNotificationId), returnValue: 0, ) as int); + @override List get walletIdsSyncOnStartup => (super.noSuchMethod( Invocation.getter(#walletIdsSyncOnStartup), returnValue: [], ) as List); + @override set walletIdsSyncOnStartup(List? walletIdsSyncOnStartup) => super.noSuchMethod( @@ -398,11 +425,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override _i16.SyncingType get syncType => (super.noSuchMethod( Invocation.getter(#syncType), returnValue: _i16.SyncingType.currentWalletOnly, ) as _i16.SyncingType); + @override set syncType(_i16.SyncingType? syncType) => super.noSuchMethod( Invocation.setter( @@ -411,11 +440,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get wifiOnly => (super.noSuchMethod( Invocation.getter(#wifiOnly), returnValue: false, ) as bool); + @override set wifiOnly(bool? wifiOnly) => super.noSuchMethod( Invocation.setter( @@ -424,11 +455,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get showFavoriteWallets => (super.noSuchMethod( Invocation.getter(#showFavoriteWallets), returnValue: false, ) as bool); + @override set showFavoriteWallets(bool? showFavoriteWallets) => super.noSuchMethod( Invocation.setter( @@ -437,6 +470,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override String get language => (super.noSuchMethod( Invocation.getter(#language), @@ -445,6 +479,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { Invocation.getter(#language), ), ) as String); + @override set language(String? newLanguage) => super.noSuchMethod( Invocation.setter( @@ -453,6 +488,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override String get currency => (super.noSuchMethod( Invocation.getter(#currency), @@ -461,6 +497,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { Invocation.getter(#currency), ), ) as String); + @override set currency(String? newCurrency) => super.noSuchMethod( Invocation.setter( @@ -469,11 +506,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get randomizePIN => (super.noSuchMethod( Invocation.getter(#randomizePIN), returnValue: false, ) as bool); + @override set randomizePIN(bool? randomizePIN) => super.noSuchMethod( Invocation.setter( @@ -482,11 +521,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get useBiometrics => (super.noSuchMethod( Invocation.getter(#useBiometrics), returnValue: false, ) as bool); + @override set useBiometrics(bool? useBiometrics) => super.noSuchMethod( Invocation.setter( @@ -495,11 +536,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get hasPin => (super.noSuchMethod( Invocation.getter(#hasPin), returnValue: false, ) as bool); + @override set hasPin(bool? hasPin) => super.noSuchMethod( Invocation.setter( @@ -508,11 +551,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override int get familiarity => (super.noSuchMethod( Invocation.getter(#familiarity), returnValue: 0, ) as int); + @override set familiarity(int? familiarity) => super.noSuchMethod( Invocation.setter( @@ -521,11 +566,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get torKillSwitch => (super.noSuchMethod( Invocation.getter(#torKillSwitch), returnValue: false, ) as bool); + @override set torKillSwitch(bool? torKillswitch) => super.noSuchMethod( Invocation.setter( @@ -534,11 +581,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get showTestNetCoins => (super.noSuchMethod( Invocation.getter(#showTestNetCoins), returnValue: false, ) as bool); + @override set showTestNetCoins(bool? showTestNetCoins) => super.noSuchMethod( Invocation.setter( @@ -547,11 +596,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get isAutoBackupEnabled => (super.noSuchMethod( Invocation.getter(#isAutoBackupEnabled), returnValue: false, ) as bool); + @override set isAutoBackupEnabled(bool? isAutoBackupEnabled) => super.noSuchMethod( Invocation.setter( @@ -560,6 +611,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override set autoBackupLocation(String? autoBackupLocation) => super.noSuchMethod( Invocation.setter( @@ -568,11 +620,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override _i18.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( Invocation.getter(#backupFrequencyType), returnValue: _i18.BackupFrequencyType.everyTenMinutes, ) as _i18.BackupFrequencyType); + @override set backupFrequencyType(_i18.BackupFrequencyType? backupFrequencyType) => super.noSuchMethod( @@ -582,6 +636,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override set lastAutoBackup(DateTime? lastAutoBackup) => super.noSuchMethod( Invocation.setter( @@ -590,11 +645,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get hideBlockExplorerWarning => (super.noSuchMethod( Invocation.getter(#hideBlockExplorerWarning), returnValue: false, ) as bool); + @override set hideBlockExplorerWarning(bool? hideBlockExplorerWarning) => super.noSuchMethod( @@ -604,11 +661,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get gotoWalletOnStartup => (super.noSuchMethod( Invocation.getter(#gotoWalletOnStartup), returnValue: false, ) as bool); + @override set gotoWalletOnStartup(bool? gotoWalletOnStartup) => super.noSuchMethod( Invocation.setter( @@ -617,6 +676,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override set startupWalletId(String? startupWalletId) => super.noSuchMethod( Invocation.setter( @@ -625,11 +685,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get externalCalls => (super.noSuchMethod( Invocation.getter(#externalCalls), returnValue: false, ) as bool); + @override set externalCalls(bool? externalCalls) => super.noSuchMethod( Invocation.setter( @@ -638,11 +700,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get enableCoinControl => (super.noSuchMethod( Invocation.getter(#enableCoinControl), returnValue: false, ) as bool); + @override set enableCoinControl(bool? enableCoinControl) => super.noSuchMethod( Invocation.setter( @@ -651,11 +715,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get enableSystemBrightness => (super.noSuchMethod( Invocation.getter(#enableSystemBrightness), returnValue: false, ) as bool); + @override set enableSystemBrightness(bool? enableSystemBrightness) => super.noSuchMethod( @@ -665,6 +731,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override String get themeId => (super.noSuchMethod( Invocation.getter(#themeId), @@ -673,6 +740,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { Invocation.getter(#themeId), ), ) as String); + @override set themeId(String? themeId) => super.noSuchMethod( Invocation.setter( @@ -681,6 +749,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override String get systemBrightnessLightThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessLightThemeId), @@ -689,6 +758,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { Invocation.getter(#systemBrightnessLightThemeId), ), ) as String); + @override set systemBrightnessLightThemeId(String? systemBrightnessLightThemeId) => super.noSuchMethod( @@ -698,6 +768,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override String get systemBrightnessDarkThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessDarkThemeId), @@ -706,6 +777,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { Invocation.getter(#systemBrightnessDarkThemeId), ), ) as String); + @override set systemBrightnessDarkThemeId(String? systemBrightnessDarkThemeId) => super.noSuchMethod( @@ -715,11 +787,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get useTor => (super.noSuchMethod( Invocation.getter(#useTor), returnValue: false, ) as bool); + @override set useTor(bool? useTor) => super.noSuchMethod( Invocation.setter( @@ -728,11 +802,43 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + + @override + bool get autoPin => (super.noSuchMethod( + Invocation.getter(#autoPin), + returnValue: false, + ) as bool); + + @override + set autoPin(bool? autoPin) => super.noSuchMethod( + Invocation.setter( + #autoPin, + autoPin, + ), + returnValueForMissingStub: null, + ); + + @override + bool get enableExchange => (super.noSuchMethod( + Invocation.getter(#enableExchange), + returnValue: false, + ) as bool); + + @override + set enableExchange(bool? showExchange) => super.noSuchMethod( + Invocation.setter( + #enableExchange, + showExchange, + ), + returnValueForMissingStub: null, + ); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i10.Future init() => (super.noSuchMethod( Invocation.method( @@ -742,6 +848,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future incrementCurrentNotificationIndex() => (super.noSuchMethod( Invocation.method( @@ -751,6 +858,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future isExternalCallsSet() => (super.noSuchMethod( Invocation.method( @@ -759,6 +867,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValue: _i10.Future.value(false), ) as _i10.Future); + @override _i10.Future saveUserID(String? userId) => (super.noSuchMethod( Invocation.method( @@ -768,6 +877,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future saveSignupEpoch(int? signupEpoch) => (super.noSuchMethod( Invocation.method( @@ -777,6 +887,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i19.AmountUnit amountUnit(_i4.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( @@ -785,6 +896,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValue: _i19.AmountUnit.normal, ) as _i19.AmountUnit); + @override void updateAmountUnit({ required _i4.CryptoCurrency? coin, @@ -801,6 +913,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override int maxDecimals(_i4.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( @@ -809,6 +922,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValue: 0, ) as int); + @override void updateMaxDecimals({ required _i4.CryptoCurrency? coin, @@ -825,6 +939,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override _i7.FusionInfo getFusionServerInfo(_i4.CryptoCurrency? coin) => (super.noSuchMethod( @@ -840,6 +955,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), ), ) as _i7.FusionInfo); + @override void setFusionServerInfo( _i4.CryptoCurrency? coin, @@ -855,6 +971,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override void addListener(_i20.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -863,6 +980,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override void removeListener(_i20.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -871,6 +989,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -879,6 +998,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -905,11 +1025,13 @@ class MockLocaleService extends _i1.Mock implements _i21.LocaleService { Invocation.getter(#locale), ), ) as String); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i10.Future loadLocale({bool? notify = true}) => (super.noSuchMethod( Invocation.method( @@ -920,6 +1042,7 @@ class MockLocaleService extends _i1.Mock implements _i21.LocaleService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override void addListener(_i20.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -928,6 +1051,7 @@ class MockLocaleService extends _i1.Mock implements _i21.LocaleService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i20.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -936,6 +1060,7 @@ class MockLocaleService extends _i1.Mock implements _i21.LocaleService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -944,6 +1069,7 @@ class MockLocaleService extends _i1.Mock implements _i21.LocaleService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -970,21 +1096,25 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { Invocation.getter(#secureStorageInterface), ), ) as _i8.SecureStorageInterface); + @override List<_i22.NodeModel> get primaryNodes => (super.noSuchMethod( Invocation.getter(#primaryNodes), returnValue: <_i22.NodeModel>[], ) as List<_i22.NodeModel>); + @override List<_i22.NodeModel> get nodes => (super.noSuchMethod( Invocation.getter(#nodes), returnValue: <_i22.NodeModel>[], ) as List<_i22.NodeModel>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i10.Future updateDefaults() => (super.noSuchMethod( Invocation.method( @@ -994,6 +1124,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future setPrimaryNodeFor({ required _i4.CryptoCurrency? coin, @@ -1013,6 +1144,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i22.NodeModel? getPrimaryNodeFor({required _i4.CryptoCurrency? currency}) => (super.noSuchMethod(Invocation.method( @@ -1020,6 +1152,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { [], {#currency: currency}, )) as _i22.NodeModel?); + @override List<_i22.NodeModel> getNodesFor(_i4.CryptoCurrency? coin) => (super.noSuchMethod( @@ -1029,6 +1162,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValue: <_i22.NodeModel>[], ) as List<_i22.NodeModel>); + @override _i22.NodeModel? getNodeById({required String? id}) => (super.noSuchMethod(Invocation.method( @@ -1036,6 +1170,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { [], {#id: id}, )) as _i22.NodeModel?); + @override List<_i22.NodeModel> failoverNodesFor( {required _i4.CryptoCurrency? currency}) => @@ -1047,6 +1182,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValue: <_i22.NodeModel>[], ) as List<_i22.NodeModel>); + @override _i10.Future add( _i22.NodeModel? node, @@ -1065,6 +1201,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future delete( String? id, @@ -1081,6 +1218,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future setEnabledState( String? id, @@ -1099,6 +1237,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future edit( _i22.NodeModel? editedNode, @@ -1117,6 +1256,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future updateCommunityNodes() => (super.noSuchMethod( Invocation.method( @@ -1126,6 +1266,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override void addListener(_i20.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -1134,6 +1275,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i20.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -1142,6 +1284,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -1150,6 +1293,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/widget_tests/node_card_test.dart b/test/widget_tests/node_card_test.dart index 45c0dbe12..cc706824c 100644 --- a/test/widget_tests/node_card_test.dart +++ b/test/widget_tests/node_card_test.dart @@ -37,6 +37,8 @@ void main() { coinName: "Bitcoin", isFailover: false, isDown: false, + torEnabled: true, + clearnetEnabled: true, ), ); @@ -51,6 +53,8 @@ void main() { coinName: "Bitcoin", isFailover: false, isDown: false, + torEnabled: true, + clearnetEnabled: true, ), ); @@ -112,6 +116,8 @@ void main() { coinName: "Bitcoin", isFailover: false, isDown: false, + torEnabled: true, + clearnetEnabled: true, ), ); @@ -126,6 +132,8 @@ void main() { coinName: "Bitcoin", isFailover: false, isDown: false, + torEnabled: true, + clearnetEnabled: true, ), ); @@ -188,6 +196,8 @@ void main() { coinName: "Bitcoin", isFailover: false, isDown: false, + torEnabled: true, + clearnetEnabled: true, ), ); @@ -202,6 +212,8 @@ void main() { coinName: "Bitcoin", isFailover: false, isDown: false, + torEnabled: true, + clearnetEnabled: true, ), ); diff --git a/test/widget_tests/node_card_test.mocks.dart b/test/widget_tests/node_card_test.mocks.dart index c34b5b77f..441edcdbf 100644 --- a/test/widget_tests/node_card_test.mocks.dart +++ b/test/widget_tests/node_card_test.mocks.dart @@ -54,21 +54,25 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { Invocation.getter(#secureStorageInterface), ), ) as _i2.SecureStorageInterface); + @override List<_i4.NodeModel> get primaryNodes => (super.noSuchMethod( Invocation.getter(#primaryNodes), returnValue: <_i4.NodeModel>[], ) as List<_i4.NodeModel>); + @override List<_i4.NodeModel> get nodes => (super.noSuchMethod( Invocation.getter(#nodes), returnValue: <_i4.NodeModel>[], ) as List<_i4.NodeModel>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i5.Future updateDefaults() => (super.noSuchMethod( Invocation.method( @@ -78,6 +82,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future setPrimaryNodeFor({ required _i6.CryptoCurrency? coin, @@ -97,6 +102,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i4.NodeModel? getPrimaryNodeFor({required _i6.CryptoCurrency? currency}) => (super.noSuchMethod(Invocation.method( @@ -104,6 +110,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { [], {#currency: currency}, )) as _i4.NodeModel?); + @override List<_i4.NodeModel> getNodesFor(_i6.CryptoCurrency? coin) => (super.noSuchMethod( @@ -113,6 +120,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValue: <_i4.NodeModel>[], ) as List<_i4.NodeModel>); + @override _i4.NodeModel? getNodeById({required String? id}) => (super.noSuchMethod(Invocation.method( @@ -120,6 +128,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { [], {#id: id}, )) as _i4.NodeModel?); + @override List<_i4.NodeModel> failoverNodesFor( {required _i6.CryptoCurrency? currency}) => @@ -131,6 +140,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValue: <_i4.NodeModel>[], ) as List<_i4.NodeModel>); + @override _i5.Future add( _i4.NodeModel? node, @@ -149,6 +159,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future delete( String? id, @@ -165,6 +176,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future setEnabledState( String? id, @@ -183,6 +195,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future edit( _i4.NodeModel? editedNode, @@ -201,6 +214,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future updateCommunityNodes() => (super.noSuchMethod( Invocation.method( @@ -210,6 +224,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override void addListener(_i7.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -218,6 +233,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i7.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -226,6 +242,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -234,6 +251,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/widget_tests/node_options_sheet_test.dart b/test/widget_tests/node_options_sheet_test.dart index c289b29b5..998f6322a 100644 --- a/test/widget_tests/node_options_sheet_test.dart +++ b/test/widget_tests/node_options_sheet_test.dart @@ -25,17 +25,20 @@ void main() { final mockPrefs = MockPrefs(); final mockNodeService = MockNodeService(); - when(mockNodeService.getNodeById(id: "node id")).thenAnswer( - (realInvocation) => NodeModel( - host: "127.0.0.1", - port: 2000, - name: "Some other name", - id: "node id", - useSSL: true, - enabled: true, - coinName: "Bitcoin", - isFailover: false, - isDown: false)); + when(mockNodeService.getNodeById(id: "node id")) + .thenAnswer((realInvocation) => NodeModel( + host: "127.0.0.1", + port: 2000, + name: "Some other name", + id: "node id", + useSSL: true, + enabled: true, + coinName: "Bitcoin", + isFailover: false, + isDown: false, + torEnabled: true, + clearnetEnabled: true, + )); when(mockNodeService.getPrimaryNodeFor( currency: Bitcoin(CryptoCurrencyNetwork.main))) @@ -48,6 +51,8 @@ void main() { enabled: true, coinName: "Bitcoin", isFailover: false, + torEnabled: true, + clearnetEnabled: true, isDown: false)); await tester.pumpWidget( @@ -109,6 +114,8 @@ void main() { coinName: "Bitcoin", isFailover: false, isDown: false, + torEnabled: true, + clearnetEnabled: true, ), ); @@ -125,6 +132,8 @@ void main() { coinName: "Bitcoin", isFailover: false, isDown: false, + torEnabled: true, + clearnetEnabled: true, ), ); @@ -186,6 +195,8 @@ void main() { coinName: "Bitcoin", isFailover: false, isDown: false, + torEnabled: true, + clearnetEnabled: true, ), ); @@ -202,6 +213,8 @@ void main() { coinName: "Bitcoin", isFailover: false, isDown: false, + torEnabled: true, + clearnetEnabled: true, ), ); diff --git a/test/widget_tests/node_options_sheet_test.mocks.dart b/test/widget_tests/node_options_sheet_test.mocks.dart index 59450b63e..5e878015f 100644 --- a/test/widget_tests/node_options_sheet_test.mocks.dart +++ b/test/widget_tests/node_options_sheet_test.mocks.dart @@ -122,6 +122,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { Invocation.getter(#nodeService), ), ) as _i2.NodeService); + @override set nodeService(_i2.NodeService? _nodeService) => super.noSuchMethod( Invocation.setter( @@ -130,6 +131,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get mainDB => (super.noSuchMethod( Invocation.getter(#mainDB), @@ -138,6 +140,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { Invocation.getter(#mainDB), ), ) as _i3.MainDB); + @override set mainDB(_i3.MainDB? _mainDB) => super.noSuchMethod( Invocation.setter( @@ -146,11 +149,13 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { ), returnValueForMissingStub: null, ); + @override List<_i5.Wallet<_i4.CryptoCurrency>> get wallets => (super.noSuchMethod( Invocation.getter(#wallets), returnValue: <_i5.Wallet<_i4.CryptoCurrency>>[], ) as List<_i5.Wallet<_i4.CryptoCurrency>>); + @override _i5.Wallet<_i4.CryptoCurrency> getWallet(String? walletId) => (super.noSuchMethod( @@ -166,6 +171,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { ), ), ) as _i5.Wallet<_i4.CryptoCurrency>); + @override void addWallet(_i5.Wallet<_i4.CryptoCurrency>? wallet) => super.noSuchMethod( Invocation.method( @@ -174,6 +180,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { ), returnValueForMissingStub: null, ); + @override _i10.Future deleteWallet( _i11.WalletInfo? info, @@ -190,6 +197,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future load( _i12.Prefs? prefs, @@ -206,6 +214,7 @@ class MockWallets extends _i1.Mock implements _i9.Wallets { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future loadAfterStackRestore( _i12.Prefs? prefs, @@ -239,11 +248,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { Invocation.getter(#isInitialized), returnValue: false, ) as bool); + @override int get lastUnlockedTimeout => (super.noSuchMethod( Invocation.getter(#lastUnlockedTimeout), returnValue: 0, ) as int); + @override set lastUnlockedTimeout(int? lastUnlockedTimeout) => super.noSuchMethod( Invocation.setter( @@ -252,11 +263,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override int get lastUnlocked => (super.noSuchMethod( Invocation.getter(#lastUnlocked), returnValue: 0, ) as int); + @override set lastUnlocked(int? lastUnlocked) => super.noSuchMethod( Invocation.setter( @@ -265,16 +278,19 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override int get currentNotificationId => (super.noSuchMethod( Invocation.getter(#currentNotificationId), returnValue: 0, ) as int); + @override List get walletIdsSyncOnStartup => (super.noSuchMethod( Invocation.getter(#walletIdsSyncOnStartup), returnValue: [], ) as List); + @override set walletIdsSyncOnStartup(List? walletIdsSyncOnStartup) => super.noSuchMethod( @@ -284,11 +300,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override _i13.SyncingType get syncType => (super.noSuchMethod( Invocation.getter(#syncType), returnValue: _i13.SyncingType.currentWalletOnly, ) as _i13.SyncingType); + @override set syncType(_i13.SyncingType? syncType) => super.noSuchMethod( Invocation.setter( @@ -297,11 +315,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get wifiOnly => (super.noSuchMethod( Invocation.getter(#wifiOnly), returnValue: false, ) as bool); + @override set wifiOnly(bool? wifiOnly) => super.noSuchMethod( Invocation.setter( @@ -310,11 +330,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get showFavoriteWallets => (super.noSuchMethod( Invocation.getter(#showFavoriteWallets), returnValue: false, ) as bool); + @override set showFavoriteWallets(bool? showFavoriteWallets) => super.noSuchMethod( Invocation.setter( @@ -323,6 +345,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override String get language => (super.noSuchMethod( Invocation.getter(#language), @@ -331,6 +354,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { Invocation.getter(#language), ), ) as String); + @override set language(String? newLanguage) => super.noSuchMethod( Invocation.setter( @@ -339,6 +363,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override String get currency => (super.noSuchMethod( Invocation.getter(#currency), @@ -347,6 +372,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { Invocation.getter(#currency), ), ) as String); + @override set currency(String? newCurrency) => super.noSuchMethod( Invocation.setter( @@ -355,11 +381,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get randomizePIN => (super.noSuchMethod( Invocation.getter(#randomizePIN), returnValue: false, ) as bool); + @override set randomizePIN(bool? randomizePIN) => super.noSuchMethod( Invocation.setter( @@ -368,11 +396,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get useBiometrics => (super.noSuchMethod( Invocation.getter(#useBiometrics), returnValue: false, ) as bool); + @override set useBiometrics(bool? useBiometrics) => super.noSuchMethod( Invocation.setter( @@ -381,11 +411,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get hasPin => (super.noSuchMethod( Invocation.getter(#hasPin), returnValue: false, ) as bool); + @override set hasPin(bool? hasPin) => super.noSuchMethod( Invocation.setter( @@ -394,11 +426,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override int get familiarity => (super.noSuchMethod( Invocation.getter(#familiarity), returnValue: 0, ) as int); + @override set familiarity(int? familiarity) => super.noSuchMethod( Invocation.setter( @@ -407,11 +441,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get torKillSwitch => (super.noSuchMethod( Invocation.getter(#torKillSwitch), returnValue: false, ) as bool); + @override set torKillSwitch(bool? torKillswitch) => super.noSuchMethod( Invocation.setter( @@ -420,11 +456,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get showTestNetCoins => (super.noSuchMethod( Invocation.getter(#showTestNetCoins), returnValue: false, ) as bool); + @override set showTestNetCoins(bool? showTestNetCoins) => super.noSuchMethod( Invocation.setter( @@ -433,11 +471,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get isAutoBackupEnabled => (super.noSuchMethod( Invocation.getter(#isAutoBackupEnabled), returnValue: false, ) as bool); + @override set isAutoBackupEnabled(bool? isAutoBackupEnabled) => super.noSuchMethod( Invocation.setter( @@ -446,6 +486,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override set autoBackupLocation(String? autoBackupLocation) => super.noSuchMethod( Invocation.setter( @@ -454,11 +495,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override _i15.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( Invocation.getter(#backupFrequencyType), returnValue: _i15.BackupFrequencyType.everyTenMinutes, ) as _i15.BackupFrequencyType); + @override set backupFrequencyType(_i15.BackupFrequencyType? backupFrequencyType) => super.noSuchMethod( @@ -468,6 +511,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override set lastAutoBackup(DateTime? lastAutoBackup) => super.noSuchMethod( Invocation.setter( @@ -476,11 +520,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get hideBlockExplorerWarning => (super.noSuchMethod( Invocation.getter(#hideBlockExplorerWarning), returnValue: false, ) as bool); + @override set hideBlockExplorerWarning(bool? hideBlockExplorerWarning) => super.noSuchMethod( @@ -490,11 +536,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get gotoWalletOnStartup => (super.noSuchMethod( Invocation.getter(#gotoWalletOnStartup), returnValue: false, ) as bool); + @override set gotoWalletOnStartup(bool? gotoWalletOnStartup) => super.noSuchMethod( Invocation.setter( @@ -503,6 +551,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override set startupWalletId(String? startupWalletId) => super.noSuchMethod( Invocation.setter( @@ -511,11 +560,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get externalCalls => (super.noSuchMethod( Invocation.getter(#externalCalls), returnValue: false, ) as bool); + @override set externalCalls(bool? externalCalls) => super.noSuchMethod( Invocation.setter( @@ -524,11 +575,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get enableCoinControl => (super.noSuchMethod( Invocation.getter(#enableCoinControl), returnValue: false, ) as bool); + @override set enableCoinControl(bool? enableCoinControl) => super.noSuchMethod( Invocation.setter( @@ -537,11 +590,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get enableSystemBrightness => (super.noSuchMethod( Invocation.getter(#enableSystemBrightness), returnValue: false, ) as bool); + @override set enableSystemBrightness(bool? enableSystemBrightness) => super.noSuchMethod( @@ -551,6 +606,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override String get themeId => (super.noSuchMethod( Invocation.getter(#themeId), @@ -559,6 +615,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { Invocation.getter(#themeId), ), ) as String); + @override set themeId(String? themeId) => super.noSuchMethod( Invocation.setter( @@ -567,6 +624,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override String get systemBrightnessLightThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessLightThemeId), @@ -575,6 +633,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { Invocation.getter(#systemBrightnessLightThemeId), ), ) as String); + @override set systemBrightnessLightThemeId(String? systemBrightnessLightThemeId) => super.noSuchMethod( @@ -584,6 +643,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override String get systemBrightnessDarkThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessDarkThemeId), @@ -592,6 +652,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { Invocation.getter(#systemBrightnessDarkThemeId), ), ) as String); + @override set systemBrightnessDarkThemeId(String? systemBrightnessDarkThemeId) => super.noSuchMethod( @@ -601,11 +662,13 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override bool get useTor => (super.noSuchMethod( Invocation.getter(#useTor), returnValue: false, ) as bool); + @override set useTor(bool? useTor) => super.noSuchMethod( Invocation.setter( @@ -614,11 +677,43 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + + @override + bool get autoPin => (super.noSuchMethod( + Invocation.getter(#autoPin), + returnValue: false, + ) as bool); + + @override + set autoPin(bool? autoPin) => super.noSuchMethod( + Invocation.setter( + #autoPin, + autoPin, + ), + returnValueForMissingStub: null, + ); + + @override + bool get enableExchange => (super.noSuchMethod( + Invocation.getter(#enableExchange), + returnValue: false, + ) as bool); + + @override + set enableExchange(bool? showExchange) => super.noSuchMethod( + Invocation.setter( + #enableExchange, + showExchange, + ), + returnValueForMissingStub: null, + ); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i10.Future init() => (super.noSuchMethod( Invocation.method( @@ -628,6 +723,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future incrementCurrentNotificationIndex() => (super.noSuchMethod( Invocation.method( @@ -637,6 +733,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future isExternalCallsSet() => (super.noSuchMethod( Invocation.method( @@ -645,6 +742,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValue: _i10.Future.value(false), ) as _i10.Future); + @override _i10.Future saveUserID(String? userId) => (super.noSuchMethod( Invocation.method( @@ -654,6 +752,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future saveSignupEpoch(int? signupEpoch) => (super.noSuchMethod( Invocation.method( @@ -663,6 +762,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i16.AmountUnit amountUnit(_i4.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( @@ -671,6 +771,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValue: _i16.AmountUnit.normal, ) as _i16.AmountUnit); + @override void updateAmountUnit({ required _i4.CryptoCurrency? coin, @@ -687,6 +788,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override int maxDecimals(_i4.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( @@ -695,6 +797,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValue: 0, ) as int); + @override void updateMaxDecimals({ required _i4.CryptoCurrency? coin, @@ -711,6 +814,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override _i6.FusionInfo getFusionServerInfo(_i4.CryptoCurrency? coin) => (super.noSuchMethod( @@ -726,6 +830,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), ), ) as _i6.FusionInfo); + @override void setFusionServerInfo( _i4.CryptoCurrency? coin, @@ -741,6 +846,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override void addListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -749,6 +855,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override void removeListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -757,6 +864,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -765,6 +873,7 @@ class MockPrefs extends _i1.Mock implements _i12.Prefs { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -791,21 +900,25 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { Invocation.getter(#secureStorageInterface), ), ) as _i7.SecureStorageInterface); + @override List<_i18.NodeModel> get primaryNodes => (super.noSuchMethod( Invocation.getter(#primaryNodes), returnValue: <_i18.NodeModel>[], ) as List<_i18.NodeModel>); + @override List<_i18.NodeModel> get nodes => (super.noSuchMethod( Invocation.getter(#nodes), returnValue: <_i18.NodeModel>[], ) as List<_i18.NodeModel>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i10.Future updateDefaults() => (super.noSuchMethod( Invocation.method( @@ -815,6 +928,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future setPrimaryNodeFor({ required _i4.CryptoCurrency? coin, @@ -834,6 +948,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i18.NodeModel? getPrimaryNodeFor({required _i4.CryptoCurrency? currency}) => (super.noSuchMethod(Invocation.method( @@ -841,6 +956,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { [], {#currency: currency}, )) as _i18.NodeModel?); + @override List<_i18.NodeModel> getNodesFor(_i4.CryptoCurrency? coin) => (super.noSuchMethod( @@ -850,6 +966,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValue: <_i18.NodeModel>[], ) as List<_i18.NodeModel>); + @override _i18.NodeModel? getNodeById({required String? id}) => (super.noSuchMethod(Invocation.method( @@ -857,6 +974,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { [], {#id: id}, )) as _i18.NodeModel?); + @override List<_i18.NodeModel> failoverNodesFor( {required _i4.CryptoCurrency? currency}) => @@ -868,6 +986,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValue: <_i18.NodeModel>[], ) as List<_i18.NodeModel>); + @override _i10.Future add( _i18.NodeModel? node, @@ -886,6 +1005,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future delete( String? id, @@ -902,6 +1022,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future setEnabledState( String? id, @@ -920,6 +1041,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future edit( _i18.NodeModel? editedNode, @@ -938,6 +1060,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future updateCommunityNodes() => (super.noSuchMethod( Invocation.method( @@ -947,6 +1070,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override void addListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -955,6 +1079,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -963,6 +1088,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -971,6 +1097,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -994,6 +1121,7 @@ class MockTorService extends _i1.Mock implements _i19.TorService { Invocation.getter(#status), returnValue: _i20.TorConnectionStatus.disconnected, ) as _i20.TorConnectionStatus); + @override ({_i8.InternetAddress host, int port}) getProxyInfo() => (super.noSuchMethod( Invocation.method( @@ -1011,6 +1139,7 @@ class MockTorService extends _i1.Mock implements _i19.TorService { port: 0 ), ) as ({_i8.InternetAddress host, int port})); + @override void init({ required String? torDataDirPath, @@ -1027,6 +1156,7 @@ class MockTorService extends _i1.Mock implements _i19.TorService { ), returnValueForMissingStub: null, ); + @override _i10.Future start() => (super.noSuchMethod( Invocation.method( @@ -1036,6 +1166,7 @@ class MockTorService extends _i1.Mock implements _i19.TorService { returnValue: _i10.Future.value(), returnValueForMissingStub: _i10.Future.value(), ) as _i10.Future); + @override _i10.Future disable() => (super.noSuchMethod( Invocation.method( diff --git a/test/widget_tests/table_view/table_view_row_test.mocks.dart b/test/widget_tests/table_view/table_view_row_test.mocks.dart index 09791d1f0..9393a5730 100644 --- a/test/widget_tests/table_view/table_view_row_test.mocks.dart +++ b/test/widget_tests/table_view/table_view_row_test.mocks.dart @@ -91,6 +91,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { Invocation.getter(#nodeService), ), ) as _i2.NodeService); + @override set nodeService(_i2.NodeService? _nodeService) => super.noSuchMethod( Invocation.setter( @@ -99,6 +100,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get mainDB => (super.noSuchMethod( Invocation.getter(#mainDB), @@ -107,6 +109,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { Invocation.getter(#mainDB), ), ) as _i3.MainDB); + @override set mainDB(_i3.MainDB? _mainDB) => super.noSuchMethod( Invocation.setter( @@ -115,11 +118,13 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { ), returnValueForMissingStub: null, ); + @override List<_i5.Wallet<_i4.CryptoCurrency>> get wallets => (super.noSuchMethod( Invocation.getter(#wallets), returnValue: <_i5.Wallet<_i4.CryptoCurrency>>[], ) as List<_i5.Wallet<_i4.CryptoCurrency>>); + @override _i5.Wallet<_i4.CryptoCurrency> getWallet(String? walletId) => (super.noSuchMethod( @@ -135,6 +140,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { ), ), ) as _i5.Wallet<_i4.CryptoCurrency>); + @override void addWallet(_i5.Wallet<_i4.CryptoCurrency>? wallet) => super.noSuchMethod( Invocation.method( @@ -143,6 +149,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { ), returnValueForMissingStub: null, ); + @override _i8.Future deleteWallet( _i9.WalletInfo? info, @@ -159,6 +166,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override _i8.Future load( _i11.Prefs? prefs, @@ -175,6 +183,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override _i8.Future loadAfterStackRestore( _i11.Prefs? prefs, @@ -211,6 +220,7 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { Invocation.getter(#client), ), ) as _i6.HTTP); + @override set client(_i6.HTTP? _client) => super.noSuchMethod( Invocation.setter( @@ -219,6 +229,7 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get db => (super.noSuchMethod( Invocation.getter(#db), @@ -227,11 +238,13 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { Invocation.getter(#db), ), ) as _i3.MainDB); + @override List<_i13.StackTheme> get installedThemes => (super.noSuchMethod( Invocation.getter(#installedThemes), returnValue: <_i13.StackTheme>[], ) as List<_i13.StackTheme>); + @override void init(_i3.MainDB? db) => super.noSuchMethod( Invocation.method( @@ -240,6 +253,7 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { ), returnValueForMissingStub: null, ); + @override _i8.Future install({required _i14.Uint8List? themeArchiveData}) => (super.noSuchMethod( @@ -251,6 +265,7 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override _i8.Future remove({required String? themeId}) => (super.noSuchMethod( Invocation.method( @@ -261,6 +276,7 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override _i8.Future checkDefaultThemesOnStartup() => (super.noSuchMethod( Invocation.method( @@ -270,6 +286,7 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override _i8.Future verifyInstalled({required String? themeId}) => (super.noSuchMethod( @@ -280,6 +297,7 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { ), returnValue: _i8.Future.value(false), ) as _i8.Future); + @override _i8.Future> fetchThemes() => (super.noSuchMethod( @@ -290,6 +308,7 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { returnValue: _i8.Future>.value( <_i12.StackThemeMetaData>[]), ) as _i8.Future>); + @override _i8.Future<_i14.Uint8List> fetchTheme( {required _i12.StackThemeMetaData? themeMetaData}) => @@ -301,6 +320,7 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { ), returnValue: _i8.Future<_i14.Uint8List>.value(_i14.Uint8List(0)), ) as _i8.Future<_i14.Uint8List>); + @override _i13.StackTheme? getTheme({required String? themeId}) => (super.noSuchMethod(Invocation.method( diff --git a/test/widget_tests/trade_card_test.mocks.dart b/test/widget_tests/trade_card_test.mocks.dart index 0f62658a4..a8080d990 100644 --- a/test/widget_tests/trade_card_test.mocks.dart +++ b/test/widget_tests/trade_card_test.mocks.dart @@ -62,6 +62,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { Invocation.getter(#client), ), ) as _i2.HTTP); + @override set client(_i2.HTTP? _client) => super.noSuchMethod( Invocation.setter( @@ -70,6 +71,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get db => (super.noSuchMethod( Invocation.getter(#db), @@ -78,11 +80,13 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { Invocation.getter(#db), ), ) as _i3.MainDB); + @override List<_i5.StackTheme> get installedThemes => (super.noSuchMethod( Invocation.getter(#installedThemes), returnValue: <_i5.StackTheme>[], ) as List<_i5.StackTheme>); + @override void init(_i3.MainDB? db) => super.noSuchMethod( Invocation.method( @@ -91,6 +95,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValueForMissingStub: null, ); + @override _i6.Future install({required _i7.Uint8List? themeArchiveData}) => (super.noSuchMethod( @@ -102,6 +107,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future remove({required String? themeId}) => (super.noSuchMethod( Invocation.method( @@ -112,6 +118,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future checkDefaultThemesOnStartup() => (super.noSuchMethod( Invocation.method( @@ -121,6 +128,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + @override _i6.Future verifyInstalled({required String? themeId}) => (super.noSuchMethod( @@ -131,6 +139,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValue: _i6.Future.value(false), ) as _i6.Future); + @override _i6.Future> fetchThemes() => (super.noSuchMethod( Invocation.method( @@ -140,6 +149,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { returnValue: _i6.Future>.value( <_i4.StackThemeMetaData>[]), ) as _i6.Future>); + @override _i6.Future<_i7.Uint8List> fetchTheme( {required _i4.StackThemeMetaData? themeMetaData}) => @@ -151,6 +161,7 @@ class MockThemeService extends _i1.Mock implements _i4.ThemeService { ), returnValue: _i6.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), ) as _i6.Future<_i7.Uint8List>); + @override _i5.StackTheme? getTheme({required String? themeId}) => (super.noSuchMethod(Invocation.method( @@ -176,6 +187,7 @@ class MockIThemeAssets extends _i1.Mock implements _i5.IThemeAssets { Invocation.getter(#bellNew), ), ) as String); + @override String get buy => (super.noSuchMethod( Invocation.getter(#buy), @@ -184,6 +196,7 @@ class MockIThemeAssets extends _i1.Mock implements _i5.IThemeAssets { Invocation.getter(#buy), ), ) as String); + @override String get exchange => (super.noSuchMethod( Invocation.getter(#exchange), @@ -192,6 +205,7 @@ class MockIThemeAssets extends _i1.Mock implements _i5.IThemeAssets { Invocation.getter(#exchange), ), ) as String); + @override String get personaIncognito => (super.noSuchMethod( Invocation.getter(#personaIncognito), @@ -200,6 +214,7 @@ class MockIThemeAssets extends _i1.Mock implements _i5.IThemeAssets { Invocation.getter(#personaIncognito), ), ) as String); + @override String get personaEasy => (super.noSuchMethod( Invocation.getter(#personaEasy), @@ -208,6 +223,7 @@ class MockIThemeAssets extends _i1.Mock implements _i5.IThemeAssets { Invocation.getter(#personaEasy), ), ) as String); + @override String get stack => (super.noSuchMethod( Invocation.getter(#stack), @@ -216,6 +232,7 @@ class MockIThemeAssets extends _i1.Mock implements _i5.IThemeAssets { Invocation.getter(#stack), ), ) as String); + @override String get stackIcon => (super.noSuchMethod( Invocation.getter(#stackIcon), @@ -224,6 +241,7 @@ class MockIThemeAssets extends _i1.Mock implements _i5.IThemeAssets { Invocation.getter(#stackIcon), ), ) as String); + @override String get receive => (super.noSuchMethod( Invocation.getter(#receive), @@ -232,6 +250,7 @@ class MockIThemeAssets extends _i1.Mock implements _i5.IThemeAssets { Invocation.getter(#receive), ), ) as String); + @override String get receivePending => (super.noSuchMethod( Invocation.getter(#receivePending), @@ -240,6 +259,7 @@ class MockIThemeAssets extends _i1.Mock implements _i5.IThemeAssets { Invocation.getter(#receivePending), ), ) as String); + @override String get receiveCancelled => (super.noSuchMethod( Invocation.getter(#receiveCancelled), @@ -248,6 +268,7 @@ class MockIThemeAssets extends _i1.Mock implements _i5.IThemeAssets { Invocation.getter(#receiveCancelled), ), ) as String); + @override String get send => (super.noSuchMethod( Invocation.getter(#send), @@ -256,6 +277,7 @@ class MockIThemeAssets extends _i1.Mock implements _i5.IThemeAssets { Invocation.getter(#send), ), ) as String); + @override String get sendPending => (super.noSuchMethod( Invocation.getter(#sendPending), @@ -264,6 +286,7 @@ class MockIThemeAssets extends _i1.Mock implements _i5.IThemeAssets { Invocation.getter(#sendPending), ), ) as String); + @override String get sendCancelled => (super.noSuchMethod( Invocation.getter(#sendCancelled), @@ -272,6 +295,7 @@ class MockIThemeAssets extends _i1.Mock implements _i5.IThemeAssets { Invocation.getter(#sendCancelled), ), ) as String); + @override String get themeSelector => (super.noSuchMethod( Invocation.getter(#themeSelector), @@ -280,6 +304,7 @@ class MockIThemeAssets extends _i1.Mock implements _i5.IThemeAssets { Invocation.getter(#themeSelector), ), ) as String); + @override String get themePreview => (super.noSuchMethod( Invocation.getter(#themePreview), @@ -288,6 +313,7 @@ class MockIThemeAssets extends _i1.Mock implements _i5.IThemeAssets { Invocation.getter(#themePreview), ), ) as String); + @override String get txExchange => (super.noSuchMethod( Invocation.getter(#txExchange), @@ -296,6 +322,7 @@ class MockIThemeAssets extends _i1.Mock implements _i5.IThemeAssets { Invocation.getter(#txExchange), ), ) as String); + @override String get txExchangePending => (super.noSuchMethod( Invocation.getter(#txExchangePending), @@ -304,6 +331,7 @@ class MockIThemeAssets extends _i1.Mock implements _i5.IThemeAssets { Invocation.getter(#txExchangePending), ), ) as String); + @override String get txExchangeFailed => (super.noSuchMethod( Invocation.getter(#txExchangeFailed), diff --git a/test/widget_tests/transaction_card_test.mocks.dart b/test/widget_tests/transaction_card_test.mocks.dart index cc4157a10..a5816bdcb 100644 --- a/test/widget_tests/transaction_card_test.mocks.dart +++ b/test/widget_tests/transaction_card_test.mocks.dart @@ -160,6 +160,7 @@ class MockWallets extends _i1.Mock implements _i10.Wallets { Invocation.getter(#nodeService), ), ) as _i2.NodeService); + @override set nodeService(_i2.NodeService? _nodeService) => super.noSuchMethod( Invocation.setter( @@ -168,6 +169,7 @@ class MockWallets extends _i1.Mock implements _i10.Wallets { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get mainDB => (super.noSuchMethod( Invocation.getter(#mainDB), @@ -176,6 +178,7 @@ class MockWallets extends _i1.Mock implements _i10.Wallets { Invocation.getter(#mainDB), ), ) as _i3.MainDB); + @override set mainDB(_i3.MainDB? _mainDB) => super.noSuchMethod( Invocation.setter( @@ -184,11 +187,13 @@ class MockWallets extends _i1.Mock implements _i10.Wallets { ), returnValueForMissingStub: null, ); + @override List<_i5.Wallet<_i4.CryptoCurrency>> get wallets => (super.noSuchMethod( Invocation.getter(#wallets), returnValue: <_i5.Wallet<_i4.CryptoCurrency>>[], ) as List<_i5.Wallet<_i4.CryptoCurrency>>); + @override _i5.Wallet<_i4.CryptoCurrency> getWallet(String? walletId) => (super.noSuchMethod( @@ -204,6 +209,7 @@ class MockWallets extends _i1.Mock implements _i10.Wallets { ), ), ) as _i5.Wallet<_i4.CryptoCurrency>); + @override void addWallet(_i5.Wallet<_i4.CryptoCurrency>? wallet) => super.noSuchMethod( Invocation.method( @@ -212,6 +218,7 @@ class MockWallets extends _i1.Mock implements _i10.Wallets { ), returnValueForMissingStub: null, ); + @override _i11.Future deleteWallet( _i12.WalletInfo? info, @@ -228,6 +235,7 @@ class MockWallets extends _i1.Mock implements _i10.Wallets { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future load( _i14.Prefs? prefs, @@ -244,6 +252,7 @@ class MockWallets extends _i1.Mock implements _i10.Wallets { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future loadAfterStackRestore( _i14.Prefs? prefs, @@ -280,11 +289,13 @@ class MockLocaleService extends _i1.Mock implements _i15.LocaleService { Invocation.getter(#locale), ), ) as String); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i11.Future loadLocale({bool? notify = true}) => (super.noSuchMethod( Invocation.method( @@ -295,6 +306,7 @@ class MockLocaleService extends _i1.Mock implements _i15.LocaleService { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override void addListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -303,6 +315,7 @@ class MockLocaleService extends _i1.Mock implements _i15.LocaleService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -311,6 +324,7 @@ class MockLocaleService extends _i1.Mock implements _i15.LocaleService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -319,6 +333,7 @@ class MockLocaleService extends _i1.Mock implements _i15.LocaleService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -342,11 +357,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { Invocation.getter(#isInitialized), returnValue: false, ) as bool); + @override int get lastUnlockedTimeout => (super.noSuchMethod( Invocation.getter(#lastUnlockedTimeout), returnValue: 0, ) as int); + @override set lastUnlockedTimeout(int? lastUnlockedTimeout) => super.noSuchMethod( Invocation.setter( @@ -355,11 +372,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override int get lastUnlocked => (super.noSuchMethod( Invocation.getter(#lastUnlocked), returnValue: 0, ) as int); + @override set lastUnlocked(int? lastUnlocked) => super.noSuchMethod( Invocation.setter( @@ -368,16 +387,19 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override int get currentNotificationId => (super.noSuchMethod( Invocation.getter(#currentNotificationId), returnValue: 0, ) as int); + @override List get walletIdsSyncOnStartup => (super.noSuchMethod( Invocation.getter(#walletIdsSyncOnStartup), returnValue: [], ) as List); + @override set walletIdsSyncOnStartup(List? walletIdsSyncOnStartup) => super.noSuchMethod( @@ -387,11 +409,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override _i18.SyncingType get syncType => (super.noSuchMethod( Invocation.getter(#syncType), returnValue: _i18.SyncingType.currentWalletOnly, ) as _i18.SyncingType); + @override set syncType(_i18.SyncingType? syncType) => super.noSuchMethod( Invocation.setter( @@ -400,11 +424,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override bool get wifiOnly => (super.noSuchMethod( Invocation.getter(#wifiOnly), returnValue: false, ) as bool); + @override set wifiOnly(bool? wifiOnly) => super.noSuchMethod( Invocation.setter( @@ -413,11 +439,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override bool get showFavoriteWallets => (super.noSuchMethod( Invocation.getter(#showFavoriteWallets), returnValue: false, ) as bool); + @override set showFavoriteWallets(bool? showFavoriteWallets) => super.noSuchMethod( Invocation.setter( @@ -426,6 +454,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override String get language => (super.noSuchMethod( Invocation.getter(#language), @@ -434,6 +463,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { Invocation.getter(#language), ), ) as String); + @override set language(String? newLanguage) => super.noSuchMethod( Invocation.setter( @@ -442,6 +472,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override String get currency => (super.noSuchMethod( Invocation.getter(#currency), @@ -450,6 +481,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { Invocation.getter(#currency), ), ) as String); + @override set currency(String? newCurrency) => super.noSuchMethod( Invocation.setter( @@ -458,11 +490,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override bool get randomizePIN => (super.noSuchMethod( Invocation.getter(#randomizePIN), returnValue: false, ) as bool); + @override set randomizePIN(bool? randomizePIN) => super.noSuchMethod( Invocation.setter( @@ -471,11 +505,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override bool get useBiometrics => (super.noSuchMethod( Invocation.getter(#useBiometrics), returnValue: false, ) as bool); + @override set useBiometrics(bool? useBiometrics) => super.noSuchMethod( Invocation.setter( @@ -484,11 +520,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override bool get hasPin => (super.noSuchMethod( Invocation.getter(#hasPin), returnValue: false, ) as bool); + @override set hasPin(bool? hasPin) => super.noSuchMethod( Invocation.setter( @@ -497,11 +535,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override int get familiarity => (super.noSuchMethod( Invocation.getter(#familiarity), returnValue: 0, ) as int); + @override set familiarity(int? familiarity) => super.noSuchMethod( Invocation.setter( @@ -510,11 +550,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override bool get torKillSwitch => (super.noSuchMethod( Invocation.getter(#torKillSwitch), returnValue: false, ) as bool); + @override set torKillSwitch(bool? torKillswitch) => super.noSuchMethod( Invocation.setter( @@ -523,11 +565,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override bool get showTestNetCoins => (super.noSuchMethod( Invocation.getter(#showTestNetCoins), returnValue: false, ) as bool); + @override set showTestNetCoins(bool? showTestNetCoins) => super.noSuchMethod( Invocation.setter( @@ -536,11 +580,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override bool get isAutoBackupEnabled => (super.noSuchMethod( Invocation.getter(#isAutoBackupEnabled), returnValue: false, ) as bool); + @override set isAutoBackupEnabled(bool? isAutoBackupEnabled) => super.noSuchMethod( Invocation.setter( @@ -549,6 +595,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override set autoBackupLocation(String? autoBackupLocation) => super.noSuchMethod( Invocation.setter( @@ -557,11 +604,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override _i19.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( Invocation.getter(#backupFrequencyType), returnValue: _i19.BackupFrequencyType.everyTenMinutes, ) as _i19.BackupFrequencyType); + @override set backupFrequencyType(_i19.BackupFrequencyType? backupFrequencyType) => super.noSuchMethod( @@ -571,6 +620,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override set lastAutoBackup(DateTime? lastAutoBackup) => super.noSuchMethod( Invocation.setter( @@ -579,11 +629,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override bool get hideBlockExplorerWarning => (super.noSuchMethod( Invocation.getter(#hideBlockExplorerWarning), returnValue: false, ) as bool); + @override set hideBlockExplorerWarning(bool? hideBlockExplorerWarning) => super.noSuchMethod( @@ -593,11 +645,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override bool get gotoWalletOnStartup => (super.noSuchMethod( Invocation.getter(#gotoWalletOnStartup), returnValue: false, ) as bool); + @override set gotoWalletOnStartup(bool? gotoWalletOnStartup) => super.noSuchMethod( Invocation.setter( @@ -606,6 +660,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override set startupWalletId(String? startupWalletId) => super.noSuchMethod( Invocation.setter( @@ -614,11 +669,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override bool get externalCalls => (super.noSuchMethod( Invocation.getter(#externalCalls), returnValue: false, ) as bool); + @override set externalCalls(bool? externalCalls) => super.noSuchMethod( Invocation.setter( @@ -627,11 +684,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override bool get enableCoinControl => (super.noSuchMethod( Invocation.getter(#enableCoinControl), returnValue: false, ) as bool); + @override set enableCoinControl(bool? enableCoinControl) => super.noSuchMethod( Invocation.setter( @@ -640,11 +699,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override bool get enableSystemBrightness => (super.noSuchMethod( Invocation.getter(#enableSystemBrightness), returnValue: false, ) as bool); + @override set enableSystemBrightness(bool? enableSystemBrightness) => super.noSuchMethod( @@ -654,6 +715,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override String get themeId => (super.noSuchMethod( Invocation.getter(#themeId), @@ -662,6 +724,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { Invocation.getter(#themeId), ), ) as String); + @override set themeId(String? themeId) => super.noSuchMethod( Invocation.setter( @@ -670,6 +733,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override String get systemBrightnessLightThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessLightThemeId), @@ -678,6 +742,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { Invocation.getter(#systemBrightnessLightThemeId), ), ) as String); + @override set systemBrightnessLightThemeId(String? systemBrightnessLightThemeId) => super.noSuchMethod( @@ -687,6 +752,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override String get systemBrightnessDarkThemeId => (super.noSuchMethod( Invocation.getter(#systemBrightnessDarkThemeId), @@ -695,6 +761,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { Invocation.getter(#systemBrightnessDarkThemeId), ), ) as String); + @override set systemBrightnessDarkThemeId(String? systemBrightnessDarkThemeId) => super.noSuchMethod( @@ -704,11 +771,13 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override bool get useTor => (super.noSuchMethod( Invocation.getter(#useTor), returnValue: false, ) as bool); + @override set useTor(bool? useTor) => super.noSuchMethod( Invocation.setter( @@ -717,11 +786,43 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + + @override + bool get autoPin => (super.noSuchMethod( + Invocation.getter(#autoPin), + returnValue: false, + ) as bool); + + @override + set autoPin(bool? autoPin) => super.noSuchMethod( + Invocation.setter( + #autoPin, + autoPin, + ), + returnValueForMissingStub: null, + ); + + @override + bool get enableExchange => (super.noSuchMethod( + Invocation.getter(#enableExchange), + returnValue: false, + ) as bool); + + @override + set enableExchange(bool? showExchange) => super.noSuchMethod( + Invocation.setter( + #enableExchange, + showExchange, + ), + returnValueForMissingStub: null, + ); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i11.Future init() => (super.noSuchMethod( Invocation.method( @@ -731,6 +832,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future incrementCurrentNotificationIndex() => (super.noSuchMethod( Invocation.method( @@ -740,6 +842,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future isExternalCallsSet() => (super.noSuchMethod( Invocation.method( @@ -748,6 +851,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValue: _i11.Future.value(false), ) as _i11.Future); + @override _i11.Future saveUserID(String? userId) => (super.noSuchMethod( Invocation.method( @@ -757,6 +861,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future saveSignupEpoch(int? signupEpoch) => (super.noSuchMethod( Invocation.method( @@ -766,6 +871,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i20.AmountUnit amountUnit(_i4.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( @@ -774,6 +880,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValue: _i20.AmountUnit.normal, ) as _i20.AmountUnit); + @override void updateAmountUnit({ required _i4.CryptoCurrency? coin, @@ -790,6 +897,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override int maxDecimals(_i4.CryptoCurrency? coin) => (super.noSuchMethod( Invocation.method( @@ -798,6 +906,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValue: 0, ) as int); + @override void updateMaxDecimals({ required _i4.CryptoCurrency? coin, @@ -814,6 +923,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override _i6.FusionInfo getFusionServerInfo(_i4.CryptoCurrency? coin) => (super.noSuchMethod( @@ -829,6 +939,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), ), ) as _i6.FusionInfo); + @override void setFusionServerInfo( _i4.CryptoCurrency? coin, @@ -844,6 +955,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override void addListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -852,6 +964,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override void removeListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -860,6 +973,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -868,6 +982,7 @@ class MockPrefs extends _i1.Mock implements _i14.Prefs { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -894,6 +1009,7 @@ class MockPriceService extends _i1.Mock implements _i21.PriceService { Invocation.getter(#baseTicker), ), ) as String); + @override set baseTicker(String? _baseTicker) => super.noSuchMethod( Invocation.setter( @@ -902,6 +1018,7 @@ class MockPriceService extends _i1.Mock implements _i21.PriceService { ), returnValueForMissingStub: null, ); + @override Duration get updateInterval => (super.noSuchMethod( Invocation.getter(#updateInterval), @@ -910,17 +1027,20 @@ class MockPriceService extends _i1.Mock implements _i21.PriceService { Invocation.getter(#updateInterval), ), ) as Duration); + @override _i11.Future> get tokenContractAddressesToCheck => (super.noSuchMethod( Invocation.getter(#tokenContractAddressesToCheck), returnValue: _i11.Future>.value({}), ) as _i11.Future>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i7.Tuple2<_i22.Decimal, double> getPrice(_i4.CryptoCurrency? coin) => (super.noSuchMethod( @@ -936,6 +1056,7 @@ class MockPriceService extends _i1.Mock implements _i21.PriceService { ), ), ) as _i7.Tuple2<_i22.Decimal, double>); + @override _i7.Tuple2<_i22.Decimal, double> getTokenPrice(String? contractAddress) => (super.noSuchMethod( @@ -951,6 +1072,7 @@ class MockPriceService extends _i1.Mock implements _i21.PriceService { ), ), ) as _i7.Tuple2<_i22.Decimal, double>); + @override _i11.Future updatePrice() => (super.noSuchMethod( Invocation.method( @@ -960,6 +1082,7 @@ class MockPriceService extends _i1.Mock implements _i21.PriceService { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override void cancel() => super.noSuchMethod( Invocation.method( @@ -968,6 +1091,7 @@ class MockPriceService extends _i1.Mock implements _i21.PriceService { ), returnValueForMissingStub: null, ); + @override void start(bool? rightAway) => super.noSuchMethod( Invocation.method( @@ -976,6 +1100,7 @@ class MockPriceService extends _i1.Mock implements _i21.PriceService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -984,6 +1109,7 @@ class MockPriceService extends _i1.Mock implements _i21.PriceService { ), returnValueForMissingStub: null, ); + @override void addListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -992,6 +1118,7 @@ class MockPriceService extends _i1.Mock implements _i21.PriceService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i17.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -1000,6 +1127,7 @@ class MockPriceService extends _i1.Mock implements _i21.PriceService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -1026,6 +1154,7 @@ class MockThemeService extends _i1.Mock implements _i23.ThemeService { Invocation.getter(#client), ), ) as _i8.HTTP); + @override set client(_i8.HTTP? _client) => super.noSuchMethod( Invocation.setter( @@ -1034,6 +1163,7 @@ class MockThemeService extends _i1.Mock implements _i23.ThemeService { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get db => (super.noSuchMethod( Invocation.getter(#db), @@ -1042,11 +1172,13 @@ class MockThemeService extends _i1.Mock implements _i23.ThemeService { Invocation.getter(#db), ), ) as _i3.MainDB); + @override List<_i24.StackTheme> get installedThemes => (super.noSuchMethod( Invocation.getter(#installedThemes), returnValue: <_i24.StackTheme>[], ) as List<_i24.StackTheme>); + @override void init(_i3.MainDB? db) => super.noSuchMethod( Invocation.method( @@ -1055,6 +1187,7 @@ class MockThemeService extends _i1.Mock implements _i23.ThemeService { ), returnValueForMissingStub: null, ); + @override _i11.Future install({required _i25.Uint8List? themeArchiveData}) => (super.noSuchMethod( @@ -1066,6 +1199,7 @@ class MockThemeService extends _i1.Mock implements _i23.ThemeService { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future remove({required String? themeId}) => (super.noSuchMethod( Invocation.method( @@ -1076,6 +1210,7 @@ class MockThemeService extends _i1.Mock implements _i23.ThemeService { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future checkDefaultThemesOnStartup() => (super.noSuchMethod( Invocation.method( @@ -1085,6 +1220,7 @@ class MockThemeService extends _i1.Mock implements _i23.ThemeService { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future verifyInstalled({required String? themeId}) => (super.noSuchMethod( @@ -1095,6 +1231,7 @@ class MockThemeService extends _i1.Mock implements _i23.ThemeService { ), returnValue: _i11.Future.value(false), ) as _i11.Future); + @override _i11.Future> fetchThemes() => (super.noSuchMethod( @@ -1105,6 +1242,7 @@ class MockThemeService extends _i1.Mock implements _i23.ThemeService { returnValue: _i11.Future>.value( <_i23.StackThemeMetaData>[]), ) as _i11.Future>); + @override _i11.Future<_i25.Uint8List> fetchTheme( {required _i23.StackThemeMetaData? themeMetaData}) => @@ -1116,6 +1254,7 @@ class MockThemeService extends _i1.Mock implements _i23.ThemeService { ), returnValue: _i11.Future<_i25.Uint8List>.value(_i25.Uint8List(0)), ) as _i11.Future<_i25.Uint8List>); + @override _i24.StackTheme? getTheme({required String? themeId}) => (super.noSuchMethod(Invocation.method( @@ -1141,6 +1280,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { Invocation.getter(#isar), ), ) as _i9.Isar); + @override _i11.Future initMainDB({_i9.Isar? mock}) => (super.noSuchMethod( Invocation.method( @@ -1150,6 +1290,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future.value(false), ) as _i11.Future); + @override _i11.Future putWalletInfo(_i12.WalletInfo? walletInfo) => (super.noSuchMethod( @@ -1160,6 +1301,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future updateWalletInfo(_i12.WalletInfo? walletInfo) => (super.noSuchMethod( @@ -1170,6 +1312,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override List<_i26.ContactEntry> getContactEntries() => (super.noSuchMethod( Invocation.method( @@ -1178,6 +1321,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: <_i26.ContactEntry>[], ) as List<_i26.ContactEntry>); + @override _i11.Future deleteContactEntry({required String? id}) => (super.noSuchMethod( @@ -1188,6 +1332,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future.value(false), ) as _i11.Future); + @override _i11.Future isContactEntryExists({required String? id}) => (super.noSuchMethod( @@ -1198,6 +1343,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future.value(false), ) as _i11.Future); + @override _i26.ContactEntry? getContactEntry({required String? id}) => (super.noSuchMethod(Invocation.method( @@ -1205,6 +1351,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { [], {#id: id}, )) as _i26.ContactEntry?); + @override _i11.Future putContactEntry( {required _i26.ContactEntry? contactEntry}) => @@ -1216,6 +1363,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future.value(false), ) as _i11.Future); + @override _i27.TransactionBlockExplorer? getTransactionBlockExplorer( {required _i4.CryptoCurrency? cryptoCurrency}) => @@ -1224,6 +1372,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { [], {#cryptoCurrency: cryptoCurrency}, )) as _i27.TransactionBlockExplorer?); + @override _i11.Future putTransactionBlockExplorer( _i27.TransactionBlockExplorer? explorer) => @@ -1234,6 +1383,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future.value(0), ) as _i11.Future); + @override _i9.QueryBuilder<_i28.Address, _i28.Address, _i9.QAfterWhereClause> getAddresses(String? walletId) => (super.noSuchMethod( @@ -1251,6 +1401,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), ) as _i9 .QueryBuilder<_i28.Address, _i28.Address, _i9.QAfterWhereClause>); + @override _i11.Future putAddress(_i28.Address? address) => (super.noSuchMethod( Invocation.method( @@ -1259,6 +1410,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future.value(0), ) as _i11.Future); + @override _i11.Future> putAddresses(List<_i28.Address>? addresses) => (super.noSuchMethod( @@ -1268,6 +1420,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future>.value([]), ) as _i11.Future>); + @override _i11.Future> updateOrPutAddresses(List<_i28.Address>? addresses) => (super.noSuchMethod( @@ -1277,6 +1430,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future>.value([]), ) as _i11.Future>); + @override _i11.Future<_i28.Address?> getAddress( String? walletId, @@ -1292,6 +1446,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future<_i28.Address?>.value(), ) as _i11.Future<_i28.Address?>); + @override _i11.Future updateAddress( _i28.Address? oldAddress, @@ -1307,6 +1462,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future.value(0), ) as _i11.Future); + @override _i9.QueryBuilder<_i28.Transaction, _i28.Transaction, _i9.QAfterWhereClause> getTransactions(String? walletId) => (super.noSuchMethod( @@ -1324,6 +1480,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), ) as _i9.QueryBuilder<_i28.Transaction, _i28.Transaction, _i9.QAfterWhereClause>); + @override _i11.Future putTransaction(_i28.Transaction? transaction) => (super.noSuchMethod( @@ -1333,6 +1490,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future.value(0), ) as _i11.Future); + @override _i11.Future> putTransactions( List<_i28.Transaction>? transactions) => @@ -1343,6 +1501,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future>.value([]), ) as _i11.Future>); + @override _i11.Future<_i28.Transaction?> getTransaction( String? walletId, @@ -1358,6 +1517,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future<_i28.Transaction?>.value(), ) as _i11.Future<_i28.Transaction?>); + @override _i11.Stream<_i28.Transaction?> watchTransaction({ required int? id, @@ -1374,6 +1534,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Stream<_i28.Transaction?>.empty(), ) as _i11.Stream<_i28.Transaction?>); + @override _i9.QueryBuilder<_i28.UTXO, _i28.UTXO, _i9.QAfterWhereClause> getUTXOs( String? walletId) => @@ -1391,6 +1552,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), ), ) as _i9.QueryBuilder<_i28.UTXO, _i28.UTXO, _i9.QAfterWhereClause>); + @override _i9.QueryBuilder<_i28.UTXO, _i28.UTXO, _i9.QAfterFilterCondition> getUTXOsByAddress( @@ -1418,6 +1580,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), ) as _i9 .QueryBuilder<_i28.UTXO, _i28.UTXO, _i9.QAfterFilterCondition>); + @override _i11.Future putUTXO(_i28.UTXO? utxo) => (super.noSuchMethod( Invocation.method( @@ -1427,6 +1590,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future putUTXOs(List<_i28.UTXO>? utxos) => (super.noSuchMethod( Invocation.method( @@ -1436,6 +1600,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future updateUTXOs( String? walletId, @@ -1451,6 +1616,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future.value(false), ) as _i11.Future); + @override _i11.Stream<_i28.UTXO?> watchUTXO({ required int? id, @@ -1467,6 +1633,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Stream<_i28.UTXO?>.empty(), ) as _i11.Stream<_i28.UTXO?>); + @override _i9.QueryBuilder<_i28.TransactionNote, _i28.TransactionNote, _i9.QAfterWhereClause> getTransactionNotes( @@ -1486,6 +1653,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), ) as _i9.QueryBuilder<_i28.TransactionNote, _i28.TransactionNote, _i9.QAfterWhereClause>); + @override _i11.Future putTransactionNote(_i28.TransactionNote? transactionNote) => (super.noSuchMethod( @@ -1496,6 +1664,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future putTransactionNotes( List<_i28.TransactionNote>? transactionNotes) => @@ -1507,6 +1676,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future<_i28.TransactionNote?> getTransactionNote( String? walletId, @@ -1522,6 +1692,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future<_i28.TransactionNote?>.value(), ) as _i11.Future<_i28.TransactionNote?>); + @override _i11.Stream<_i28.TransactionNote?> watchTransactionNote({ required int? id, @@ -1538,6 +1709,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Stream<_i28.TransactionNote?>.empty(), ) as _i11.Stream<_i28.TransactionNote?>); + @override _i9.QueryBuilder<_i28.AddressLabel, _i28.AddressLabel, _i9.QAfterWhereClause> getAddressLabels(String? walletId) => (super.noSuchMethod( @@ -1555,6 +1727,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), ) as _i9.QueryBuilder<_i28.AddressLabel, _i28.AddressLabel, _i9.QAfterWhereClause>); + @override _i11.Future putAddressLabel(_i28.AddressLabel? addressLabel) => (super.noSuchMethod( @@ -1564,6 +1737,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future.value(0), ) as _i11.Future); + @override int putAddressLabelSync(_i28.AddressLabel? addressLabel) => (super.noSuchMethod( @@ -1573,6 +1747,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: 0, ) as int); + @override _i11.Future putAddressLabels(List<_i28.AddressLabel>? addressLabels) => (super.noSuchMethod( @@ -1583,6 +1758,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future<_i28.AddressLabel?> getAddressLabel( String? walletId, @@ -1598,6 +1774,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future<_i28.AddressLabel?>.value(), ) as _i11.Future<_i28.AddressLabel?>); + @override _i28.AddressLabel? getAddressLabelSync( String? walletId, @@ -1610,6 +1787,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { addressString, ], )) as _i28.AddressLabel?); + @override _i11.Stream<_i28.AddressLabel?> watchAddressLabel({ required int? id, @@ -1626,6 +1804,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Stream<_i28.AddressLabel?>.empty(), ) as _i11.Stream<_i28.AddressLabel?>); + @override _i11.Future updateAddressLabel(_i28.AddressLabel? addressLabel) => (super.noSuchMethod( @@ -1635,6 +1814,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future.value(0), ) as _i11.Future); + @override _i11.Future deleteWalletBlockchainData(String? walletId) => (super.noSuchMethod( @@ -1645,6 +1825,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future deleteAddressLabels(String? walletId) => (super.noSuchMethod( @@ -1655,6 +1836,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future deleteTransactionNotes(String? walletId) => (super.noSuchMethod( @@ -1665,6 +1847,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future addNewTransactionData( List<_i7.Tuple2<_i28.Transaction, _i28.Address?>>? transactionsData, @@ -1681,6 +1864,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future> updateOrPutTransactionV2s( List<_i29.TransactionV2>? transactions) => @@ -1691,6 +1875,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future>.value([]), ) as _i11.Future>); + @override _i9.QueryBuilder<_i28.EthContract, _i28.EthContract, _i9.QWhere> getEthContracts() => (super.noSuchMethod( @@ -1708,6 +1893,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), ) as _i9 .QueryBuilder<_i28.EthContract, _i28.EthContract, _i9.QWhere>); + @override _i11.Future<_i28.EthContract?> getEthContract(String? contractAddress) => (super.noSuchMethod( @@ -1717,12 +1903,14 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future<_i28.EthContract?>.value(), ) as _i11.Future<_i28.EthContract?>); + @override _i28.EthContract? getEthContractSync(String? contractAddress) => (super.noSuchMethod(Invocation.method( #getEthContractSync, [contractAddress], )) as _i28.EthContract?); + @override _i11.Future putEthContract(_i28.EthContract? contract) => (super.noSuchMethod( @@ -1732,6 +1920,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { ), returnValue: _i11.Future.value(0), ) as _i11.Future); + @override _i11.Future putEthContracts(List<_i28.EthContract>? contracts) => (super.noSuchMethod( @@ -1742,6 +1931,7 @@ class MockMainDB extends _i1.Mock implements _i3.MainDB { returnValue: _i11.Future.value(), returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); + @override _i11.Future getHighestUsedMintIndex({required String? walletId}) => (super.noSuchMethod( @@ -1770,6 +1960,7 @@ class MockIThemeAssets extends _i1.Mock implements _i24.IThemeAssets { Invocation.getter(#bellNew), ), ) as String); + @override String get buy => (super.noSuchMethod( Invocation.getter(#buy), @@ -1778,6 +1969,7 @@ class MockIThemeAssets extends _i1.Mock implements _i24.IThemeAssets { Invocation.getter(#buy), ), ) as String); + @override String get exchange => (super.noSuchMethod( Invocation.getter(#exchange), @@ -1786,6 +1978,7 @@ class MockIThemeAssets extends _i1.Mock implements _i24.IThemeAssets { Invocation.getter(#exchange), ), ) as String); + @override String get personaIncognito => (super.noSuchMethod( Invocation.getter(#personaIncognito), @@ -1794,6 +1987,7 @@ class MockIThemeAssets extends _i1.Mock implements _i24.IThemeAssets { Invocation.getter(#personaIncognito), ), ) as String); + @override String get personaEasy => (super.noSuchMethod( Invocation.getter(#personaEasy), @@ -1802,6 +1996,7 @@ class MockIThemeAssets extends _i1.Mock implements _i24.IThemeAssets { Invocation.getter(#personaEasy), ), ) as String); + @override String get stack => (super.noSuchMethod( Invocation.getter(#stack), @@ -1810,6 +2005,7 @@ class MockIThemeAssets extends _i1.Mock implements _i24.IThemeAssets { Invocation.getter(#stack), ), ) as String); + @override String get stackIcon => (super.noSuchMethod( Invocation.getter(#stackIcon), @@ -1818,6 +2014,7 @@ class MockIThemeAssets extends _i1.Mock implements _i24.IThemeAssets { Invocation.getter(#stackIcon), ), ) as String); + @override String get receive => (super.noSuchMethod( Invocation.getter(#receive), @@ -1826,6 +2023,7 @@ class MockIThemeAssets extends _i1.Mock implements _i24.IThemeAssets { Invocation.getter(#receive), ), ) as String); + @override String get receivePending => (super.noSuchMethod( Invocation.getter(#receivePending), @@ -1834,6 +2032,7 @@ class MockIThemeAssets extends _i1.Mock implements _i24.IThemeAssets { Invocation.getter(#receivePending), ), ) as String); + @override String get receiveCancelled => (super.noSuchMethod( Invocation.getter(#receiveCancelled), @@ -1842,6 +2041,7 @@ class MockIThemeAssets extends _i1.Mock implements _i24.IThemeAssets { Invocation.getter(#receiveCancelled), ), ) as String); + @override String get send => (super.noSuchMethod( Invocation.getter(#send), @@ -1850,6 +2050,7 @@ class MockIThemeAssets extends _i1.Mock implements _i24.IThemeAssets { Invocation.getter(#send), ), ) as String); + @override String get sendPending => (super.noSuchMethod( Invocation.getter(#sendPending), @@ -1858,6 +2059,7 @@ class MockIThemeAssets extends _i1.Mock implements _i24.IThemeAssets { Invocation.getter(#sendPending), ), ) as String); + @override String get sendCancelled => (super.noSuchMethod( Invocation.getter(#sendCancelled), @@ -1866,6 +2068,7 @@ class MockIThemeAssets extends _i1.Mock implements _i24.IThemeAssets { Invocation.getter(#sendCancelled), ), ) as String); + @override String get themeSelector => (super.noSuchMethod( Invocation.getter(#themeSelector), @@ -1874,6 +2077,7 @@ class MockIThemeAssets extends _i1.Mock implements _i24.IThemeAssets { Invocation.getter(#themeSelector), ), ) as String); + @override String get themePreview => (super.noSuchMethod( Invocation.getter(#themePreview), @@ -1882,6 +2086,7 @@ class MockIThemeAssets extends _i1.Mock implements _i24.IThemeAssets { Invocation.getter(#themePreview), ), ) as String); + @override String get txExchange => (super.noSuchMethod( Invocation.getter(#txExchange), @@ -1890,6 +2095,7 @@ class MockIThemeAssets extends _i1.Mock implements _i24.IThemeAssets { Invocation.getter(#txExchange), ), ) as String); + @override String get txExchangePending => (super.noSuchMethod( Invocation.getter(#txExchangePending), @@ -1898,6 +2104,7 @@ class MockIThemeAssets extends _i1.Mock implements _i24.IThemeAssets { Invocation.getter(#txExchangePending), ), ) as String); + @override String get txExchangeFailed => (super.noSuchMethod( Invocation.getter(#txExchangeFailed), diff --git a/test/widget_tests/wallet_card_test.mocks.dart b/test/widget_tests/wallet_card_test.mocks.dart index 1c8afe8db..80c03af35 100644 --- a/test/widget_tests/wallet_card_test.mocks.dart +++ b/test/widget_tests/wallet_card_test.mocks.dart @@ -94,6 +94,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { Invocation.getter(#nodeService), ), ) as _i2.NodeService); + @override set nodeService(_i2.NodeService? _nodeService) => super.noSuchMethod( Invocation.setter( @@ -102,6 +103,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get mainDB => (super.noSuchMethod( Invocation.getter(#mainDB), @@ -110,6 +112,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { Invocation.getter(#mainDB), ), ) as _i3.MainDB); + @override set mainDB(_i3.MainDB? _mainDB) => super.noSuchMethod( Invocation.setter( @@ -118,11 +121,13 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { ), returnValueForMissingStub: null, ); + @override List<_i5.Wallet<_i4.CryptoCurrency>> get wallets => (super.noSuchMethod( Invocation.getter(#wallets), returnValue: <_i5.Wallet<_i4.CryptoCurrency>>[], ) as List<_i5.Wallet<_i4.CryptoCurrency>>); + @override _i5.Wallet<_i4.CryptoCurrency> getWallet(String? walletId) => (super.noSuchMethod( @@ -138,6 +143,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { ), ), ) as _i5.Wallet<_i4.CryptoCurrency>); + @override void addWallet(_i5.Wallet<_i4.CryptoCurrency>? wallet) => super.noSuchMethod( Invocation.method( @@ -146,6 +152,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { ), returnValueForMissingStub: null, ); + @override _i8.Future deleteWallet( _i9.WalletInfo? info, @@ -162,6 +169,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override _i8.Future load( _i11.Prefs? prefs, @@ -178,6 +186,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override _i8.Future loadAfterStackRestore( _i11.Prefs? prefs, @@ -214,11 +223,13 @@ class MockLocaleService extends _i1.Mock implements _i12.LocaleService { Invocation.getter(#locale), ), ) as String); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i8.Future loadLocale({bool? notify = true}) => (super.noSuchMethod( Invocation.method( @@ -229,6 +240,7 @@ class MockLocaleService extends _i1.Mock implements _i12.LocaleService { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override void addListener(_i14.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -237,6 +249,7 @@ class MockLocaleService extends _i1.Mock implements _i12.LocaleService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i14.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -245,6 +258,7 @@ class MockLocaleService extends _i1.Mock implements _i12.LocaleService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -253,6 +267,7 @@ class MockLocaleService extends _i1.Mock implements _i12.LocaleService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( @@ -279,6 +294,7 @@ class MockThemeService extends _i1.Mock implements _i15.ThemeService { Invocation.getter(#client), ), ) as _i6.HTTP); + @override set client(_i6.HTTP? _client) => super.noSuchMethod( Invocation.setter( @@ -287,6 +303,7 @@ class MockThemeService extends _i1.Mock implements _i15.ThemeService { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get db => (super.noSuchMethod( Invocation.getter(#db), @@ -295,11 +312,13 @@ class MockThemeService extends _i1.Mock implements _i15.ThemeService { Invocation.getter(#db), ), ) as _i3.MainDB); + @override List<_i16.StackTheme> get installedThemes => (super.noSuchMethod( Invocation.getter(#installedThemes), returnValue: <_i16.StackTheme>[], ) as List<_i16.StackTheme>); + @override void init(_i3.MainDB? db) => super.noSuchMethod( Invocation.method( @@ -308,6 +327,7 @@ class MockThemeService extends _i1.Mock implements _i15.ThemeService { ), returnValueForMissingStub: null, ); + @override _i8.Future install({required _i17.Uint8List? themeArchiveData}) => (super.noSuchMethod( @@ -319,6 +339,7 @@ class MockThemeService extends _i1.Mock implements _i15.ThemeService { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override _i8.Future remove({required String? themeId}) => (super.noSuchMethod( Invocation.method( @@ -329,6 +350,7 @@ class MockThemeService extends _i1.Mock implements _i15.ThemeService { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override _i8.Future checkDefaultThemesOnStartup() => (super.noSuchMethod( Invocation.method( @@ -338,6 +360,7 @@ class MockThemeService extends _i1.Mock implements _i15.ThemeService { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override _i8.Future verifyInstalled({required String? themeId}) => (super.noSuchMethod( @@ -348,6 +371,7 @@ class MockThemeService extends _i1.Mock implements _i15.ThemeService { ), returnValue: _i8.Future.value(false), ) as _i8.Future); + @override _i8.Future> fetchThemes() => (super.noSuchMethod( @@ -358,6 +382,7 @@ class MockThemeService extends _i1.Mock implements _i15.ThemeService { returnValue: _i8.Future>.value( <_i15.StackThemeMetaData>[]), ) as _i8.Future>); + @override _i8.Future<_i17.Uint8List> fetchTheme( {required _i15.StackThemeMetaData? themeMetaData}) => @@ -369,6 +394,7 @@ class MockThemeService extends _i1.Mock implements _i15.ThemeService { ), returnValue: _i8.Future<_i17.Uint8List>.value(_i17.Uint8List(0)), ) as _i8.Future<_i17.Uint8List>); + @override _i16.StackTheme? getTheme({required String? themeId}) => (super.noSuchMethod(Invocation.method( diff --git a/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.mocks.dart b/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.mocks.dart index 2084bd833..d7fea45d4 100644 --- a/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.mocks.dart +++ b/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.mocks.dart @@ -90,6 +90,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { Invocation.getter(#nodeService), ), ) as _i2.NodeService); + @override set nodeService(_i2.NodeService? _nodeService) => super.noSuchMethod( Invocation.setter( @@ -98,6 +99,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get mainDB => (super.noSuchMethod( Invocation.getter(#mainDB), @@ -106,6 +108,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { Invocation.getter(#mainDB), ), ) as _i3.MainDB); + @override set mainDB(_i3.MainDB? _mainDB) => super.noSuchMethod( Invocation.setter( @@ -114,11 +117,13 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { ), returnValueForMissingStub: null, ); + @override List<_i5.Wallet<_i4.CryptoCurrency>> get wallets => (super.noSuchMethod( Invocation.getter(#wallets), returnValue: <_i5.Wallet<_i4.CryptoCurrency>>[], ) as List<_i5.Wallet<_i4.CryptoCurrency>>); + @override _i5.Wallet<_i4.CryptoCurrency> getWallet(String? walletId) => (super.noSuchMethod( @@ -134,6 +139,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { ), ), ) as _i5.Wallet<_i4.CryptoCurrency>); + @override void addWallet(_i5.Wallet<_i4.CryptoCurrency>? wallet) => super.noSuchMethod( Invocation.method( @@ -142,6 +148,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { ), returnValueForMissingStub: null, ); + @override _i8.Future deleteWallet( _i9.WalletInfo? info, @@ -158,6 +165,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override _i8.Future load( _i10.Prefs? prefs, @@ -174,6 +182,7 @@ class MockWallets extends _i1.Mock implements _i7.Wallets { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override _i8.Future loadAfterStackRestore( _i10.Prefs? prefs, @@ -210,21 +219,25 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { Invocation.getter(#secureStorageInterface), ), ) as _i6.SecureStorageInterface); + @override List<_i11.NodeModel> get primaryNodes => (super.noSuchMethod( Invocation.getter(#primaryNodes), returnValue: <_i11.NodeModel>[], ) as List<_i11.NodeModel>); + @override List<_i11.NodeModel> get nodes => (super.noSuchMethod( Invocation.getter(#nodes), returnValue: <_i11.NodeModel>[], ) as List<_i11.NodeModel>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i8.Future updateDefaults() => (super.noSuchMethod( Invocation.method( @@ -234,6 +247,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override _i8.Future setPrimaryNodeFor({ required _i4.CryptoCurrency? coin, @@ -253,6 +267,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override _i11.NodeModel? getPrimaryNodeFor({required _i4.CryptoCurrency? currency}) => (super.noSuchMethod(Invocation.method( @@ -260,6 +275,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { [], {#currency: currency}, )) as _i11.NodeModel?); + @override List<_i11.NodeModel> getNodesFor(_i4.CryptoCurrency? coin) => (super.noSuchMethod( @@ -269,6 +285,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValue: <_i11.NodeModel>[], ) as List<_i11.NodeModel>); + @override _i11.NodeModel? getNodeById({required String? id}) => (super.noSuchMethod(Invocation.method( @@ -276,6 +293,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { [], {#id: id}, )) as _i11.NodeModel?); + @override List<_i11.NodeModel> failoverNodesFor( {required _i4.CryptoCurrency? currency}) => @@ -287,6 +305,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValue: <_i11.NodeModel>[], ) as List<_i11.NodeModel>); + @override _i8.Future add( _i11.NodeModel? node, @@ -305,6 +324,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override _i8.Future delete( String? id, @@ -321,6 +341,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override _i8.Future setEnabledState( String? id, @@ -339,6 +360,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override _i8.Future edit( _i11.NodeModel? editedNode, @@ -357,6 +379,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override _i8.Future updateCommunityNodes() => (super.noSuchMethod( Invocation.method( @@ -366,6 +389,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override void addListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -374,6 +398,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -382,6 +407,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -390,6 +416,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/test/widget_tests/wallet_info_row/wallet_info_row_test.mocks.dart b/test/widget_tests/wallet_info_row/wallet_info_row_test.mocks.dart index 057284d51..88f854589 100644 --- a/test/widget_tests/wallet_info_row/wallet_info_row_test.mocks.dart +++ b/test/widget_tests/wallet_info_row/wallet_info_row_test.mocks.dart @@ -104,6 +104,7 @@ class MockWallets extends _i1.Mock implements _i8.Wallets { Invocation.getter(#nodeService), ), ) as _i2.NodeService); + @override set nodeService(_i2.NodeService? _nodeService) => super.noSuchMethod( Invocation.setter( @@ -112,6 +113,7 @@ class MockWallets extends _i1.Mock implements _i8.Wallets { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get mainDB => (super.noSuchMethod( Invocation.getter(#mainDB), @@ -120,6 +122,7 @@ class MockWallets extends _i1.Mock implements _i8.Wallets { Invocation.getter(#mainDB), ), ) as _i3.MainDB); + @override set mainDB(_i3.MainDB? _mainDB) => super.noSuchMethod( Invocation.setter( @@ -128,11 +131,13 @@ class MockWallets extends _i1.Mock implements _i8.Wallets { ), returnValueForMissingStub: null, ); + @override List<_i5.Wallet<_i4.CryptoCurrency>> get wallets => (super.noSuchMethod( Invocation.getter(#wallets), returnValue: <_i5.Wallet<_i4.CryptoCurrency>>[], ) as List<_i5.Wallet<_i4.CryptoCurrency>>); + @override _i5.Wallet<_i4.CryptoCurrency> getWallet(String? walletId) => (super.noSuchMethod( @@ -148,6 +153,7 @@ class MockWallets extends _i1.Mock implements _i8.Wallets { ), ), ) as _i5.Wallet<_i4.CryptoCurrency>); + @override void addWallet(_i5.Wallet<_i4.CryptoCurrency>? wallet) => super.noSuchMethod( Invocation.method( @@ -156,6 +162,7 @@ class MockWallets extends _i1.Mock implements _i8.Wallets { ), returnValueForMissingStub: null, ); + @override _i9.Future deleteWallet( _i10.WalletInfo? info, @@ -172,6 +179,7 @@ class MockWallets extends _i1.Mock implements _i8.Wallets { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future load( _i11.Prefs? prefs, @@ -188,6 +196,7 @@ class MockWallets extends _i1.Mock implements _i8.Wallets { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future loadAfterStackRestore( _i11.Prefs? prefs, @@ -224,6 +233,7 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { Invocation.getter(#client), ), ) as _i6.HTTP); + @override set client(_i6.HTTP? _client) => super.noSuchMethod( Invocation.setter( @@ -232,6 +242,7 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { ), returnValueForMissingStub: null, ); + @override _i3.MainDB get db => (super.noSuchMethod( Invocation.getter(#db), @@ -240,11 +251,13 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { Invocation.getter(#db), ), ) as _i3.MainDB); + @override List<_i13.StackTheme> get installedThemes => (super.noSuchMethod( Invocation.getter(#installedThemes), returnValue: <_i13.StackTheme>[], ) as List<_i13.StackTheme>); + @override void init(_i3.MainDB? db) => super.noSuchMethod( Invocation.method( @@ -253,6 +266,7 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { ), returnValueForMissingStub: null, ); + @override _i9.Future install({required _i14.Uint8List? themeArchiveData}) => (super.noSuchMethod( @@ -264,6 +278,7 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future remove({required String? themeId}) => (super.noSuchMethod( Invocation.method( @@ -274,6 +289,7 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future checkDefaultThemesOnStartup() => (super.noSuchMethod( Invocation.method( @@ -283,6 +299,7 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future verifyInstalled({required String? themeId}) => (super.noSuchMethod( @@ -293,6 +310,7 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { ), returnValue: _i9.Future.value(false), ) as _i9.Future); + @override _i9.Future> fetchThemes() => (super.noSuchMethod( @@ -303,6 +321,7 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { returnValue: _i9.Future>.value( <_i12.StackThemeMetaData>[]), ) as _i9.Future>); + @override _i9.Future<_i14.Uint8List> fetchTheme( {required _i12.StackThemeMetaData? themeMetaData}) => @@ -314,6 +333,7 @@ class MockThemeService extends _i1.Mock implements _i12.ThemeService { ), returnValue: _i9.Future<_i14.Uint8List>.value(_i14.Uint8List(0)), ) as _i9.Future<_i14.Uint8List>); + @override _i13.StackTheme? getTheme({required String? themeId}) => (super.noSuchMethod(Invocation.method( @@ -339,21 +359,25 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { Invocation.getter(#secureStorageInterface), ), ) as _i7.SecureStorageInterface); + @override List<_i15.NodeModel> get primaryNodes => (super.noSuchMethod( Invocation.getter(#primaryNodes), returnValue: <_i15.NodeModel>[], ) as List<_i15.NodeModel>); + @override List<_i15.NodeModel> get nodes => (super.noSuchMethod( Invocation.getter(#nodes), returnValue: <_i15.NodeModel>[], ) as List<_i15.NodeModel>); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); + @override _i9.Future updateDefaults() => (super.noSuchMethod( Invocation.method( @@ -363,6 +387,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future setPrimaryNodeFor({ required _i4.CryptoCurrency? coin, @@ -382,6 +407,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i15.NodeModel? getPrimaryNodeFor({required _i4.CryptoCurrency? currency}) => (super.noSuchMethod(Invocation.method( @@ -389,6 +415,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { [], {#currency: currency}, )) as _i15.NodeModel?); + @override List<_i15.NodeModel> getNodesFor(_i4.CryptoCurrency? coin) => (super.noSuchMethod( @@ -398,6 +425,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValue: <_i15.NodeModel>[], ) as List<_i15.NodeModel>); + @override _i15.NodeModel? getNodeById({required String? id}) => (super.noSuchMethod(Invocation.method( @@ -405,6 +433,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { [], {#id: id}, )) as _i15.NodeModel?); + @override List<_i15.NodeModel> failoverNodesFor( {required _i4.CryptoCurrency? currency}) => @@ -416,6 +445,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValue: <_i15.NodeModel>[], ) as List<_i15.NodeModel>); + @override _i9.Future add( _i15.NodeModel? node, @@ -434,6 +464,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future delete( String? id, @@ -450,6 +481,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future setEnabledState( String? id, @@ -468,6 +500,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future edit( _i15.NodeModel? editedNode, @@ -486,6 +519,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future updateCommunityNodes() => (super.noSuchMethod( Invocation.method( @@ -495,6 +529,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override void addListener(_i16.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -503,6 +538,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValueForMissingStub: null, ); + @override void removeListener(_i16.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -511,6 +547,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -519,6 +556,7 @@ class MockNodeService extends _i1.Mock implements _i2.NodeService { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index d8d4b219f..74ef33c4e 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,11 +6,14 @@ #include "generated_plugin_registrant.h" +#include #include +#include #include #include #include #include +#include #include #include #include @@ -19,8 +22,12 @@ #include void RegisterPlugins(flutter::PluginRegistry* registry) { + CameraWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("CameraWindows")); ConnectivityPlusWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); + CsMoneroFlutterLibsWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("CsMoneroFlutterLibsWindowsPluginCApi")); DesktopDropPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("DesktopDropPlugin")); FlutterLibepiccashPluginCApiRegisterWithRegistrar( @@ -29,6 +36,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); IsarFlutterLibsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("IsarFlutterLibsPlugin")); + LocalAuthPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("LocalAuthPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); SharePlusWindowsPluginCApiRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b00cd149f..2f370e685 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,11 +3,14 @@ # list(APPEND FLUTTER_PLUGIN_LIST + camera_windows connectivity_plus + cs_monero_flutter_libs_windows desktop_drop flutter_libepiccash flutter_secure_storage_windows isar_flutter_libs + local_auth_windows permission_handler_windows share_plus sqlite3_flutter_libs