diff --git a/.github/workflows/cache_dependencies.yml b/.github/workflows/cache_dependencies.yml index bf0d0f7bc..4654ac033 100644 --- a/.github/workflows/cache_dependencies.yml +++ b/.github/workflows/cache_dependencies.yml @@ -46,6 +46,7 @@ jobs: /opt/android/cake_wallet/cw_monero/android/.cxx /opt/android/cake_wallet/cw_monero/ios/External /opt/android/cake_wallet/cw_shared_external/ios/External + /opt/android/cake_wallet/scripts/monero_c/release key: ${{ hashFiles('**/build_monero.sh', '**/build_haven.sh', '**/monero_api.cpp') }} - if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }} diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 5c8ea82a7..99a45287f 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -27,18 +27,25 @@ jobs: if: github.event_name != 'pull_request' run: echo "BRANCH_NAME=${{ github.event.inputs.branch }}" >> $GITHUB_ENV - - name: Free Up GitHub Actions Ubuntu Runner Disk Space - run: | - sudo rm -rf /usr/share/dotnet - sudo rm -rf /opt/ghc - sudo rm -rf "/usr/local/share/boost" - sudo rm -rf "$AGENT_TOOLSDIRECTORY" + - name: Free Disk Space (Ubuntu) + uses: insightsengineering/disk-space-reclaimer@v1 + with: + tools-cache: true + android: false + dotnet: true + haskell: true + large-packages: true + swap-storage: true + docker-images: true - uses: actions/checkout@v2 - uses: actions/setup-java@v1 with: java-version: "11.x" - + - name: Configure placeholder git details + run: | + git config --global user.email "CI@cakewallet.com" + git config --global user.name "Cake Github Actions" - name: Flutter action uses: subosito/flutter-action@v1 with: @@ -72,7 +79,8 @@ jobs: /opt/android/cake_wallet/cw_monero/android/.cxx /opt/android/cake_wallet/cw_monero/ios/External /opt/android/cake_wallet/cw_shared_external/ios/External - key: ${{ hashFiles('**/build_monero.sh', '**/build_haven.sh', '**/monero_api.cpp') }} + /opt/android/cake_wallet/scripts/monero_c/release + key: ${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh', '**/build_haven.sh', '**/monero_api.cpp') }} - if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }} name: Generate Externals diff --git a/.gitignore b/.gitignore index d0952ca98..77441e66f 100644 --- a/.gitignore +++ b/.gitignore @@ -136,6 +136,7 @@ lib/nano/nano.dart lib/polygon/polygon.dart lib/solana/solana.dart lib/tron/tron.dart +lib/wownero/wownero.dart ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_180.png ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_120.png @@ -156,6 +157,7 @@ assets/images/app_logo.png macos/Runner/Info.plist macos/Runner/DebugProfile.entitlements macos/Runner/Release.entitlements +macos/Runner/Runner.entitlements lib/core/secure_storage.dart macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png @@ -166,3 +168,9 @@ macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png macos/Runner/Configs/AppInfo.xcconfig + +# Monero.dart (Monero_C) +scripts/monero_c +# iOS generated framework bin +ios/MoneroWallet.framework/MoneroWallet +ios/WowneroWallet.framework/WowneroWallet diff --git a/.metadata b/.metadata index cdddb9350..7d00ca21a 100644 --- a/.metadata +++ b/.metadata @@ -1,11 +1,11 @@ # This file tracks properties of this Flutter project. # Used by Flutter tool to assess capabilities and perform upgrades etc. # -# This file should be version controlled. +# This file should be version controlled and should not be manually edited. version: - revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 - channel: stable + revision: "367f9ea16bfae1ca451b9cc27c1366870b187ae2" + channel: "stable" project_type: app @@ -13,11 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 - base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 - - platform: macos - create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 - base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + create_revision: 367f9ea16bfae1ca451b9cc27c1366870b187ae2 + base_revision: 367f9ea16bfae1ca451b9cc27c1366870b187ae2 + - platform: windows + create_revision: 367f9ea16bfae1ca451b9cc27c1366870b187ae2 + base_revision: 367f9ea16bfae1ca451b9cc27c1366870b187ae2 # User provided section diff --git a/android/app/src/main/AndroidManifestBase.xml b/android/app/src/main/AndroidManifestBase.xml index 57462099c..b03c8a925 100644 --- a/android/app/src/main/AndroidManifestBase.xml +++ b/android/app/src/main/AndroidManifestBase.xml @@ -38,7 +38,8 @@ android:fullBackupContent="false" android:versionCode="__versionCode__" android:versionName="__versionName__" - android:requestLegacyExternalStorage="true"> + android:requestLegacyExternalStorage="true" + android:extractNativeLibs="true"> + + + diff --git a/cw_monero/ios/Assets/.gitkeep b/android/app/src/main/jniLibs/arm64-v8a/.gitkeep similarity index 100% rename from cw_monero/ios/Assets/.gitkeep rename to android/app/src/main/jniLibs/arm64-v8a/.gitkeep diff --git a/android/app/src/main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so b/android/app/src/main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so new file mode 120000 index 000000000..6cdcd70a2 --- /dev/null +++ b/android/app/src/main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so @@ -0,0 +1 @@ +../../../../../../scripts/monero_c/release/monero/aarch64-linux-android_libwallet2_api_c.so \ No newline at end of file diff --git a/android/app/src/main/jniLibs/arm64-v8a/libwownero_libwallet2_api_c.so b/android/app/src/main/jniLibs/arm64-v8a/libwownero_libwallet2_api_c.so new file mode 120000 index 000000000..8f6150ee3 --- /dev/null +++ b/android/app/src/main/jniLibs/arm64-v8a/libwownero_libwallet2_api_c.so @@ -0,0 +1 @@ +../../../../../../scripts/monero_c/release/wownero/aarch64-linux-android_libwallet2_api_c.so \ No newline at end of file diff --git a/android/app/src/main/jniLibs/armeabi-v7a/.gitkeep b/android/app/src/main/jniLibs/armeabi-v7a/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/android/app/src/main/jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so b/android/app/src/main/jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so new file mode 120000 index 000000000..c0d56dd3b --- /dev/null +++ b/android/app/src/main/jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so @@ -0,0 +1 @@ +../../../../../../scripts/monero_c/release/monero/armv7a-linux-androideabi_libwallet2_api_c.so \ No newline at end of file diff --git a/android/app/src/main/jniLibs/armeabi-v7a/libwownero_libwallet2_api_c.so b/android/app/src/main/jniLibs/armeabi-v7a/libwownero_libwallet2_api_c.so new file mode 120000 index 000000000..5a71e87b1 --- /dev/null +++ b/android/app/src/main/jniLibs/armeabi-v7a/libwownero_libwallet2_api_c.so @@ -0,0 +1 @@ +../../../../../../scripts/monero_c/release/wownero/armv7a-linux-androideabi_libwallet2_api_c.so \ No newline at end of file diff --git a/android/app/src/main/jniLibs/x86/.gitkeep b/android/app/src/main/jniLibs/x86/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/android/app/src/main/jniLibs/x86_64/.gitkeep b/android/app/src/main/jniLibs/x86_64/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/android/app/src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so b/android/app/src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so new file mode 120000 index 000000000..654be50b9 --- /dev/null +++ b/android/app/src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so @@ -0,0 +1 @@ +../../../../../../scripts/monero_c/release/monero/x86_64-linux-android_libwallet2_api_c.so \ No newline at end of file diff --git a/android/app/src/main/jniLibs/x86_64/libwownero_libwallet2_api_c.so b/android/app/src/main/jniLibs/x86_64/libwownero_libwallet2_api_c.so new file mode 120000 index 000000000..bb3da908f --- /dev/null +++ b/android/app/src/main/jniLibs/x86_64/libwownero_libwallet2_api_c.so @@ -0,0 +1 @@ +../../../../../../scripts/monero_c/release/wownero/x86_64-linux-android_libwallet2_api_c.so \ No newline at end of file diff --git a/assets/bitcoin_electrum_server_list.yml b/assets/bitcoin_electrum_server_list.yml index 2b6649271..8b734a7bb 100644 --- a/assets/bitcoin_electrum_server_list.yml +++ b/assets/bitcoin_electrum_server_list.yml @@ -1,2 +1,8 @@ - - uri: electrum.cakewallet.com:50002 \ No newline at end of file + uri: electrum.cakewallet.com:50002 + useSSL: true +- + uri: btc-electrum.cakewallet.com:50002 + isDefault: true +- + uri: electrs.cakewallet.com:50001 diff --git a/assets/images/wownero_icon.png b/assets/images/wownero_icon.png new file mode 100644 index 000000000..a3da77b9e Binary files /dev/null and b/assets/images/wownero_icon.png differ diff --git a/assets/images/wownero_menu.png b/assets/images/wownero_menu.png new file mode 100644 index 000000000..a3da77b9e Binary files /dev/null and b/assets/images/wownero_menu.png differ diff --git a/assets/text/Monerocom_Release_Notes.txt b/assets/text/Monerocom_Release_Notes.txt index 34f805b90..551d775ef 100644 --- a/assets/text/Monerocom_Release_Notes.txt +++ b/assets/text/Monerocom_Release_Notes.txt @@ -1,2 +1,2 @@ -In-app Cake Pay is Back -Bug fixes and generic enhancements \ No newline at end of file +Monero enhancements +Bug fixes \ No newline at end of file diff --git a/assets/text/Release_Notes.txt b/assets/text/Release_Notes.txt index dcaf59665..551d775ef 100644 --- a/assets/text/Release_Notes.txt +++ b/assets/text/Release_Notes.txt @@ -1,3 +1,2 @@ -In-app Cake Pay is Back -Bitcoin nodes stability enhancements -Bug fixes and generic enhancements \ No newline at end of file +Monero enhancements +Bug fixes \ No newline at end of file diff --git a/assets/wownero_node_list.yml b/assets/wownero_node_list.yml new file mode 100644 index 000000000..a4873b3e4 --- /dev/null +++ b/assets/wownero_node_list.yml @@ -0,0 +1,12 @@ +- + uri: node3.monerodevs.org:34568 + is_default: true + useSSL: false +- + uri: node2.monerodevs.org:34568 + is_default: false + useSSL: false +- + uri: node.monerodevs.org:34568 + is_default: false + useSSL: false diff --git a/build-guide-win.md b/build-guide-win.md new file mode 100644 index 000000000..6ace961af --- /dev/null +++ b/build-guide-win.md @@ -0,0 +1,38 @@ +# Building CakeWallet for Windows + +## Requirements and Setup + +The following are the system requirements to build CakeWallet for your Windows PC. + +``` +Windows 10 or later (64-bit), x86-64 based +Flutter 3 or above +``` + +## Building CakeWallet on Windows + +These steps will help you configure and execute a build of CakeWallet from its source code. + +### 1. Installing Package Dependencies + +For build CakeWallet windows application from sources you will be needed to have: +> [Install Flutter]Follow installation guide (https://docs.flutter.dev/get-started/install/windows) and install do not miss to dev tools (install https://docs.flutter.dev/get-started/install/windows/desktop#development-tools) which are required for windows desktop development (need to install Git for Windows and Visual Studio 2022). Then install `Desktop development with C++` packages via GUI Visual Studio 2022, or Visual Studio Build Tools 2022 including: `C++ Build Tools core features`, `C++ 2022 Redistributable Update`, `C++ core desktop features`, `MVC v143 - VS 2022 C++ x64/x86 build tools`, `C++ CMake tools for Windwos`, `Testing tools core features - Build Tools`, `C++ AddressSanitizer`. +> [Install WSL] for building monero dependencies need to install Windows WSL (https://learn.microsoft.com/en-us/windows/wsl/install) and required packages for WSL (Ubuntu): +`$ sudo apt update ` +`$ sudo apt build-essential cmake gcc-mingw-w64 g++-mingw-w64 autoconf libtool pkg-config` + +### 2. Pull CakeWallet source code + +You can downlaod CakeWallet source code from our [GitHub repository](github.com/cake-tech/cake_wallet) via git by following next command: +`$ git clone https://github.com/cake-tech/cake_wallet.git --branch MrCyjaneK-cyjan-monerodart` +OR you can download it as [Zip archive](https://github.com/cake-tech/cake_wallet/archive/refs/heads/MrCyjaneK-cyjan-monerodart.zip) + +### 3. Build Monero, Monero_c and their dependencies + +For use monero in the application need to build Monero wrapper - Monero_C which will be used by monero.dart package. For that need to run shell (bash - typically same named utility should be available after WSL is enabled in your system) with previously installed WSL, then change current directory to the application project directory with your used shell and then change current directory to `scripts/windows`: `$ cd scripts/windows`. Run build script: `$ ./build_all.sh`. + +### 4. Configure and build CakeWallet application + +To configure the application open directory where you have downloaded or unarchived CakeWallet sources and run `cakewallet.bat`. +Or if you used WSL and have active shell session you can run `$ ./cakewallet.sh` script in `scripts/windows` which will run `cakewallet.bat` in WSL. +After execution of `cakewallet.bat` you should to get `Cake Wallet.zip` in project root directory which will contains `CakeWallet.exe` file and another needed files for run the application. Now you can extract files from `Cake Wallet.zip` archive and run the application. diff --git a/cakewallet.bat b/cakewallet.bat new file mode 100644 index 000000000..1904c5710 --- /dev/null +++ b/cakewallet.bat @@ -0,0 +1,51 @@ +@echo off +set cw_win_app_config=--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron +set cw_root=%cd% +set cw_archive_name=Cake Wallet.zip +set cw_archive_path=%cw_root%\%cw_archive_name% +set secrets_file_path=lib\.secrets.g.dart +set release_dir=build\windows\x64\runner\Release +@REM Path could be different +if [%~1]==[] (set tools_root=C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Redist\MSVC\14.38.33135\x64\Microsoft.VC143.CRT) else (set tools_root=%1) +@REM Generate android manifest file +cd scripts +bash.exe gen_android_manifest.sh +cd /d %cw_root% +echo === Generating pubspec.yaml === +copy /Y pubspec_description.yaml pubspec.yaml > nul +call flutter pub get > nul +call dart run tool\generate_pubspec.dart +call flutter pub get > nul +call dart run tool\configure.dart %cw_win_app_config% + +IF NOT EXIST "%secrets_file_path%" ( + echo === Generating new secrets file === + call dart run tool\generate_new_secrets.dart +) ELSE (echo === Using previously/already generated secrets file: %secrets_file_path% ===) + +echo === Generating mobx models === +for /d %%i in (cw_core cw_monero cw_bitcoin cw_ethereum cw_evm cw_polygon cw_nano cw_bitcoin_cash cw_solana cw_tron .) do ( + cd %%i + call flutter pub get > nul + call dart run build_runner build --delete-conflicting-outputs > nul + cd /d %cw_root% +) + +echo === Generating localization files === +call dart run tool\generate_localization.dart + +echo === Building the application executable file === +call flutter build windows --dart-define-from-file=env.json --release + +echo === Prepare distribution actions. Copy needed files to the application bundle === +copy /Y "%tools_root%\msvcp140.dll" "%release_dir%\" > nul +copy /Y "%tools_root%\vcruntime140.dll" "%release_dir%\" > nul +copy /Y "%tools_root%\vcruntime140_1.dll" "%release_dir%\" > nul + +echo === Generate the application archive === +xcopy /s /e /v /Y "%release_dir%\*.*" "build\Cake Wallet\" > nul +tar acf "%cw_archive_name%" -C build\ "Cake Wallet" + +echo === Open Explorer with the application archive === +echo Cake Wallet created archive at: %cw_archive_path% +%SystemRoot%\explorer.exe /select, %cw_archive_path% diff --git a/configure_cake_wallet.sh b/configure_cake_wallet.sh index 837a002e9..0539221a3 100755 --- a/configure_cake_wallet.sh +++ b/configure_cake_wallet.sh @@ -1,11 +1,14 @@ +#!/bin/bash + IOS="ios" ANDROID="android" +MACOS="macos" -PLATFORMS=($IOS $ANDROID) +PLATFORMS=($IOS $ANDROID $MACOS) PLATFORM=$1 if ! [[ " ${PLATFORMS[*]} " =~ " ${PLATFORM} " ]]; then - echo "specify platform: ./configure_cake_wallet.sh ios|android" + echo "specify platform: ./configure_cake_wallet.sh ios|android|macos" exit 1 fi @@ -14,6 +17,11 @@ if [ "$PLATFORM" == "$IOS" ]; then cd scripts/ios fi +if [ "$PLATFORM" == "$MACOS" ]; then + echo "Configuring for macOS" + cd scripts/macos +fi + if [ "$PLATFORM" == "$ANDROID" ]; then echo "Configuring for Android" cd scripts/android @@ -22,5 +30,6 @@ fi source ./app_env.sh cakewallet ./app_config.sh cd ../.. && flutter pub get -flutter packages pub run tool/generate_localization.dart +#flutter packages pub run tool/generate_localization.dart ./model_generator.sh +#cd macos && pod install \ No newline at end of file diff --git a/cw_core/lib/amount_converter.dart b/cw_core/lib/amount_converter.dart index 249b87bd3..1c5456b07 100644 --- a/cw_core/lib/amount_converter.dart +++ b/cw_core/lib/amount_converter.dart @@ -4,10 +4,8 @@ import 'package:cw_core/crypto_currency.dart'; class AmountConverter { static const _moneroAmountLength = 12; static const _moneroAmountDivider = 1000000000000; - static const _litecoinAmountDivider = 100000000; - static const _ethereumAmountDivider = 1000000000000000000; - static const _dashAmountDivider = 100000000; - static const _bitcoinCashAmountDivider = 100000000; + static const _wowneroAmountLength = 11; + static const _wowneroAmountDivider = 100000000000; static const _bitcoinAmountDivider = 100000000; static const _bitcoinAmountLength = 8; static final _bitcoinAmountFormat = NumberFormat() @@ -16,69 +14,16 @@ class AmountConverter { static final _moneroAmountFormat = NumberFormat() ..maximumFractionDigits = _moneroAmountLength ..minimumFractionDigits = 1; - - static double amountIntToDouble(CryptoCurrency cryptoCurrency, int amount) { - switch (cryptoCurrency) { - case CryptoCurrency.xmr: - return _moneroAmountToDouble(amount); - case CryptoCurrency.btc: - return _bitcoinAmountToDouble(amount); - case CryptoCurrency.bch: - return _bitcoinCashAmountToDouble(amount); - case CryptoCurrency.dash: - return _dashAmountToDouble(amount); - case CryptoCurrency.eth: - return _ethereumAmountToDouble(amount); - case CryptoCurrency.ltc: - return _litecoinAmountToDouble(amount); - case CryptoCurrency.xhv: - case CryptoCurrency.xag: - case CryptoCurrency.xau: - case CryptoCurrency.xaud: - case CryptoCurrency.xbtc: - case CryptoCurrency.xcad: - case CryptoCurrency.xchf: - case CryptoCurrency.xcny: - case CryptoCurrency.xeur: - case CryptoCurrency.xgbp: - case CryptoCurrency.xjpy: - case CryptoCurrency.xnok: - case CryptoCurrency.xnzd: - case CryptoCurrency.xusd: - return _moneroAmountToDouble(amount); - default: - return 0.0; - } - } - - static int amountStringToInt(CryptoCurrency cryptoCurrency, String amount) { - switch (cryptoCurrency) { - case CryptoCurrency.xmr: - return _moneroParseAmount(amount); - case CryptoCurrency.xhv: - case CryptoCurrency.xag: - case CryptoCurrency.xau: - case CryptoCurrency.xaud: - case CryptoCurrency.xbtc: - case CryptoCurrency.xcad: - case CryptoCurrency.xchf: - case CryptoCurrency.xcny: - case CryptoCurrency.xeur: - case CryptoCurrency.xgbp: - case CryptoCurrency.xjpy: - case CryptoCurrency.xnok: - case CryptoCurrency.xnzd: - case CryptoCurrency.xusd: - return _moneroParseAmount(amount); - default: - return 0; - } - } + static final _wowneroAmountFormat = NumberFormat() + ..maximumFractionDigits = _wowneroAmountLength + ..minimumFractionDigits = 1; static String amountIntToString(CryptoCurrency cryptoCurrency, int amount) { switch (cryptoCurrency) { case CryptoCurrency.xmr: return _moneroAmountToString(amount); + case CryptoCurrency.wow: + return _wowneroAmountToString(amount); case CryptoCurrency.btc: case CryptoCurrency.bch: case CryptoCurrency.ltc: @@ -106,34 +51,12 @@ class AmountConverter { static double cryptoAmountToDouble({required num amount, required num divider}) => amount / divider; - static String _moneroAmountToString(int amount) => _moneroAmountFormat.format( - cryptoAmountToDouble(amount: amount, divider: _moneroAmountDivider)); + static String _moneroAmountToString(int amount) => _moneroAmountFormat + .format(cryptoAmountToDouble(amount: amount, divider: _moneroAmountDivider)); - static double _moneroAmountToDouble(int amount) => - cryptoAmountToDouble(amount: amount, divider: _moneroAmountDivider); + static String _bitcoinAmountToString(int amount) => _bitcoinAmountFormat + .format(cryptoAmountToDouble(amount: amount, divider: _bitcoinAmountDivider)); - static int _moneroParseAmount(String amount) => - _moneroAmountFormat.parse(amount).toInt(); - - static String _bitcoinAmountToString(int amount) => - _bitcoinAmountFormat.format( - cryptoAmountToDouble(amount: amount, divider: _bitcoinAmountDivider)); - - static double _bitcoinAmountToDouble(int amount) => - cryptoAmountToDouble(amount: amount, divider: _bitcoinAmountDivider); - - static int _doubleToBitcoinAmount(double amount) => - (amount * _bitcoinAmountDivider).toInt(); - - static double _bitcoinCashAmountToDouble(int amount) => - cryptoAmountToDouble(amount: amount, divider: _bitcoinCashAmountDivider); - - static double _dashAmountToDouble(int amount) => - cryptoAmountToDouble(amount: amount, divider: _dashAmountDivider); - - static double _ethereumAmountToDouble(num amount) => - cryptoAmountToDouble(amount: amount, divider: _ethereumAmountDivider); - - static double _litecoinAmountToDouble(int amount) => - cryptoAmountToDouble(amount: amount, divider: _litecoinAmountDivider); + static String _wowneroAmountToString(int amount) => _wowneroAmountFormat + .format(cryptoAmountToDouble(amount: amount, divider: _wowneroAmountDivider)); } diff --git a/cw_core/lib/crypto_currency.dart b/cw_core/lib/crypto_currency.dart index 2bd4eaf91..6881393ed 100644 --- a/cw_core/lib/crypto_currency.dart +++ b/cw_core/lib/crypto_currency.dart @@ -105,6 +105,7 @@ class CryptoCurrency extends EnumerableItem with Serializable implemen CryptoCurrency.usdtSol, CryptoCurrency.usdcTrc20, CryptoCurrency.tbtc, + CryptoCurrency.wow, ]; static const havenCurrencies = [ @@ -221,6 +222,7 @@ class CryptoCurrency extends EnumerableItem with Serializable implemen static const usdtSol = CryptoCurrency(title: 'USDT', tag: 'SOL', fullName: 'USDT Tether', raw: 91, name: 'usdtsol', iconPath: 'assets/images/usdt_icon.png', decimals: 6); static const usdcTrc20 = CryptoCurrency(title: 'USDC', tag: 'TRX', fullName: 'USDC Coin', raw: 92, name: 'usdctrc20', iconPath: 'assets/images/usdc_icon.png', decimals: 6); static const tbtc = CryptoCurrency(title: 'tBTC', fullName: 'Testnet Bitcoin', raw: 93, name: 'tbtc', iconPath: 'assets/images/tbtc.png', decimals: 8); + static const wow = CryptoCurrency(title: 'WOW', fullName: 'Wownero', raw: 94, name: 'wow', iconPath: 'assets/images/wownero_icon.png', decimals: 11); static final Map _rawCurrencyMap = diff --git a/cw_core/lib/currency_for_wallet_type.dart b/cw_core/lib/currency_for_wallet_type.dart index 8cf438769..630078949 100644 --- a/cw_core/lib/currency_for_wallet_type.dart +++ b/cw_core/lib/currency_for_wallet_type.dart @@ -28,7 +28,9 @@ CryptoCurrency currencyForWalletType(WalletType type, {bool? isTestnet}) { return CryptoCurrency.sol; case WalletType.tron: return CryptoCurrency.trx; - default: + case WalletType.wownero: + return CryptoCurrency.wow; + case WalletType.none: throw Exception( 'Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType'); } diff --git a/cw_core/lib/get_height_by_date.dart b/cw_core/lib/get_height_by_date.dart index d62a78468..6f1b4078b 100644 --- a/cw_core/lib/get_height_by_date.dart +++ b/cw_core/lib/get_height_by_date.dart @@ -297,3 +297,81 @@ DateTime getDateByBitcoinHeight(int height) { return estimatedDate; } + +// TODO: enhance all of this global const lists +const wowDates = { + "2023-12": 583048, + "2023-11": 575048, + "2023-10": 566048, + "2023-09": 558048, + "2023-08": 549048, + "2023-07": 540048, + "2023-06": 532048, + "2023-05": 523048, + "2023-04": 514048, + "2023-03": 505048, + "2023-02": 497048, + "2023-01": 488048, + "2022-12": 479048, + "2022-11": 471048, + "2022-10": 462048, + "2022-09": 453048, + "2022-08": 444048, + "2022-07": 435048, + "2022-06": 427048, + "2022-05": 418048, + "2022-04": 410048, + "2022-03": 401048, + "2022-02": 393048, + "2022-01": 384048, + "2021-12": 375048, + "2021-11": 367048, + "2021-10": 358048, + "2021-09": 349048, + "2021-08": 340048, + "2021-07": 331048, + "2021-06": 322048, + "2021-05": 313048, + "2021-04": 305048, + "2021-03": 295048, + "2021-02": 287048, + "2021-01": 279148, + "2020-10": 252000, + "2020-09": 243000, + "2020-08": 234000, + "2020-07": 225000, + "2020-06": 217500, + "2020-05": 208500, + "2020-04": 199500, + "2020-03": 190500, + "2020-02": 183000, + "2020-01": 174000, + "2019-12": 165000, + "2019-11": 156000, + "2019-10": 147000, + "2019-09": 138000, + "2019-08": 129000, + "2019-07": 120000, + "2019-06": 112500, + "2019-05": 103500, + "2019-04": 94500, + "2019-03": 85500, + "2019-02": 79500, + "2019-01": 73500, + "2018-12": 67500, + "2018-11": 61500, + "2018-10": 52500, + "2018-09": 45000, + "2018-08": 36000, + "2018-07": 27000, + "2018-06": 18000, + "2018-05": 9000, + "2018-04": 1 +}; + +int getWowneroHeightByDate({required DateTime date}) { + String closestKey = + wowDates.keys.firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => ''); + + return wowDates[closestKey] ?? 0; +} \ No newline at end of file diff --git a/cw_core/lib/node.dart b/cw_core/lib/node.dart index 00b2c51f1..3bbbf38de 100644 --- a/cw_core/lib/node.dart +++ b/cw_core/lib/node.dart @@ -79,6 +79,7 @@ class Node extends HiveObject with Keyable { switch (type) { case WalletType.monero: case WalletType.haven: + case WalletType.wownero: return Uri.http(uriRaw, ''); case WalletType.bitcoin: case WalletType.litecoin: @@ -96,7 +97,7 @@ class Node extends HiveObject with Keyable { case WalletType.solana: case WalletType.tron: return Uri.https(uriRaw, path ?? ''); - default: + case WalletType.none: throw Exception('Unexpected type ${type.toString()} for Node uri'); } } @@ -143,6 +144,7 @@ class Node extends HiveObject with Keyable { switch (type) { case WalletType.monero: case WalletType.haven: + case WalletType.wownero: return requestMoneroNode(); case WalletType.nano: case WalletType.banano: @@ -155,7 +157,7 @@ class Node extends HiveObject with Keyable { case WalletType.solana: case WalletType.tron: return requestElectrumServer(); - default: + case WalletType.none: return false; } } catch (_) { diff --git a/cw_core/lib/pathForWallet.dart b/cw_core/lib/pathForWallet.dart index cfc33ef21..9aa721923 100644 --- a/cw_core/lib/pathForWallet.dart +++ b/cw_core/lib/pathForWallet.dart @@ -1,9 +1,10 @@ import 'dart:io'; +import 'package:cw_core/root_dir.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:path_provider/path_provider.dart'; Future pathForWalletDir({required String name, required WalletType type}) async { - final root = await getApplicationDocumentsDirectory(); + final root = await getAppDir(); final prefix = walletTypeToString(type).toLowerCase(); final walletsDir = Directory('${root.path}/wallets'); final walletDire = Directory('${walletsDir.path}/$prefix/$name'); @@ -20,8 +21,8 @@ Future pathForWallet({required String name, required WalletType type}) a .then((path) => path + '/$name'); Future outdatedAndroidPathForWalletDir({required String name}) async { - final directory = await getApplicationDocumentsDirectory(); + final directory = await getAppDir(); final pathDir = directory.path + '/$name'; return pathDir; -} \ No newline at end of file +} diff --git a/cw_core/lib/root_dir.dart b/cw_core/lib/root_dir.dart new file mode 100644 index 000000000..c2a8170bc --- /dev/null +++ b/cw_core/lib/root_dir.dart @@ -0,0 +1,35 @@ +import 'dart:io'; +import 'package:path_provider/path_provider.dart'; + +String? _rootDirPath; + +void setRootDirFromEnv() => _rootDirPath = Platform.environment['CAKE_WALLET_DIR']; + +Future getAppDir({String appName = 'cake_wallet'}) async { + Directory dir; + + if (_rootDirPath != null && _rootDirPath!.isNotEmpty) { + dir = Directory.fromUri(Uri.file(_rootDirPath!)); + dir.create(recursive: true); + } else { + if (Platform.isWindows) { + dir = await getApplicationSupportDirectory(); + } else if (Platform.isLinux) { + String appDirPath; + + try { + dir = await getApplicationDocumentsDirectory(); + appDirPath = '${dir.path}/$appName'; + } catch (e) { + appDirPath = '/home/${Platform.environment['USER']}/.$appName'; + } + + dir = Directory.fromUri(Uri.file(appDirPath)); + await dir.create(recursive: true); + } else { + dir = await getApplicationDocumentsDirectory(); + } + } + + return dir; +} diff --git a/cw_core/lib/sec_random_native.dart b/cw_core/lib/sec_random_native.dart index ce251efc0..2011602bf 100644 --- a/cw_core/lib/sec_random_native.dart +++ b/cw_core/lib/sec_random_native.dart @@ -1,3 +1,5 @@ +import 'dart:io'; +import 'dart:math'; import 'dart:typed_data'; import 'package:flutter/services.dart'; @@ -6,6 +8,12 @@ const utils = const MethodChannel('com.cake_wallet/native_utils'); Future secRandom(int count) async { try { + if (Platform.isWindows || Platform.isLinux) { + // Used method to get securely generated random bytes from cake backups + const byteSize = 256; + final rng = Random.secure(); + return Uint8List.fromList(List.generate(count, (_) => rng.nextInt(byteSize))); + } return await utils.invokeMethod('sec_random', {'count': count}) ?? Uint8List.fromList([]); } on PlatformException catch (_) { return Uint8List.fromList([]); diff --git a/cw_core/lib/wallet_type.dart b/cw_core/lib/wallet_type.dart index b3e41a989..e3957b4e7 100644 --- a/cw_core/lib/wallet_type.dart +++ b/cw_core/lib/wallet_type.dart @@ -54,7 +54,10 @@ enum WalletType { solana, @HiveField(11) - tron + tron, + + @HiveField(12) + wownero, } int serializeToInt(WalletType type) { @@ -81,7 +84,9 @@ int serializeToInt(WalletType type) { return 9; case WalletType.tron: return 10; - default: + case WalletType.wownero: + return 11; + case WalletType.none: return -1; } } @@ -110,6 +115,8 @@ WalletType deserializeFromInt(int raw) { return WalletType.solana; case 10: return WalletType.tron; + case 11: + return WalletType.wownero; default: throw Exception('Unexpected token: $raw for WalletType deserializeFromInt'); } @@ -139,7 +146,9 @@ String walletTypeToString(WalletType type) { return 'Solana'; case WalletType.tron: return 'Tron'; - default: + case WalletType.wownero: + return 'Wownero'; + case WalletType.none: return ''; } } @@ -168,7 +177,9 @@ String walletTypeToDisplayName(WalletType type) { return 'Solana (SOL)'; case WalletType.tron: return 'Tron (TRX)'; - default: + case WalletType.wownero: + return 'Wownero (WOW)'; + case WalletType.none: return ''; } } @@ -200,7 +211,9 @@ CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = fal return CryptoCurrency.sol; case WalletType.tron: return CryptoCurrency.trx; - default: + case WalletType.wownero: + return CryptoCurrency.wow; + case WalletType.none: throw Exception( 'Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency'); } diff --git a/cw_core/lib/wownero_amount_format.dart b/cw_core/lib/wownero_amount_format.dart new file mode 100644 index 000000000..96d2797e8 --- /dev/null +++ b/cw_core/lib/wownero_amount_format.dart @@ -0,0 +1,18 @@ +import 'package:intl/intl.dart'; +import 'package:cw_core/crypto_amount_format.dart'; + +const wowneroAmountLength = 11; +const wowneroAmountDivider = 100000000000; +final wowneroAmountFormat = NumberFormat() + ..maximumFractionDigits = wowneroAmountLength + ..minimumFractionDigits = 1; + +String wowneroAmountToString({required int amount}) => wowneroAmountFormat + .format(cryptoAmountToDouble(amount: amount, divider: wowneroAmountDivider)) + .replaceAll(',', ''); + +double wowneroAmountToDouble({required int amount}) => + cryptoAmountToDouble(amount: amount, divider: wowneroAmountDivider); + +int wowneroParseAmount({required String amount}) => + (double.parse(amount) * wowneroAmountDivider).round(); \ No newline at end of file diff --git a/cw_core/lib/wownero_balance.dart b/cw_core/lib/wownero_balance.dart new file mode 100644 index 000000000..2820659f2 --- /dev/null +++ b/cw_core/lib/wownero_balance.dart @@ -0,0 +1,38 @@ +import 'package:cw_core/balance.dart'; +import 'package:cw_core/wownero_amount_format.dart'; + +class WowneroBalance extends Balance { + WowneroBalance({required this.fullBalance, required this.unlockedBalance, this.frozenBalance = 0}) + : formattedFullBalance = wowneroAmountToString(amount: fullBalance), + formattedUnlockedBalance = wowneroAmountToString(amount: unlockedBalance - frozenBalance), + formattedLockedBalance = + wowneroAmountToString(amount: frozenBalance + fullBalance - unlockedBalance), + super(unlockedBalance, fullBalance); + + WowneroBalance.fromString( + {required this.formattedFullBalance, + required this.formattedUnlockedBalance, + this.formattedLockedBalance = '0.0'}) + : fullBalance = wowneroParseAmount(amount: formattedFullBalance), + unlockedBalance = wowneroParseAmount(amount: formattedUnlockedBalance), + frozenBalance = wowneroParseAmount(amount: formattedLockedBalance), + super(wowneroParseAmount(amount: formattedUnlockedBalance), + wowneroParseAmount(amount: formattedFullBalance)); + + final int fullBalance; + final int unlockedBalance; + final int frozenBalance; + final String formattedFullBalance; + final String formattedUnlockedBalance; + final String formattedLockedBalance; + + @override + String get formattedUnAvailableBalance => + formattedLockedBalance == '0.0' ? '' : formattedLockedBalance; + + @override + String get formattedAvailableBalance => formattedUnlockedBalance; + + @override + String get formattedAdditionalBalance => formattedFullBalance; +} \ No newline at end of file diff --git a/cw_core/pubspec.lock b/cw_core/pubspec.lock index 88fddae09..518c71b94 100644 --- a/cw_core/pubspec.lock +++ b/cw_core/pubspec.lock @@ -660,10 +660,10 @@ packages: dependency: "direct main" description: name: unorm_dart - sha256: "5b35bff83fce4d76467641438f9e867dc9bcfdb8c1694854f230579d68cd8f4b" + sha256: "23d8bf65605401a6a32cff99435fed66ef3dab3ddcad3454059165df46496a3b" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.3.0" vector_math: dependency: transitive description: diff --git a/cw_haven/lib/api/account_list.dart b/cw_haven/lib/api/account_list.dart index a05446c8e..87f036206 100644 --- a/cw_haven/lib/api/account_list.dart +++ b/cw_haven/lib/api/account_list.dart @@ -4,7 +4,6 @@ import 'package:cw_haven/api/signatures.dart'; import 'package:cw_haven/api/types.dart'; import 'package:cw_haven/api/haven_api.dart'; import 'package:cw_haven/api/structs/account_row.dart'; -import 'package:flutter/foundation.dart'; import 'package:cw_haven/api/wallet.dart'; final accountSizeNative = havenApi @@ -72,12 +71,11 @@ void _setLabelForAccount(Map args) { } Future addAccount({required String label}) async { - await compute(_addAccount, label); + _addAccount(label); await store(); } Future setLabelForAccount({required int accountIndex, required String label}) async { - await compute( - _setLabelForAccount, {'accountIndex': accountIndex, 'label': label}); + _setLabelForAccount({'accountIndex': accountIndex, 'label': label}); await store(); } \ No newline at end of file diff --git a/cw_monero/android/.classpath b/cw_monero/android/.classpath deleted file mode 100644 index 4a04201ca..000000000 --- a/cw_monero/android/.classpath +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/cw_monero/android/.gitignore b/cw_monero/android/.gitignore deleted file mode 100644 index c6cbe562a..000000000 --- a/cw_monero/android/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea/workspace.xml -/.idea/libraries -.DS_Store -/build -/captures diff --git a/cw_monero/android/.project b/cw_monero/android/.project deleted file mode 100644 index e0799208f..000000000 --- a/cw_monero/android/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - cw_monero - Project android created by Buildship. - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.buildship.core.gradleprojectbuilder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.buildship.core.gradleprojectnature - - diff --git a/cw_monero/android/.settings/org.eclipse.buildship.core.prefs b/cw_monero/android/.settings/org.eclipse.buildship.core.prefs deleted file mode 100644 index a88c4d484..000000000 --- a/cw_monero/android/.settings/org.eclipse.buildship.core.prefs +++ /dev/null @@ -1,13 +0,0 @@ -arguments= -auto.sync=false -build.scans.enabled=false -connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(6.0-20191016123526+0000)) -connection.project.dir=../../android -eclipse.preferences.version=1 -gradle.user.home= -java.home= -jvm.arguments= -offline.mode=false -override.workspace.settings=true -show.console.view=true -show.executions.view=true diff --git a/cw_monero/android/CMakeLists.txt b/cw_monero/android/CMakeLists.txt deleted file mode 100644 index f9f98927c..000000000 --- a/cw_monero/android/CMakeLists.txt +++ /dev/null @@ -1,232 +0,0 @@ -cmake_minimum_required(VERSION 3.4.1) - -add_library( cw_monero - SHARED - ./jni/monero_jni.cpp - ../ios/Classes/monero_api.cpp) - - find_library( log-lib log ) - -set(EXTERNAL_LIBS_DIR ${CMAKE_SOURCE_DIR}/../../cw_shared_external/ios/External/android) - -############ -# libsodium -############ - -add_library(sodium STATIC IMPORTED) -set_target_properties(sodium PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libsodium.a) - -############ -# OpenSSL -############ - -add_library(crypto STATIC IMPORTED) -set_target_properties(crypto PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libcrypto.a) - -add_library(ssl STATIC IMPORTED) -set_target_properties(ssl PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libssl.a) - -############ -# Boost -############ - -add_library(boost_chrono STATIC IMPORTED) -set_target_properties(boost_chrono PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_chrono.a) - -add_library(boost_date_time STATIC IMPORTED) -set_target_properties(boost_date_time PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_date_time.a) - -add_library(boost_filesystem STATIC IMPORTED) -set_target_properties(boost_filesystem PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_filesystem.a) - -add_library(boost_program_options STATIC IMPORTED) -set_target_properties(boost_program_options PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_program_options.a) - -add_library(boost_regex STATIC IMPORTED) -set_target_properties(boost_regex PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_regex.a) - -add_library(boost_serialization STATIC IMPORTED) -set_target_properties(boost_serialization PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_serialization.a) - -add_library(boost_system STATIC IMPORTED) -set_target_properties(boost_system PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_system.a) - -add_library(boost_thread STATIC IMPORTED) -set_target_properties(boost_thread PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_thread.a) - -add_library(boost_wserialization STATIC IMPORTED) -set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_wserialization.a) - -############# -# Monero -############# - -add_library(wallet_api STATIC IMPORTED) -set_target_properties(wallet_api PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libwallet_api.a) - -add_library(wallet STATIC IMPORTED) -set_target_properties(wallet PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libwallet.a) - -add_library(cryptonote_core STATIC IMPORTED) -set_target_properties(cryptonote_core PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libcryptonote_core.a) - -add_library(cryptonote_basic STATIC IMPORTED) -set_target_properties(cryptonote_basic PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libcryptonote_basic.a) - -add_library(cryptonote_format_utils_basic STATIC IMPORTED) -set_target_properties(cryptonote_format_utils_basic PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libcryptonote_format_utils_basic.a) - -add_library(mnemonics STATIC IMPORTED) -set_target_properties(mnemonics PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libmnemonics.a) - -add_library(common STATIC IMPORTED) -set_target_properties(common PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libcommon.a) - -add_library(cncrypto STATIC IMPORTED) -set_target_properties(cncrypto PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libcncrypto.a) - -add_library(ringct STATIC IMPORTED) -set_target_properties(ringct PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libringct.a) - -add_library(ringct_basic STATIC IMPORTED) -set_target_properties(ringct_basic PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libringct_basic.a) - -add_library(blockchain_db STATIC IMPORTED) -set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libblockchain_db.a) - -add_library(lmdb STATIC IMPORTED) -set_target_properties(lmdb PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/liblmdb.a) - -add_library(easylogging STATIC IMPORTED) -set_target_properties(easylogging PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libeasylogging.a) - -add_library(unbound STATIC IMPORTED) -set_target_properties(unbound PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libunbound.a) - -add_library(epee STATIC IMPORTED) -set_target_properties(epee PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libepee.a) - -add_library(blocks STATIC IMPORTED) -set_target_properties(blocks PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libblocks.a) - -add_library(checkpoints STATIC IMPORTED) -set_target_properties(checkpoints PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libcheckpoints.a) - -add_library(device STATIC IMPORTED) -set_target_properties(device PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libdevice.a) - -add_library(device_trezor STATIC IMPORTED) -set_target_properties(device_trezor PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libdevice_trezor.a) - -add_library(multisig STATIC IMPORTED) -set_target_properties(multisig PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libmultisig.a) - -add_library(version STATIC IMPORTED) -set_target_properties(version PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libversion.a) - -add_library(net STATIC IMPORTED) -set_target_properties(net PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libnet.a) - -add_library(hardforks STATIC IMPORTED) -set_target_properties(hardforks PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libhardforks.a) - -add_library(randomx STATIC IMPORTED) -set_target_properties(randomx PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/librandomx.a) - -add_library(rpc_base STATIC IMPORTED) -set_target_properties(rpc_base PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/librpc_base.a) - -add_library(wallet-crypto STATIC IMPORTED) -set_target_properties(wallet-crypto PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libwallet-crypto.a) - -set(WALLET_CRYPTO "") - -if(${ANDROID_ABI} STREQUAL "x86_64") - set(WALLET_CRYPTO "wallet-crypto") -endif() - -include_directories( ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/include ) - -target_link_libraries( cw_monero - - wallet_api - wallet - cryptonote_core - cryptonote_basic - cryptonote_format_utils_basic - mnemonics - ringct - ringct_basic - net - common - cncrypto - blockchain_db - lmdb - easylogging - unbound - epee - blocks - checkpoints - device - device_trezor - multisig - version - randomx - hardforks - rpc_base - ${WALLET_CRYPTO} - - boost_chrono - boost_date_time - boost_filesystem - boost_program_options - boost_regex - boost_serialization - boost_system - boost_thread - boost_wserialization - - ssl - crypto - - sodium - - ${log-lib} ) \ No newline at end of file diff --git a/cw_monero/android/build.gradle b/cw_monero/android/build.gradle deleted file mode 100644 index fc4835e81..000000000 --- a/cw_monero/android/build.gradle +++ /dev/null @@ -1,49 +0,0 @@ -group 'com.cakewallet.monero' -version '1.0-SNAPSHOT' - -buildscript { - ext.kotlin_version = '1.7.10' - repositories { - google() - jcenter() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.3.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -rootProject.allprojects { - repositories { - google() - jcenter() - } -} - -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' - -android { - compileSdkVersion 28 - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - defaultConfig { - minSdkVersion 21 - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - lintOptions { - disable 'InvalidPackage' - } - externalNativeBuild { - cmake { - path "CMakeLists.txt" - } - } -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/cw_monero/android/gradle.properties b/cw_monero/android/gradle.properties deleted file mode 100644 index 38c8d4544..000000000 --- a/cw_monero/android/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.enableR8=true -android.useAndroidX=true -android.enableJetifier=true diff --git a/cw_monero/android/gradle/wrapper/gradle-wrapper.properties b/cw_monero/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 019065d1d..000000000 --- a/cw_monero/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip diff --git a/cw_monero/android/jni/monero_jni.cpp b/cw_monero/android/jni/monero_jni.cpp deleted file mode 100644 index 83e06a41f..000000000 --- a/cw_monero/android/jni/monero_jni.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include -#include -#include "../../ios/Classes/monero_api.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -JNIEXPORT void JNICALL -Java_com_cakewallet_monero_MoneroApi_setNodeAddressJNI( - JNIEnv *env, - jobject inst, - jstring uri, - jstring login, - jstring password, - jboolean use_ssl, - jboolean is_light_wallet) { - const char *_uri = env->GetStringUTFChars(uri, 0); - const char *_login = ""; - const char *_password = ""; - char *error; - - if (login != NULL) { - _login = env->GetStringUTFChars(login, 0); - } - - if (password != NULL) { - _password = env->GetStringUTFChars(password, 0); - } - char *__uri = (char*) _uri; - char *__login = (char*) _login; - char *__password = (char*) _password; - bool inited = setup_node(__uri, __login, __password, false, false, error); - - if (!inited) { - env->ThrowNew(env->FindClass("java/lang/Exception"), error); - } -} - -JNIEXPORT void JNICALL -Java_com_cakewallet_monero_MoneroApi_connectToNodeJNI( - JNIEnv *env, - jobject inst) { - char *error; - bool is_connected = connect_to_node(error); - - if (!is_connected) { - env->ThrowNew(env->FindClass("java/lang/Exception"), error); - } -} - -JNIEXPORT void JNICALL -Java_com_cakewallet_monero_MoneroApi_startSyncJNI( - JNIEnv *env, - jobject inst) { - start_refresh(); -} - -JNIEXPORT void JNICALL -Java_com_cakewallet_monero_MoneroApi_loadWalletJNI( - JNIEnv *env, - jobject inst, - jstring path, - jstring password) { - char *_path = (char *) env->GetStringUTFChars(path, 0); - char *_password = (char *) env->GetStringUTFChars(password, 0); - - load_wallet(_path, _password, 0); -} - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/cw_monero/android/settings.gradle b/cw_monero/android/settings.gradle deleted file mode 100644 index 1f9e2a39d..000000000 --- a/cw_monero/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'cw_monero' diff --git a/cw_monero/android/src/main/AndroidManifest.xml b/cw_monero/android/src/main/AndroidManifest.xml deleted file mode 100644 index 8152415a2..000000000 --- a/cw_monero/android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/cw_monero/android/src/main/kotlin/com/cakewallet/monero/CwMoneroPlugin.kt b/cw_monero/android/src/main/kotlin/com/cakewallet/monero/CwMoneroPlugin.kt deleted file mode 100644 index 37684a16a..000000000 --- a/cw_monero/android/src/main/kotlin/com/cakewallet/monero/CwMoneroPlugin.kt +++ /dev/null @@ -1,74 +0,0 @@ -package com.cakewallet.monero - -import android.app.Activity -import android.os.AsyncTask -import android.os.Looper -import android.os.Handler -import android.os.Process - -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel -import io.flutter.plugin.common.MethodChannel.MethodCallHandler -import io.flutter.plugin.common.MethodChannel.Result -import io.flutter.plugin.common.PluginRegistry.Registrar - -class doAsync(val handler: () -> Unit) : AsyncTask() { - override fun doInBackground(vararg params: Void?): Void? { - Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO); - handler() - return null - } -} - -class CwMoneroPlugin: MethodCallHandler { - companion object { -// val moneroApi = MoneroApi() - val main = Handler(Looper.getMainLooper()); - - init { - System.loadLibrary("cw_monero") - } - - @JvmStatic - fun registerWith(registrar: Registrar) { - val channel = MethodChannel(registrar.messenger(), "cw_monero") - channel.setMethodCallHandler(CwMoneroPlugin()) - } - } - - override fun onMethodCall(call: MethodCall, result: Result) { - if (call.method == "setupNode") { - val uri = call.argument("address") ?: "" - val login = call.argument("login") ?: "" - val password = call.argument("password") ?: "" - val useSSL = false - val isLightWallet = false -// doAsync { -// try { -// moneroApi.setNodeAddressJNI(uri, login, password, useSSL, isLightWallet) -// main.post({ -// result.success(true) -// }); -// } catch(e: Throwable) { -// main.post({ -// result.error("CONNECTION_ERROR", e.message, null) -// }); -// } -// }.execute() - } - if (call.method == "startSync") { -// doAsync { -// moneroApi.startSyncJNI() -// main.post({ -// result.success(true) -// }); -// }.execute() - } - if (call.method == "loadWallet") { - val path = call.argument("path") ?: "" - val password = call.argument("password") ?: "" -// moneroApi.loadWalletJNI(path, password) - result.success(true) - } - } -} diff --git a/cw_monero/example/.gitignore b/cw_monero/example/.gitignore deleted file mode 100644 index 24476c5d1..000000000 --- a/cw_monero/example/.gitignore +++ /dev/null @@ -1,44 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ -migrate_working_dir/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -**/ios/Flutter/.last_build_id -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -.packages -.pub-cache/ -.pub/ -/build/ - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json - -# Android Studio will place build artifacts here -/android/app/debug -/android/app/profile -/android/app/release diff --git a/cw_monero/example/README.md b/cw_monero/example/README.md deleted file mode 100644 index 18cf6d109..000000000 --- a/cw_monero/example/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# cw_monero_example - -Demonstrates how to use the cw_monero plugin. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) - -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/cw_monero/example/analysis_options.yaml b/cw_monero/example/analysis_options.yaml deleted file mode 100644 index 61b6c4de1..000000000 --- a/cw_monero/example/analysis_options.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. -include: package:flutter_lints/flutter.yaml - -linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at - # https://dart-lang.github.io/linter/lints/index.html. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. - rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/cw_monero/example/lib/main.dart b/cw_monero/example/lib/main.dart deleted file mode 100644 index e4374f097..000000000 --- a/cw_monero/example/lib/main.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/material.dart'; -import 'dart:async'; - -import 'package:flutter/services.dart'; -import 'package:cw_monero/cw_monero.dart'; - -void main() { - runApp(const MyApp()); -} - -class MyApp extends StatefulWidget { - const MyApp({super.key}); - - @override - State createState() => _MyAppState(); -} - -class _MyAppState extends State { - String _platformVersion = 'Unknown'; - final _cwMoneroPlugin = CwMonero(); - - @override - void initState() { - super.initState(); - initPlatformState(); - } - - // Platform messages are asynchronous, so we initialize in an async method. - Future initPlatformState() async { - String platformVersion; - // Platform messages may fail, so we use a try/catch PlatformException. - // We also handle the message potentially returning null. - try { - platformVersion = - await _cwMoneroPlugin.getPlatformVersion() ?? 'Unknown platform version'; - } on PlatformException { - platformVersion = 'Failed to get platform version.'; - } - - // If the widget was removed from the tree while the asynchronous platform - // message was in flight, we want to discard the reply rather than calling - // setState to update our non-existent appearance. - if (!mounted) return; - - setState(() { - _platformVersion = platformVersion; - }); - } - - @override - Widget build(BuildContext context) { - return MaterialApp( - home: Scaffold( - appBar: AppBar( - title: const Text('Plugin example app'), - ), - body: Center( - child: Text('Running on: $_platformVersion\n'), - ), - ), - ); - } -} diff --git a/cw_monero/example/macos/.gitignore b/cw_monero/example/macos/.gitignore deleted file mode 100644 index 746adbb6b..000000000 --- a/cw_monero/example/macos/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Flutter-related -**/Flutter/ephemeral/ -**/Pods/ - -# Xcode-related -**/dgph -**/xcuserdata/ diff --git a/cw_monero/example/macos/Flutter/Flutter-Debug.xcconfig b/cw_monero/example/macos/Flutter/Flutter-Debug.xcconfig deleted file mode 100644 index 4b81f9b2d..000000000 --- a/cw_monero/example/macos/Flutter/Flutter-Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/cw_monero/example/macos/Flutter/Flutter-Release.xcconfig b/cw_monero/example/macos/Flutter/Flutter-Release.xcconfig deleted file mode 100644 index 5caa9d157..000000000 --- a/cw_monero/example/macos/Flutter/Flutter-Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/cw_monero/example/macos/Flutter/GeneratedPluginRegistrant.swift b/cw_monero/example/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index e25d64097..000000000 --- a/cw_monero/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - -import cw_monero -import path_provider_foundation - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - CwMoneroPlugin.register(with: registry.registrar(forPlugin: "CwMoneroPlugin")) - PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) -} diff --git a/cw_monero/example/macos/Podfile b/cw_monero/example/macos/Podfile deleted file mode 100644 index dade8dfad..000000000 --- a/cw_monero/example/macos/Podfile +++ /dev/null @@ -1,40 +0,0 @@ -platform :osx, '10.11' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_macos_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_macos_build_settings(target) - end -end diff --git a/cw_monero/example/macos/Podfile.lock b/cw_monero/example/macos/Podfile.lock deleted file mode 100644 index 692176b30..000000000 --- a/cw_monero/example/macos/Podfile.lock +++ /dev/null @@ -1,22 +0,0 @@ -PODS: - - FlutterMacOS (1.0.0) - - path_provider_macos (0.0.1): - - FlutterMacOS - -DEPENDENCIES: - - FlutterMacOS (from `Flutter/ephemeral`) - - path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`) - -EXTERNAL SOURCES: - FlutterMacOS: - :path: Flutter/ephemeral - path_provider_macos: - :path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos - -SPEC CHECKSUMS: - FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811 - path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19 - -PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c - -COCOAPODS: 1.11.2 diff --git a/cw_monero/example/macos/Runner.xcodeproj/project.pbxproj b/cw_monero/example/macos/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 472859e8c..000000000 --- a/cw_monero/example/macos/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,632 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 51; - objects = { - -/* Begin PBXAggregateTarget section */ - 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; - buildPhases = ( - 33CC111E2044C6BF0003C045 /* ShellScript */, - ); - dependencies = ( - ); - name = "Flutter Assemble"; - productName = FLX; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - 428E7496E2068D0AB138F295 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C29B2253BA962B7A415DBA77 /* Pods_Runner.framework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC111A2044C6BA0003C045; - remoteInfo = FLX; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 33CC110E2044A8840003C045 /* Bundle Framework */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Bundle Framework"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* cw_monero_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = cw_monero_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; - 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; - 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; - 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; - 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; - 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - A9CDA1605413332AB9056C23 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - C29B2253BA962B7A415DBA77 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - E434913D71DC2682EF8E9059 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - EEF09839C86335F78056F812 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 33CC10EA2044A3C60003C045 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 428E7496E2068D0AB138F295 /* Pods_Runner.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 33BA886A226E78AF003329D5 /* Configs */ = { - isa = PBXGroup; - children = ( - 33E5194F232828860026EE4D /* AppInfo.xcconfig */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, - ); - path = Configs; - sourceTree = ""; - }; - 33CC10E42044A3C60003C045 = { - isa = PBXGroup; - children = ( - 33FAB671232836740065AC1E /* Runner */, - 33CEB47122A05771004F2AC0 /* Flutter */, - 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, - 77870A4C94A9AB6EEC2EE261 /* Pods */, - ); - sourceTree = ""; - }; - 33CC10EE2044A3C60003C045 /* Products */ = { - isa = PBXGroup; - children = ( - 33CC10ED2044A3C60003C045 /* cw_monero_example.app */, - ); - name = Products; - sourceTree = ""; - }; - 33CC11242044D66E0003C045 /* Resources */ = { - isa = PBXGroup; - children = ( - 33CC10F22044A3C60003C045 /* Assets.xcassets */, - 33CC10F42044A3C60003C045 /* MainMenu.xib */, - 33CC10F72044A3C60003C045 /* Info.plist */, - ); - name = Resources; - path = ..; - sourceTree = ""; - }; - 33CEB47122A05771004F2AC0 /* Flutter */ = { - isa = PBXGroup; - children = ( - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, - ); - path = Flutter; - sourceTree = ""; - }; - 33FAB671232836740065AC1E /* Runner */ = { - isa = PBXGroup; - children = ( - 33CC10F02044A3C60003C045 /* AppDelegate.swift */, - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, - 33E51913231747F40026EE4D /* DebugProfile.entitlements */, - 33E51914231749380026EE4D /* Release.entitlements */, - 33CC11242044D66E0003C045 /* Resources */, - 33BA886A226E78AF003329D5 /* Configs */, - ); - path = Runner; - sourceTree = ""; - }; - 77870A4C94A9AB6EEC2EE261 /* Pods */ = { - isa = PBXGroup; - children = ( - EEF09839C86335F78056F812 /* Pods-Runner.debug.xcconfig */, - A9CDA1605413332AB9056C23 /* Pods-Runner.release.xcconfig */, - E434913D71DC2682EF8E9059 /* Pods-Runner.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - C29B2253BA962B7A415DBA77 /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 33CC10EC2044A3C60003C045 /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 0A239C1738C005E3F6E4DFC6 /* [CP] Check Pods Manifest.lock */, - 33CC10E92044A3C60003C045 /* Sources */, - 33CC10EA2044A3C60003C045 /* Frameworks */, - 33CC10EB2044A3C60003C045 /* Resources */, - 33CC110E2044A8840003C045 /* Bundle Framework */, - 3399D490228B24CF009A79C7 /* ShellScript */, - 0CEAA82AE8A029C31B39F234 /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 33CC11202044C79F0003C045 /* PBXTargetDependency */, - ); - name = Runner; - productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* cw_monero_example.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 33CC10E52044A3C60003C045 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 33CC10EC2044A3C60003C045 = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 1100; - ProvisioningStyle = Automatic; - SystemCapabilities = { - com.apple.Sandbox = { - enabled = 1; - }; - }; - }; - 33CC111A2044C6BA0003C045 = { - CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Manual; - }; - }; - }; - buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 33CC10E42044A3C60003C045; - productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 33CC10EC2044A3C60003C045 /* Runner */, - 33CC111A2044C6BA0003C045 /* Flutter Assemble */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 33CC10EB2044A3C60003C045 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 0A239C1738C005E3F6E4DFC6 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 0CEAA82AE8A029C31B39F234 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 3399D490228B24CF009A79C7 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; - }; - 33CC111E2044C6BF0003C045 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - Flutter/ephemeral/FlutterInputs.xcfilelist, - ); - inputPaths = ( - Flutter/ephemeral/tripwire, - ); - outputFileListPaths = ( - Flutter/ephemeral/FlutterOutputs.xcfilelist, - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 33CC10E92044A3C60003C045 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; - targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 33CC10F52044A3C60003C045 /* Base */, - ); - name = MainMenu.xib; - path = Runner; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 338D0CE9231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Profile; - }; - 338D0CEA231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Profile; - }; - 338D0CEB231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Profile; - }; - 33CC10F92044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 33CC10FA2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Release; - }; - 33CC10FC2044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 33CC10FD2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 33CC111C2044C6BA0003C045 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 33CC111D2044C6BA0003C045 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10F92044A3C60003C045 /* Debug */, - 33CC10FA2044A3C60003C045 /* Release */, - 338D0CE9231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10FC2044A3C60003C045 /* Debug */, - 33CC10FD2044A3C60003C045 /* Release */, - 338D0CEA231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC111C2044C6BA0003C045 /* Debug */, - 33CC111D2044C6BA0003C045 /* Release */, - 338D0CEB231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 33CC10E52044A3C60003C045 /* Project object */; -} diff --git a/cw_monero/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/cw_monero/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003..000000000 --- a/cw_monero/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/cw_monero/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/cw_monero/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index 4e44b7ced..000000000 --- a/cw_monero/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/cw_monero/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/cw_monero/example/macos/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc14c..000000000 --- a/cw_monero/example/macos/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/cw_monero/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/cw_monero/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003..000000000 --- a/cw_monero/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/cw_monero/example/macos/Runner/AppDelegate.swift b/cw_monero/example/macos/Runner/AppDelegate.swift deleted file mode 100644 index d53ef6437..000000000 --- a/cw_monero/example/macos/Runner/AppDelegate.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Cocoa -import FlutterMacOS - -@NSApplicationMain -class AppDelegate: FlutterAppDelegate { - override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - return true - } -} diff --git a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index a2ec33f19..000000000 --- a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "images" : [ - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_16.png", - "scale" : "1x" - }, - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "2x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "1x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_64.png", - "scale" : "2x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_128.png", - "scale" : "1x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "2x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "1x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "2x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "1x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_1024.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png deleted file mode 100644 index 82b6f9d9a..000000000 Binary files a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png and /dev/null differ diff --git a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png deleted file mode 100644 index 13b35eba5..000000000 Binary files a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png and /dev/null differ diff --git a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png deleted file mode 100644 index 0a3f5fa40..000000000 Binary files a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png and /dev/null differ diff --git a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png deleted file mode 100644 index bdb57226d..000000000 Binary files a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png and /dev/null differ diff --git a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png deleted file mode 100644 index f083318e0..000000000 Binary files a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png and /dev/null differ diff --git a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png deleted file mode 100644 index 326c0e72c..000000000 Binary files a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png and /dev/null differ diff --git a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png deleted file mode 100644 index 2f1632cfd..000000000 Binary files a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png and /dev/null differ diff --git a/cw_monero/example/macos/Runner/Base.lproj/MainMenu.xib b/cw_monero/example/macos/Runner/Base.lproj/MainMenu.xib deleted file mode 100644 index 80e867a4e..000000000 --- a/cw_monero/example/macos/Runner/Base.lproj/MainMenu.xib +++ /dev/null @@ -1,343 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/cw_monero/example/macos/Runner/Configs/AppInfo.xcconfig b/cw_monero/example/macos/Runner/Configs/AppInfo.xcconfig deleted file mode 100644 index a80a25602..000000000 --- a/cw_monero/example/macos/Runner/Configs/AppInfo.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -// Application-level settings for the Runner target. -// -// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the -// future. If not, the values below would default to using the project name when this becomes a -// 'flutter create' template. - -// The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = cw_monero_example - -// The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.cakewallet.cwMoneroExample - -// The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2022 com.cakewallet. All rights reserved. diff --git a/cw_monero/example/macos/Runner/Configs/Debug.xcconfig b/cw_monero/example/macos/Runner/Configs/Debug.xcconfig deleted file mode 100644 index 36b0fd946..000000000 --- a/cw_monero/example/macos/Runner/Configs/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Debug.xcconfig" -#include "Warnings.xcconfig" diff --git a/cw_monero/example/macos/Runner/Configs/Release.xcconfig b/cw_monero/example/macos/Runner/Configs/Release.xcconfig deleted file mode 100644 index dff4f4956..000000000 --- a/cw_monero/example/macos/Runner/Configs/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Release.xcconfig" -#include "Warnings.xcconfig" diff --git a/cw_monero/example/macos/Runner/Configs/Warnings.xcconfig b/cw_monero/example/macos/Runner/Configs/Warnings.xcconfig deleted file mode 100644 index 42bcbf478..000000000 --- a/cw_monero/example/macos/Runner/Configs/Warnings.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings -GCC_WARN_UNDECLARED_SELECTOR = YES -CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES -CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN_PRAGMA_PACK = YES -CLANG_WARN_STRICT_PROTOTYPES = YES -CLANG_WARN_COMMA = YES -GCC_WARN_STRICT_SELECTOR_MATCH = YES -CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES -CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES -GCC_WARN_SHADOW = YES -CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/cw_monero/example/macos/Runner/Info.plist b/cw_monero/example/macos/Runner/Info.plist deleted file mode 100644 index 4789daa6a..000000000 --- a/cw_monero/example/macos/Runner/Info.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIconFile - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - NSHumanReadableCopyright - $(PRODUCT_COPYRIGHT) - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - - diff --git a/cw_monero/example/macos/Runner/MainFlutterWindow.swift b/cw_monero/example/macos/Runner/MainFlutterWindow.swift deleted file mode 100644 index 2722837ec..000000000 --- a/cw_monero/example/macos/Runner/MainFlutterWindow.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Cocoa -import FlutterMacOS - -class MainFlutterWindow: NSWindow { - override func awakeFromNib() { - let flutterViewController = FlutterViewController.init() - let windowFrame = self.frame - self.contentViewController = flutterViewController - self.setFrame(windowFrame, display: true) - - RegisterGeneratedPlugins(registry: flutterViewController) - - super.awakeFromNib() - } -} diff --git a/cw_monero/example/macos/Runner/Release.entitlements b/cw_monero/example/macos/Runner/Release.entitlements deleted file mode 100644 index 852fa1a47..000000000 --- a/cw_monero/example/macos/Runner/Release.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.security.app-sandbox - - - diff --git a/cw_monero/example/pubspec.yaml b/cw_monero/example/pubspec.yaml deleted file mode 100644 index 2dee5337f..000000000 --- a/cw_monero/example/pubspec.yaml +++ /dev/null @@ -1,84 +0,0 @@ -name: cw_monero_example -description: Demonstrates how to use the cw_monero plugin. - -# The following line prevents the package from being accidentally published to -# pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev - -environment: - sdk: '>=2.18.1 <3.0.0' - -# Dependencies specify other packages that your package needs in order to work. -# To automatically upgrade your package dependencies to the latest versions -# consider running `flutter pub upgrade --major-versions`. Alternatively, -# dependencies can be manually updated by changing the version numbers below to -# the latest version available on pub.dev. To see which dependencies have newer -# versions available, run `flutter pub outdated`. -dependencies: - flutter: - sdk: flutter - - cw_monero: - # When depending on this package from a real application you should use: - # cw_monero: ^x.y.z - # See https://dart.dev/tools/pub/dependencies#version-constraints - # The example app is bundled with the plugin so we use a path dependency on - # the parent directory to use the current plugin's version. - path: ../ - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - -dev_dependencies: - flutter_test: - sdk: flutter - - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. - flutter_lints: ^2.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. -flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # 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 diff --git a/cw_monero/example/test/widget_test.dart b/cw_monero/example/test/widget_test.dart deleted file mode 100644 index b37e6313d..000000000 --- a/cw_monero/example/test/widget_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:cw_monero_example/main.dart'; - -void main() { - testWidgets('Verify Platform version', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that platform version is retrieved. - expect( - find.byWidgetPredicate( - (Widget widget) => widget is Text && - widget.data!.startsWith('Running on:'), - ), - findsOneWidget, - ); - }); -} diff --git a/cw_monero/ios/.gitignore b/cw_monero/ios/.gitignore deleted file mode 100644 index aa479fd3c..000000000 --- a/cw_monero/ios/.gitignore +++ /dev/null @@ -1,37 +0,0 @@ -.idea/ -.vagrant/ -.sconsign.dblite -.svn/ - -.DS_Store -*.swp -profile - -DerivedData/ -build/ -GeneratedPluginRegistrant.h -GeneratedPluginRegistrant.m - -.generated/ - -*.pbxuser -*.mode1v3 -*.mode2v3 -*.perspectivev3 - -!default.pbxuser -!default.mode1v3 -!default.mode2v3 -!default.perspectivev3 - -xcuserdata - -*.moved-aside - -*.pyc -*sync/ -Icon? -.tags* - -/Flutter/Generated.xcconfig -/Flutter/flutter_export_environment.sh \ No newline at end of file diff --git a/cw_monero/ios/Classes/CwMoneroPlugin.h b/cw_monero/ios/Classes/CwMoneroPlugin.h deleted file mode 100644 index a42018098..000000000 --- a/cw_monero/ios/Classes/CwMoneroPlugin.h +++ /dev/null @@ -1,4 +0,0 @@ -#import - -@interface CwMoneroPlugin : NSObject -@end diff --git a/cw_monero/ios/Classes/CwMoneroPlugin.m b/cw_monero/ios/Classes/CwMoneroPlugin.m deleted file mode 100644 index eee251212..000000000 --- a/cw_monero/ios/Classes/CwMoneroPlugin.m +++ /dev/null @@ -1,8 +0,0 @@ -#import "CwMoneroPlugin.h" -#import - -@implementation CwMoneroPlugin -+ (void)registerWithRegistrar:(NSObject*)registrar { - [SwiftCwMoneroPlugin registerWithRegistrar:registrar]; -} -@end diff --git a/cw_monero/ios/Classes/CwWalletListener.h b/cw_monero/ios/Classes/CwWalletListener.h deleted file mode 100644 index cbfcb0c4e..000000000 --- a/cw_monero/ios/Classes/CwWalletListener.h +++ /dev/null @@ -1,23 +0,0 @@ -#include - -struct CWMoneroWalletListener; - -typedef int8_t (*on_new_block_callback)(uint64_t height); -typedef int8_t (*on_need_to_refresh_callback)(); - -typedef struct CWMoneroWalletListener -{ - // on_money_spent_callback *on_money_spent; - // on_money_received_callback *on_money_received; - // on_unconfirmed_money_received_callback *on_unconfirmed_money_received; - // on_new_block_callback *on_new_block; - // on_updated_callback *on_updated; - // on_refreshed_callback *on_refreshed; - - on_new_block_callback on_new_block; -} CWMoneroWalletListener; - -struct TestListener { - // int8_t x; - on_new_block_callback on_new_block; -}; \ No newline at end of file diff --git a/cw_monero/ios/Classes/SwiftCwMoneroPlugin.swift b/cw_monero/ios/Classes/SwiftCwMoneroPlugin.swift deleted file mode 100644 index 4c03a3e44..000000000 --- a/cw_monero/ios/Classes/SwiftCwMoneroPlugin.swift +++ /dev/null @@ -1,14 +0,0 @@ -import Flutter -import UIKit - -public class SwiftCwMoneroPlugin: NSObject, FlutterPlugin { - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "cw_monero", binaryMessenger: registrar.messenger()) - let instance = SwiftCwMoneroPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - result("iOS " + UIDevice.current.systemVersion) - } -} diff --git a/cw_monero/ios/Classes/monero_api.cpp b/cw_monero/ios/Classes/monero_api.cpp deleted file mode 100644 index a2a17bd5e..000000000 --- a/cw_monero/ios/Classes/monero_api.cpp +++ /dev/null @@ -1,1044 +0,0 @@ -#include -#include "cstdlib" -#include -#include -#include -#include -#include -#include -#include -#include "thread" -#include "CwWalletListener.h" -#if __APPLE__ -// Fix for randomx on ios -void __clear_cache(void* start, void* end) { } -#include "../External/ios/include/wallet2_api.h" -#else -#include "../External/android/include/wallet2_api.h" -#endif - -using namespace std::chrono_literals; -#ifdef __cplusplus -extern "C" -{ -#endif - const uint64_t MONERO_BLOCK_SIZE = 1000; - - struct Utf8Box - { - char *value; - - Utf8Box(char *_value) - { - value = _value; - } - }; - - struct SubaddressRow - { - uint64_t id; - char *address; - char *label; - - SubaddressRow(std::size_t _id, char *_address, char *_label) - { - id = static_cast(_id); - address = _address; - label = _label; - } - }; - - struct AccountRow - { - uint64_t id; - char *label; - - AccountRow(std::size_t _id, char *_label) - { - id = static_cast(_id); - label = _label; - } - }; - - struct MoneroWalletListener : Monero::WalletListener - { - uint64_t m_height; - bool m_need_to_refresh; - bool m_new_transaction; - - MoneroWalletListener() - { - m_height = 0; - m_need_to_refresh = false; - m_new_transaction = false; - } - - void moneySpent(const std::string &txId, uint64_t amount) - { - m_new_transaction = true; - } - - void moneyReceived(const std::string &txId, uint64_t amount) - { - m_new_transaction = true; - } - - void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) - { - m_new_transaction = true; - } - - void newBlock(uint64_t height) - { - m_height = height; - } - - void updated() - { - m_new_transaction = true; - } - - void refreshed() - { - m_need_to_refresh = true; - } - - void resetNeedToRefresh() - { - m_need_to_refresh = false; - } - - bool isNeedToRefresh() - { - return m_need_to_refresh; - } - - bool isNewTransactionExist() - { - return m_new_transaction; - } - - void resetIsNewTransactionExist() - { - m_new_transaction = false; - } - - uint64_t height() - { - return m_height; - } - }; - - struct TransactionInfoRow - { - uint64_t amount; - uint64_t fee; - uint64_t blockHeight; - uint64_t confirmations; - uint32_t subaddrAccount; - int8_t direction; - int8_t isPending; - uint32_t subaddrIndex; - - char *hash; - char *paymentId; - - int64_t datetime; - - TransactionInfoRow(Monero::TransactionInfo *transaction) - { - amount = transaction->amount(); - fee = transaction->fee(); - blockHeight = transaction->blockHeight(); - subaddrAccount = transaction->subaddrAccount(); - std::set::iterator it = transaction->subaddrIndex().begin(); - subaddrIndex = *it; - confirmations = transaction->confirmations(); - datetime = static_cast(transaction->timestamp()); - direction = transaction->direction(); - isPending = static_cast(transaction->isPending()); - std::string *hash_str = new std::string(transaction->hash()); - hash = strdup(hash_str->c_str()); - paymentId = strdup(transaction->paymentId().c_str()); - } - }; - - struct PendingTransactionRaw - { - uint64_t amount; - uint64_t fee; - char *hash; - char *hex; - char *txKey; - Monero::PendingTransaction *transaction; - - PendingTransactionRaw(Monero::PendingTransaction *_transaction) - { - transaction = _transaction; - amount = _transaction->amount(); - fee = _transaction->fee(); - hash = strdup(_transaction->txid()[0].c_str()); - hex = strdup(_transaction->hex()[0].c_str()); - txKey = strdup(_transaction->txKey()[0].c_str()); - } - }; - - struct CoinsInfoRow - { - uint64_t blockHeight; - char *hash; - uint64_t internalOutputIndex; - uint64_t globalOutputIndex; - bool spent; - bool frozen; - uint64_t spentHeight; - uint64_t amount; - bool rct; - bool keyImageKnown; - uint64_t pkIndex; - uint32_t subaddrIndex; - uint32_t subaddrAccount; - char *address; - char *addressLabel; - char *keyImage; - uint64_t unlockTime; - bool unlocked; - char *pubKey; - bool coinbase; - char *description; - - CoinsInfoRow(Monero::CoinsInfo *coinsInfo) - { - blockHeight = coinsInfo->blockHeight(); - std::string *hash_str = new std::string(coinsInfo->hash()); - hash = strdup(hash_str->c_str()); - internalOutputIndex = coinsInfo->internalOutputIndex(); - globalOutputIndex = coinsInfo->globalOutputIndex(); - spent = coinsInfo->spent(); - frozen = coinsInfo->frozen(); - spentHeight = coinsInfo->spentHeight(); - amount = coinsInfo->amount(); - rct = coinsInfo->rct(); - keyImageKnown = coinsInfo->keyImageKnown(); - pkIndex = coinsInfo->pkIndex(); - subaddrIndex = coinsInfo->subaddrIndex(); - subaddrAccount = coinsInfo->subaddrAccount(); - address = strdup(coinsInfo->address().c_str()) ; - addressLabel = strdup(coinsInfo->addressLabel().c_str()); - keyImage = strdup(coinsInfo->keyImage().c_str()); - unlockTime = coinsInfo->unlockTime(); - unlocked = coinsInfo->unlocked(); - pubKey = strdup(coinsInfo->pubKey().c_str()); - coinbase = coinsInfo->coinbase(); - description = strdup(coinsInfo->description().c_str()); - } - - void setUnlocked(bool unlocked); - }; - - Monero::Coins *m_coins; - - Monero::Wallet *m_wallet; - Monero::TransactionHistory *m_transaction_history; - MoneroWalletListener *m_listener; - Monero::Subaddress *m_subaddress; - Monero::SubaddressAccount *m_account; - uint64_t m_last_known_wallet_height; - uint64_t m_cached_syncing_blockchain_height = 0; - std::list m_coins_info; - std::mutex store_lock; - bool is_storing = false; - - void change_current_wallet(Monero::Wallet *wallet) - { - m_wallet = wallet; - m_listener = nullptr; - - - if (wallet != nullptr) - { - m_transaction_history = wallet->history(); - } - else - { - m_transaction_history = nullptr; - } - - if (wallet != nullptr) - { - m_account = wallet->subaddressAccount(); - } - else - { - m_account = nullptr; - } - - if (wallet != nullptr) - { - m_subaddress = wallet->subaddress(); - } - else - { - m_subaddress = nullptr; - } - - m_coins_info = std::list(); - - if (wallet != nullptr) - { - m_coins = wallet->coins(); - } - else - { - m_coins = nullptr; - } - } - - Monero::Wallet *get_current_wallet() - { - return m_wallet; - } - - bool create_wallet(char *path, char *password, char *language, int32_t networkType, char *error) - { - Monero::NetworkType _networkType = static_cast(networkType); - Monero::WalletManager *walletManager = Monero::WalletManagerFactory::getWalletManager(); - Monero::Wallet *wallet = walletManager->createWallet(path, password, language, _networkType); - - int status; - std::string errorString; - - wallet->statusWithErrorString(status, errorString); - - if (wallet->status() != Monero::Wallet::Status_Ok) - { - error = strdup(wallet->errorString().c_str()); - return false; - } - - change_current_wallet(wallet); - - return true; - } - - bool restore_wallet_from_seed(char *path, char *password, char *seed, int32_t networkType, uint64_t restoreHeight, char *error) - { - Monero::NetworkType _networkType = static_cast(networkType); - Monero::Wallet *wallet = Monero::WalletManagerFactory::getWalletManager()->recoveryWallet( - std::string(path), - std::string(password), - std::string(seed), - _networkType, - (uint64_t)restoreHeight); - - int status; - std::string errorString; - - wallet->statusWithErrorString(status, errorString); - - if (status != Monero::Wallet::Status_Ok || !errorString.empty()) - { - error = strdup(errorString.c_str()); - return false; - } - - change_current_wallet(wallet); - return true; - } - - bool restore_wallet_from_keys(char *path, char *password, char *language, char *address, char *viewKey, char *spendKey, int32_t networkType, uint64_t restoreHeight, char *error) - { - Monero::NetworkType _networkType = static_cast(networkType); - Monero::Wallet *wallet = Monero::WalletManagerFactory::getWalletManager()->createWalletFromKeys( - std::string(path), - std::string(password), - std::string(language), - _networkType, - (uint64_t)restoreHeight, - std::string(address), - std::string(viewKey), - std::string(spendKey)); - - int status; - std::string errorString; - - wallet->statusWithErrorString(status, errorString); - - if (status != Monero::Wallet::Status_Ok || !errorString.empty()) - { - error = strdup(errorString.c_str()); - return false; - } - - change_current_wallet(wallet); - return true; - } - - bool restore_wallet_from_spend_key(char *path, char *password, char *seed, char *language, char *spendKey, int32_t networkType, uint64_t restoreHeight, char *error) - { - Monero::NetworkType _networkType = static_cast(networkType); - Monero::Wallet *wallet = Monero::WalletManagerFactory::getWalletManager()->createDeterministicWalletFromSpendKey( - std::string(path), - std::string(password), - std::string(language), - _networkType, - (uint64_t)restoreHeight, - std::string(spendKey)); - - // Cache Raw to support Polyseed - wallet->setCacheAttribute("cakewallet.seed", std::string(seed)); - - int status; - std::string errorString; - - wallet->statusWithErrorString(status, errorString); - - if (status != Monero::Wallet::Status_Ok || !errorString.empty()) - { - error = strdup(errorString.c_str()); - return false; - } - - change_current_wallet(wallet); - return true; - } - - bool load_wallet(char *path, char *password, int32_t nettype) - { - nice(19); - Monero::NetworkType networkType = static_cast(nettype); - Monero::WalletManager *walletManager = Monero::WalletManagerFactory::getWalletManager(); - Monero::Wallet *wallet = walletManager->openWallet(std::string(path), std::string(password), networkType); - int status; - std::string errorString; - - wallet->statusWithErrorString(status, errorString); - change_current_wallet(wallet); - - return !(status != Monero::Wallet::Status_Ok || !errorString.empty()); - } - - char *error_string() { - return strdup(get_current_wallet()->errorString().c_str()); - } - - - bool is_wallet_exist(char *path) - { - return Monero::WalletManagerFactory::getWalletManager()->walletExists(std::string(path)); - } - - void close_current_wallet() - { - Monero::WalletManagerFactory::getWalletManager()->closeWallet(get_current_wallet()); - change_current_wallet(nullptr); - } - - char *get_filename() - { - return strdup(get_current_wallet()->filename().c_str()); - } - - char *secret_view_key() - { - return strdup(get_current_wallet()->secretViewKey().c_str()); - } - - char *public_view_key() - { - return strdup(get_current_wallet()->publicViewKey().c_str()); - } - - char *secret_spend_key() - { - return strdup(get_current_wallet()->secretSpendKey().c_str()); - } - - char *public_spend_key() - { - return strdup(get_current_wallet()->publicSpendKey().c_str()); - } - - char *get_address(uint32_t account_index, uint32_t address_index) - { - return strdup(get_current_wallet()->address(account_index, address_index).c_str()); - } - - char *get_cache_attribute(char *name) - { - return strdup(get_current_wallet()->getCacheAttribute(std::string(name)).c_str()); - } - - bool set_cache_attribute(char *name, char *value) - { - get_current_wallet()->setCacheAttribute(std::string(name), std::string(value)); - return true; - } - - const char *seed() - { - std::string _rawSeed = get_current_wallet()->getCacheAttribute("cakewallet.seed"); - if (!_rawSeed.empty()) - { - return strdup(_rawSeed.c_str()); - } - return strdup(get_current_wallet()->seed().c_str()); - } - - uint64_t get_full_balance(uint32_t account_index) - { - return get_current_wallet()->balance(account_index); - } - - uint64_t get_unlocked_balance(uint32_t account_index) - { - return get_current_wallet()->unlockedBalance(account_index); - } - - uint64_t get_current_height() - { - return get_current_wallet()->blockChainHeight(); - } - - uint64_t get_node_height() - { - return get_current_wallet()->daemonBlockChainHeight(); - } - - bool connect_to_node(char *error) - { - nice(19); - bool is_connected = get_current_wallet()->connectToDaemon(); - - if (!is_connected) - { - error = strdup(get_current_wallet()->errorString().c_str()); - } - - return is_connected; - } - - bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *socksProxyAddress, char *error) - { - nice(19); - Monero::Wallet *wallet = get_current_wallet(); - - std::string _login = ""; - std::string _password = ""; - std::string _socksProxyAddress = ""; - - if (login != nullptr) - { - _login = std::string(login); - } - - if (password != nullptr) - { - _password = std::string(password); - } - - if (socksProxyAddress != nullptr) - { - _socksProxyAddress = std::string(socksProxyAddress); - } - - bool inited = wallet->init(std::string(address), 0, _login, _password, use_ssl, is_light_wallet, _socksProxyAddress); - - if (!inited) - { - error = strdup(wallet->errorString().c_str()); - } else if (!wallet->connectToDaemon()) { - error = strdup(wallet->errorString().c_str()); - } - - return inited; - } - - bool is_connected() - { - return get_current_wallet()->connected(); - } - - void start_refresh() - { - get_current_wallet()->refreshAsync(); - get_current_wallet()->startRefresh(); - } - - void set_refresh_from_block_height(uint64_t height) - { - get_current_wallet()->setRefreshFromBlockHeight(height); - } - - void set_recovering_from_seed(bool is_recovery) - { - get_current_wallet()->setRecoveringFromSeed(is_recovery); - } - - void store(char *path) - { - store_lock.lock(); - if (is_storing) { - return; - } - - is_storing = true; - get_current_wallet()->store(std::string(path)); - is_storing = false; - store_lock.unlock(); - } - - bool set_password(char *password, Utf8Box &error) { - bool is_changed = get_current_wallet()->setPassword(std::string(password)); - - if (!is_changed) { - error = Utf8Box(strdup(get_current_wallet()->errorString().c_str())); - } - - return is_changed; - } - - bool transaction_create(char *address, char *payment_id, char *amount, - uint8_t priority_raw, uint32_t subaddr_account, - char **preferred_inputs, uint32_t preferred_inputs_size, - Utf8Box &error, PendingTransactionRaw &pendingTransaction) - { - nice(19); - - std::set _preferred_inputs; - - for (int i = 0; i < preferred_inputs_size; i++) { - _preferred_inputs.insert(std::string(*preferred_inputs)); - preferred_inputs++; - } - - auto priority = static_cast(priority_raw); - std::string _payment_id; - Monero::PendingTransaction *transaction; - - if (payment_id != nullptr) - { - _payment_id = std::string(payment_id); - } - - if (amount != nullptr) - { - uint64_t _amount = Monero::Wallet::amountFromString(std::string(amount)); - transaction = m_wallet->createTransaction(std::string(address), _payment_id, _amount, m_wallet->defaultMixin(), priority, subaddr_account, {}, _preferred_inputs); - } - else - { - transaction = m_wallet->createTransaction(std::string(address), _payment_id, Monero::optional(), m_wallet->defaultMixin(), priority, subaddr_account, {}, _preferred_inputs); - } - - int status = transaction->status(); - - if (status == Monero::PendingTransaction::Status::Status_Error || status == Monero::PendingTransaction::Status::Status_Critical) - { - error = Utf8Box(strdup(transaction->errorString().c_str())); - return false; - } - - if (m_listener != nullptr) { - m_listener->m_new_transaction = true; - } - - pendingTransaction = PendingTransactionRaw(transaction); - return true; - } - - bool transaction_create_mult_dest(char **addresses, char *payment_id, char **amounts, uint32_t size, - uint8_t priority_raw, uint32_t subaddr_account, - char **preferred_inputs, uint32_t preferred_inputs_size, - Utf8Box &error, PendingTransactionRaw &pendingTransaction) - { - nice(19); - - std::vector _addresses; - std::vector _amounts; - - for (int i = 0; i < size; i++) { - _addresses.push_back(std::string(*addresses)); - _amounts.push_back(Monero::Wallet::amountFromString(std::string(*amounts))); - addresses++; - amounts++; - } - - std::set _preferred_inputs; - - for (int i = 0; i < preferred_inputs_size; i++) { - _preferred_inputs.insert(std::string(*preferred_inputs)); - preferred_inputs++; - } - - auto priority = static_cast(priority_raw); - std::string _payment_id; - Monero::PendingTransaction *transaction; - - if (payment_id != nullptr) - { - _payment_id = std::string(payment_id); - } - - transaction = m_wallet->createTransactionMultDest(_addresses, _payment_id, _amounts, m_wallet->defaultMixin(), priority, subaddr_account); - - int status = transaction->status(); - - if (status == Monero::PendingTransaction::Status::Status_Error || status == Monero::PendingTransaction::Status::Status_Critical) - { - error = Utf8Box(strdup(transaction->errorString().c_str())); - return false; - } - - if (m_listener != nullptr) { - m_listener->m_new_transaction = true; - } - - pendingTransaction = PendingTransactionRaw(transaction); - return true; - } - - bool transaction_commit(PendingTransactionRaw *transaction, Utf8Box &error) - { - bool committed = transaction->transaction->commit(); - - if (!committed) - { - error = Utf8Box(strdup(transaction->transaction->errorString().c_str())); - } else if (m_listener != nullptr) { - m_listener->m_new_transaction = true; - } - - return committed; - } - - uint64_t get_node_height_or_update(uint64_t base_eight) - { - if (m_cached_syncing_blockchain_height < base_eight) { - m_cached_syncing_blockchain_height = base_eight; - } - - return m_cached_syncing_blockchain_height; - } - - uint64_t get_syncing_height() - { - if (m_listener == nullptr) { - return 0; - } - - uint64_t height = m_listener->height(); - - if (height <= 1) { - return 0; - } - - if (height != m_last_known_wallet_height) - { - m_last_known_wallet_height = height; - } - - return height; - } - - uint64_t is_needed_to_refresh() - { - if (m_listener == nullptr) { - return false; - } - - bool should_refresh = m_listener->isNeedToRefresh(); - - if (should_refresh) { - m_listener->resetNeedToRefresh(); - } - - return should_refresh; - } - - uint8_t is_new_transaction_exist() - { - if (m_listener == nullptr) { - return false; - } - - bool is_new_transaction_exist = m_listener->isNewTransactionExist(); - - if (is_new_transaction_exist) - { - m_listener->resetIsNewTransactionExist(); - } - - return is_new_transaction_exist; - } - - void set_listener() - { - m_last_known_wallet_height = 0; - - if (m_listener != nullptr) - { - free(m_listener); - } - - m_listener = new MoneroWalletListener(); - get_current_wallet()->setListener(m_listener); - } - - int64_t *subaddrress_get_all() - { - std::vector _subaddresses = m_subaddress->getAll(); - size_t size = _subaddresses.size(); - int64_t *subaddresses = (int64_t *)malloc(size * sizeof(int64_t)); - - for (int i = 0; i < size; i++) - { - Monero::SubaddressRow *row = _subaddresses[i]; - SubaddressRow *_row = new SubaddressRow(row->getRowId(), strdup(row->getAddress().c_str()), strdup(row->getLabel().c_str())); - subaddresses[i] = reinterpret_cast(_row); - } - - return subaddresses; - } - - int32_t subaddrress_size() - { - std::vector _subaddresses = m_subaddress->getAll(); - return _subaddresses.size(); - } - - void subaddress_add_row(uint32_t accountIndex, char *label) - { - m_subaddress->addRow(accountIndex, std::string(label)); - } - - void subaddress_set_label(uint32_t accountIndex, uint32_t addressIndex, char *label) - { - m_subaddress->setLabel(accountIndex, addressIndex, std::string(label)); - } - - void subaddress_refresh(uint32_t accountIndex) - { - m_subaddress->refresh(accountIndex); - } - - int32_t account_size() - { - std::vector _accocunts = m_account->getAll(); - return _accocunts.size(); - } - - int64_t *account_get_all() - { - std::vector _accocunts = m_account->getAll(); - size_t size = _accocunts.size(); - int64_t *accocunts = (int64_t *)malloc(size * sizeof(int64_t)); - - for (int i = 0; i < size; i++) - { - Monero::SubaddressAccountRow *row = _accocunts[i]; - AccountRow *_row = new AccountRow(row->getRowId(), strdup(row->getLabel().c_str())); - accocunts[i] = reinterpret_cast(_row); - } - - return accocunts; - } - - void account_add_row(char *label) - { - m_account->addRow(std::string(label)); - } - - void account_set_label_row(uint32_t account_index, char *label) - { - m_account->setLabel(account_index, label); - } - - void account_refresh() - { - m_account->refresh(); - } - - int64_t *transactions_get_all() - { - std::vector transactions = m_transaction_history->getAll(); - size_t size = transactions.size(); - int64_t *transactionAddresses = (int64_t *)malloc(size * sizeof(int64_t)); - - for (int i = 0; i < size; i++) - { - Monero::TransactionInfo *row = transactions[i]; - TransactionInfoRow *tx = new TransactionInfoRow(row); - transactionAddresses[i] = reinterpret_cast(tx); - } - - return transactionAddresses; - } - - void transactions_refresh() - { - m_transaction_history->refresh(); - } - - int64_t transactions_count() - { - return m_transaction_history->count(); - } - - TransactionInfoRow* get_transaction(char * txId) - { - Monero::TransactionInfo *row = m_transaction_history->transaction(std::string(txId)); - return new TransactionInfoRow(row); - } - - int LedgerExchange( - unsigned char *command, - unsigned int cmd_len, - unsigned char *response, - unsigned int max_resp_len) - { - return -1; - } - - int LedgerFind(char *buffer, size_t len) - { - return -1; - } - - void on_startup() - { - Monero::Utils::onStartup(); - Monero::WalletManagerFactory::setLogLevel(0); - } - - void rescan_blockchain() - { - m_wallet->rescanBlockchainAsync(); - } - - char * get_tx_key(char * txId) - { - return strdup(m_wallet->getTxKey(std::string(txId)).c_str()); - } - - char *get_subaddress_label(uint32_t accountIndex, uint32_t addressIndex) - { - return strdup(get_current_wallet()->getSubaddressLabel(accountIndex, addressIndex).c_str()); - } - - void set_trusted_daemon(bool arg) - { - m_wallet->setTrustedDaemon(arg); - } - - bool trusted_daemon() - { - return m_wallet->trustedDaemon(); - } - - // Coin Control // - - CoinsInfoRow* coin(int index) - { - if (index >= 0 && index < m_coins_info.size()) { - std::list::iterator it = m_coins_info.begin(); - std::advance(it, index); - Monero::CoinsInfo* element = *it; - std::cout << "Element at index " << index << ": " << element << std::endl; - return new CoinsInfoRow(element); - } else { - std::cout << "Invalid index." << std::endl; - return nullptr; // Return a default value (nullptr) for invalid index - } - } - - void refresh_coins(uint32_t accountIndex) - { - m_coins_info.clear(); - - m_coins->refresh(); - for (const auto i : m_coins->getAll()) { - if (i->subaddrAccount() == accountIndex && !(i->spent())) { - m_coins_info.push_back(i); - } - } - } - - uint64_t coins_count() - { - return m_coins_info.size(); - } - - CoinsInfoRow** coins_from_account(uint32_t accountIndex) - { - std::vector matchingCoins; - - for (int i = 0; i < coins_count(); i++) { - CoinsInfoRow* coinInfo = coin(i); - if (coinInfo->subaddrAccount == accountIndex) { - matchingCoins.push_back(coinInfo); - } - } - - CoinsInfoRow** result = new CoinsInfoRow*[matchingCoins.size()]; - std::copy(matchingCoins.begin(), matchingCoins.end(), result); - return result; - } - - CoinsInfoRow** coins_from_txid(const char* txid, size_t* count) - { - std::vector matchingCoins; - - for (int i = 0; i < coins_count(); i++) { - CoinsInfoRow* coinInfo = coin(i); - if (std::string(coinInfo->hash) == txid) { - matchingCoins.push_back(coinInfo); - } - } - - *count = matchingCoins.size(); - CoinsInfoRow** result = new CoinsInfoRow*[*count]; - std::copy(matchingCoins.begin(), matchingCoins.end(), result); - return result; - } - - CoinsInfoRow** coins_from_key_image(const char** keyimages, size_t keyimageCount, size_t* count) - { - std::vector matchingCoins; - - for (int i = 0; i < coins_count(); i++) { - CoinsInfoRow* coinsInfoRow = coin(i); - for (size_t j = 0; j < keyimageCount; j++) { - if (coinsInfoRow->keyImageKnown && std::string(coinsInfoRow->keyImage) == keyimages[j]) { - matchingCoins.push_back(coinsInfoRow); - break; - } - } - } - - *count = matchingCoins.size(); - CoinsInfoRow** result = new CoinsInfoRow*[*count]; - std::copy(matchingCoins.begin(), matchingCoins.end(), result); - return result; - } - - void freeze_coin(int index) - { - m_coins->setFrozen(index); - } - - void thaw_coin(int index) - { - m_coins->thaw(index); - } - - // Sign Messages // - - char *sign_message(char *message, char *address = "") - { - return strdup(get_current_wallet()->signMessage(std::string(message), std::string(address)).c_str()); - } - -#ifdef __cplusplus -} -#endif diff --git a/cw_monero/ios/Classes/monero_api.h b/cw_monero/ios/Classes/monero_api.h deleted file mode 100644 index fa92a038d..000000000 --- a/cw_monero/ios/Classes/monero_api.h +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include -#include -#include "CwWalletListener.h" - -#ifdef __cplusplus -extern "C" { -#endif - -bool create_wallet(char *path, char *password, char *language, int32_t networkType, char *error); -bool restore_wallet_from_seed(char *path, char *password, char *seed, int32_t networkType, uint64_t restoreHeight, char *error); -bool restore_wallet_from_keys(char *path, char *password, char *language, char *address, char *viewKey, char *spendKey, int32_t networkType, uint64_t restoreHeight, char *error); -void load_wallet(char *path, char *password, int32_t nettype); -bool is_wallet_exist(char *path); - -char *get_filename(); -const char *seed(); -char *get_address(uint32_t account_index, uint32_t address_index); -uint64_t get_full_balance(uint32_t account_index); -uint64_t get_unlocked_balance(uint32_t account_index); -uint64_t get_current_height(); -uint64_t get_node_height(); - -bool is_connected(); - -bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *error); -bool connect_to_node(char *error); -void start_refresh(); -void set_refresh_from_block_height(uint64_t height); -void set_recovering_from_seed(bool is_recovery); -void store(char *path); - -void set_trusted_daemon(bool arg); -bool trusted_daemon(); -char *sign_message(char *message, char *address); - -#ifdef __cplusplus -} -#endif diff --git a/cw_monero/ios/cw_monero.podspec b/cw_monero/ios/cw_monero.podspec deleted file mode 100644 index d99bba923..000000000 --- a/cw_monero/ios/cw_monero.podspec +++ /dev/null @@ -1,62 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint cw_monero.podspec' to validate before publishing. -# -Pod::Spec.new do |s| - s.name = 'cw_monero' - s.version = '0.0.2' - s.summary = 'CW Monero' - s.description = 'Cake Wallet wrapper over Monero project.' - s.homepage = 'http://cakewallet.com' - s.license = { :file => '../LICENSE' } - s.author = { 'CakeWallet' => 'support@cakewallet.com' } - s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - s.public_header_files = 'Classes/**/*.h, Classes/*.h, External/ios/libs/monero/include/External/ios/**/*.h' - s.dependency 'Flutter' - s.dependency 'cw_shared_external' - s.platform = :ios, '10.0' - s.swift_version = '4.0' - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS' => 'arm64', 'ENABLE_BITCODE' => 'NO' } - s.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/Classes/*.h" } - - s.subspec 'OpenSSL' do |openssl| - openssl.preserve_paths = '../../../../../cw_shared_external/ios/External/ios/include/**/*.h' - openssl.vendored_libraries = '../../../../../cw_shared_external/ios/External/ios/lib/libcrypto.a', '../../../../../cw_shared_external/ios/External/ios/lib/libssl.a' - openssl.libraries = 'ssl', 'crypto' - openssl.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/ios/include/**" } - end - - s.subspec 'Sodium' do |sodium| - sodium.preserve_paths = '../../../../../cw_shared_external/ios/External/ios/include/**/*.h' - sodium.vendored_libraries = '../../../../../cw_shared_external/ios/External/ios/lib/libsodium.a' - sodium.libraries = 'sodium' - sodium.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/ios/include/**" } - end - - s.subspec 'Unbound' do |unbound| - unbound.preserve_paths = '../../../../../cw_shared_external/ios/External/ios/include/**/*.h' - unbound.vendored_libraries = '../../../../../cw_shared_external/ios/External/ios/lib/libunbound.a' - unbound.libraries = 'unbound' - unbound.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/ios/include/**" } - end - - s.subspec 'Boost' do |boost| - boost.preserve_paths = '../../../../../cw_shared_external/ios/External/ios/include/**/*.h', - boost.vendored_libraries = '../../../../../cw_shared_external/ios/External/ios/lib/libboost.a', - boost.libraries = 'boost' - boost.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/ios/include/**" } - end - - s.subspec 'Monero' do |monero| - monero.preserve_paths = 'External/ios/include/**/*.h' - monero.vendored_libraries = 'External/ios/lib/libmonero.a' - monero.libraries = 'monero' - monero.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/ios/include" } - end - - # s.subspec 'lmdb' do |lmdb| - # lmdb.vendored_libraries = 'External/ios/lib/liblmdb.a' - # lmdb.libraries = 'lmdb' - # end -end diff --git a/cw_monero/lib/api/account_list.dart b/cw_monero/lib/api/account_list.dart index 451ba5033..199896631 100644 --- a/cw_monero/lib/api/account_list.dart +++ b/cw_monero/lib/api/account_list.dart @@ -1,38 +1,28 @@ -import 'dart:ffi'; -import 'package:ffi/ffi.dart'; -import 'package:cw_monero/api/signatures.dart'; -import 'package:cw_monero/api/types.dart'; -import 'package:cw_monero/api/monero_api.dart'; -import 'package:cw_monero/api/structs/account_row.dart'; -import 'package:flutter/foundation.dart'; import 'package:cw_monero/api/wallet.dart'; +import 'package:monero/monero.dart' as monero; -final accountSizeNative = moneroApi - .lookup>('account_size') - .asFunction(); +monero.wallet? wptr = null; -final accountRefreshNative = moneroApi - .lookup>('account_refresh') - .asFunction(); +int _wlptrForW = 0; +monero.WalletListener? _wlptr = null; -final accountGetAllNative = moneroApi - .lookup>('account_get_all') - .asFunction(); +monero.WalletListener getWlptr() { + if (wptr!.address == _wlptrForW) return _wlptr!; + _wlptrForW = wptr!.address; + _wlptr = monero.MONERO_cw_getWalletListener(wptr!); + return _wlptr!; +} -final accountAddNewNative = moneroApi - .lookup>('account_add_row') - .asFunction(); -final accountSetLabelNative = moneroApi - .lookup>('account_set_label_row') - .asFunction(); +monero.SubaddressAccount? subaddressAccount; bool isUpdating = false; void refreshAccounts() { try { isUpdating = true; - accountRefreshNative(); + subaddressAccount = monero.Wallet_subaddressAccount(wptr!); + monero.SubaddressAccount_refresh(subaddressAccount!); isUpdating = false; } catch (e) { isUpdating = false; @@ -40,26 +30,27 @@ void refreshAccounts() { } } -List getAllAccount() { - final size = accountSizeNative(); - final accountAddressesPointer = accountGetAllNative(); - final accountAddresses = accountAddressesPointer.asTypedList(size); - - return accountAddresses - .map((addr) => Pointer.fromAddress(addr).ref) - .toList(); +List getAllAccount() { + // final size = monero.Wallet_numSubaddressAccounts(wptr!); + refreshAccounts(); + int size = monero.SubaddressAccount_getAll_size(subaddressAccount!); + print("size: $size"); + if (size == 0) { + monero.Wallet_addSubaddressAccount(wptr!); + return getAllAccount(); + } + return List.generate(size, (index) { + return monero.SubaddressAccount_getAll_byIndex(subaddressAccount!, index: index); + }); } void addAccountSync({required String label}) { - final labelPointer = label.toNativeUtf8(); - accountAddNewNative(labelPointer); - calloc.free(labelPointer); + monero.Wallet_addSubaddressAccount(wptr!, label: label); } void setLabelForAccountSync({required int accountIndex, required String label}) { - final labelPointer = label.toNativeUtf8(); - accountSetLabelNative(accountIndex, labelPointer); - calloc.free(labelPointer); + // TODO(mrcyjanek): this may be wrong function? + monero.Wallet_setSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: 0, label: label); } void _addAccount(String label) => addAccountSync(label: label); @@ -72,12 +63,11 @@ void _setLabelForAccount(Map args) { } Future addAccount({required String label}) async { - await compute(_addAccount, label); + _addAccount(label); await store(); } Future setLabelForAccount({required int accountIndex, required String label}) async { - await compute( - _setLabelForAccount, {'accountIndex': accountIndex, 'label': label}); + _setLabelForAccount({'accountIndex': accountIndex, 'label': label}); await store(); } \ No newline at end of file diff --git a/cw_monero/lib/api/coins_info.dart b/cw_monero/lib/api/coins_info.dart index d7350a6e2..c1b634cc6 100644 --- a/cw_monero/lib/api/coins_info.dart +++ b/cw_monero/lib/api/coins_info.dart @@ -1,35 +1,17 @@ -import 'dart:ffi'; -import 'package:cw_monero/api/signatures.dart'; -import 'package:cw_monero/api/structs/coins_info_row.dart'; -import 'package:cw_monero/api/types.dart'; -import 'package:cw_monero/api/monero_api.dart'; +import 'package:cw_monero/api/account_list.dart'; +import 'package:monero/monero.dart' as monero; -final refreshCoinsNative = moneroApi - .lookup>('refresh_coins') - .asFunction(); +monero.Coins? coins = null; -final coinsCountNative = moneroApi - .lookup>('coins_count') - .asFunction(); +void refreshCoins(int accountIndex) { + coins = monero.Wallet_coins(wptr!); + monero.Coins_refresh(coins!); +} -final coinNative = moneroApi - .lookup>('coin') - .asFunction(); +int countOfCoins() => monero.Coins_count(coins!); -final freezeCoinNative = moneroApi - .lookup>('freeze_coin') - .asFunction(); +monero.CoinsInfo getCoin(int index) => monero.Coins_coin(coins!, index); -final thawCoinNative = moneroApi - .lookup>('thaw_coin') - .asFunction(); +void freezeCoin(int index) => monero.Coins_setFrozen(coins!, index: index); -void refreshCoins(int accountIndex) => refreshCoinsNative(accountIndex); - -int countOfCoins() => coinsCountNative(); - -CoinsInfoRow getCoin(int index) => coinNative(index).ref; - -void freezeCoin(int index) => freezeCoinNative(index); - -void thawCoin(int index) => thawCoinNative(index); +void thawCoin(int index) => monero.Coins_thaw(coins!, index: index); diff --git a/cw_monero/lib/api/convert_utf8_to_string.dart b/cw_monero/lib/api/convert_utf8_to_string.dart deleted file mode 100644 index 41a6b648a..000000000 --- a/cw_monero/lib/api/convert_utf8_to_string.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'dart:ffi'; -import 'package:ffi/ffi.dart'; - -String convertUTF8ToString({required Pointer pointer}) { - final str = pointer.toDartString(); - calloc.free(pointer); - return str; -} \ No newline at end of file diff --git a/cw_monero/lib/api/monero_api.dart b/cw_monero/lib/api/monero_api.dart deleted file mode 100644 index 398d737d1..000000000 --- a/cw_monero/lib/api/monero_api.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'dart:ffi'; -import 'dart:io'; - -final DynamicLibrary moneroApi = Platform.isAndroid - ? DynamicLibrary.open("libcw_monero.so") - : DynamicLibrary.open("cw_monero.framework/cw_monero"); \ No newline at end of file diff --git a/cw_monero/lib/api/signatures.dart b/cw_monero/lib/api/signatures.dart deleted file mode 100644 index 40f338c8c..000000000 --- a/cw_monero/lib/api/signatures.dart +++ /dev/null @@ -1,160 +0,0 @@ -import 'dart:ffi'; -import 'package:cw_monero/api/structs/coins_info_row.dart'; -import 'package:cw_monero/api/structs/pending_transaction.dart'; -import 'package:cw_monero/api/structs/transaction_info_row.dart'; -import 'package:cw_monero/api/structs/ut8_box.dart'; -import 'package:ffi/ffi.dart'; - -typedef create_wallet = Int8 Function( - Pointer, Pointer, Pointer, Int32, Pointer); - -typedef restore_wallet_from_seed = Int8 Function( - Pointer, Pointer, Pointer, Int32, Int64, Pointer); - -typedef restore_wallet_from_keys = Int8 Function(Pointer, Pointer, Pointer, - Pointer, Pointer, Pointer, Int32, Int64, Pointer); - -typedef restore_wallet_from_spend_key = Int8 Function(Pointer, Pointer, Pointer, - Pointer, Pointer, Int32, Int64, Pointer); - -// typedef restore_wallet_from_device = Int8 Function(Pointer, Pointer, Pointer, -// Int32, Int64, Pointer); - -typedef is_wallet_exist = Int8 Function(Pointer); - -typedef load_wallet = Int8 Function(Pointer, Pointer, Int8); - -typedef error_string = Pointer Function(); - -typedef get_filename = Pointer Function(); - -typedef get_seed = Pointer Function(); - -typedef get_address = Pointer Function(Int32, Int32); - -typedef get_full_balanace = Int64 Function(Int32); - -typedef get_unlocked_balanace = Int64 Function(Int32); - -typedef get_current_height = Int64 Function(); - -typedef get_node_height = Int64 Function(); - -typedef is_connected = Int8 Function(); - -typedef setup_node = Int8 Function( - Pointer, Pointer?, Pointer?, Int8, Int8, Pointer?, Pointer); - -typedef start_refresh = Void Function(); - -typedef connect_to_node = Int8 Function(); - -typedef set_refresh_from_block_height = Void Function(Int64); - -typedef set_recovering_from_seed = Void Function(Int8); - -typedef store_c = Void Function(Pointer); - -typedef set_password = Int8 Function(Pointer password, Pointer error); - -typedef set_listener = Void Function(); - -typedef get_syncing_height = Int64 Function(); - -typedef is_needed_to_refresh = Int8 Function(); - -typedef is_new_transaction_exist = Int8 Function(); - -typedef subaddrress_size = Int32 Function(); - -typedef subaddrress_refresh = Void Function(Int32); - -typedef subaddress_get_all = Pointer Function(); - -typedef subaddress_add_new = Void Function(Int32 accountIndex, Pointer label); - -typedef subaddress_set_label = Void Function( - Int32 accountIndex, Int32 addressIndex, Pointer label); - -typedef account_size = Int32 Function(); - -typedef account_refresh = Void Function(); - -typedef account_get_all = Pointer Function(); - -typedef account_add_new = Void Function(Pointer label); - -typedef account_set_label = Void Function(Int32 accountIndex, Pointer label); - -typedef transactions_refresh = Void Function(); - -typedef get_transaction = Pointer Function(Pointer txId); - -typedef get_tx_key = Pointer? Function(Pointer txId); - -typedef transactions_count = Int64 Function(); - -typedef transactions_get_all = Pointer Function(); - -typedef transaction_create = Int8 Function( - Pointer address, - Pointer paymentId, - Pointer amount, - Int8 priorityRaw, - Int32 subaddrAccount, - Pointer> preferredInputs, - Int32 preferredInputsSize, - Pointer error, - Pointer pendingTransaction); - -typedef transaction_create_mult_dest = Int8 Function( - Pointer> addresses, - Pointer paymentId, - Pointer> amounts, - Int32 size, - Int8 priorityRaw, - Int32 subaddrAccount, - Pointer> preferredInputs, - Int32 preferredInputsSize, - Pointer error, - Pointer pendingTransaction); - -typedef transaction_commit = Int8 Function(Pointer, Pointer); - -typedef secret_view_key = Pointer Function(); - -typedef public_view_key = Pointer Function(); - -typedef secret_spend_key = Pointer Function(); - -typedef public_spend_key = Pointer Function(); - -typedef close_current_wallet = Void Function(); - -typedef on_startup = Void Function(); - -typedef rescan_blockchain = Void Function(); - -typedef get_subaddress_label = Pointer Function(Int32 accountIndex, Int32 addressIndex); - -typedef set_trusted_daemon = Void Function(Int8 trusted); - -typedef trusted_daemon = Int8 Function(); - -typedef refresh_coins = Void Function(Int32 accountIndex); - -typedef coins_count = Int64 Function(); - -// typedef coins_from_txid = Pointer Function(Pointer txid); - -typedef coin = Pointer Function(Int32 index); - -typedef freeze_coin = Void Function(Int32 index); - -typedef thaw_coin = Void Function(Int32 index); - -typedef sign_message = Pointer Function(Pointer message, Pointer address); - -typedef get_cache_attribute = Pointer Function(Pointer name); - -typedef set_cache_attribute = Int8 Function(Pointer name, Pointer value); diff --git a/cw_monero/lib/api/structs/pending_transaction.dart b/cw_monero/lib/api/structs/pending_transaction.dart index 656ed333f..dc5fbddd0 100644 --- a/cw_monero/lib/api/structs/pending_transaction.dart +++ b/cw_monero/lib/api/structs/pending_transaction.dart @@ -1,25 +1,3 @@ -import 'dart:ffi'; -import 'package:ffi/ffi.dart'; - -class PendingTransactionRaw extends Struct { - @Int64() - external int amount; - - @Int64() - external int fee; - - external Pointer hash; - - external Pointer hex; - - external Pointer txKey; - - String getHash() => hash.toDartString(); - - String getHex() => hex.toDartString(); - - String getKey() => txKey.toDartString(); -} class PendingTransactionDescription { PendingTransactionDescription({ diff --git a/cw_monero/lib/api/subaddress_list.dart b/cw_monero/lib/api/subaddress_list.dart index 1c1f1253f..57edea76e 100644 --- a/cw_monero/lib/api/subaddress_list.dart +++ b/cw_monero/lib/api/subaddress_list.dart @@ -1,38 +1,23 @@ -import 'dart:ffi'; -import 'package:ffi/ffi.dart'; -import 'package:flutter/foundation.dart'; -import 'package:cw_monero/api/signatures.dart'; -import 'package:cw_monero/api/types.dart'; -import 'package:cw_monero/api/monero_api.dart'; -import 'package:cw_monero/api/structs/subaddress_row.dart'; + +import 'package:cw_monero/api/account_list.dart'; import 'package:cw_monero/api/wallet.dart'; - -final subaddressSizeNative = moneroApi - .lookup>('subaddrress_size') - .asFunction(); - -final subaddressRefreshNative = moneroApi - .lookup>('subaddress_refresh') - .asFunction(); - -final subaddrressGetAllNative = moneroApi - .lookup>('subaddrress_get_all') - .asFunction(); - -final subaddrressAddNewNative = moneroApi - .lookup>('subaddress_add_row') - .asFunction(); - -final subaddrressSetLabelNative = moneroApi - .lookup>('subaddress_set_label') - .asFunction(); +import 'package:monero/monero.dart' as monero; bool isUpdating = false; +class SubaddressInfoMetadata { + SubaddressInfoMetadata({ + required this.accountIndex, + }); + int accountIndex; +} + +SubaddressInfoMetadata? subaddress = null; + void refreshSubaddresses({required int accountIndex}) { try { isUpdating = true; - subaddressRefreshNative(accountIndex); + subaddress = SubaddressInfoMetadata(accountIndex: accountIndex); isUpdating = false; } catch (e) { isUpdating = false; @@ -40,28 +25,39 @@ void refreshSubaddresses({required int accountIndex}) { } } -List getAllSubaddresses() { - final size = subaddressSizeNative(); - final subaddressAddressesPointer = subaddrressGetAllNative(); - final subaddressAddresses = subaddressAddressesPointer.asTypedList(size); +class Subaddress { + Subaddress({ + required this.addressIndex, + required this.accountIndex, + }); + String get address => monero.Wallet_address( + wptr!, + accountIndex: accountIndex, + addressIndex: addressIndex, + ); + final int addressIndex; + final int accountIndex; + String get label => monero.Wallet_getSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: addressIndex); +} - return subaddressAddresses - .map((addr) => Pointer.fromAddress(addr).ref) - .toList(); +List getAllSubaddresses() { + final size = monero.Wallet_numSubaddresses(wptr!, accountIndex: subaddress!.accountIndex); + return List.generate(size, (index) { + return Subaddress( + accountIndex: subaddress!.accountIndex, + addressIndex: index, + ); + }).reversed.toList(); } void addSubaddressSync({required int accountIndex, required String label}) { - final labelPointer = label.toNativeUtf8(); - subaddrressAddNewNative(accountIndex, labelPointer); - calloc.free(labelPointer); + monero.Wallet_addSubaddress(wptr!, accountIndex: accountIndex, label: label); + refreshSubaddresses(accountIndex: accountIndex); } void setLabelForSubaddressSync( {required int accountIndex, required int addressIndex, required String label}) { - final labelPointer = label.toNativeUtf8(); - - subaddrressSetLabelNative(accountIndex, addressIndex, labelPointer); - calloc.free(labelPointer); + monero.Wallet_setSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: addressIndex, label: label); } void _addSubaddress(Map args) { @@ -81,14 +77,13 @@ void _setLabelForSubaddress(Map args) { } Future addSubaddress({required int accountIndex, required String label}) async { - await compute, void>( - _addSubaddress, {'accountIndex': accountIndex, 'label': label}); - await store(); + _addSubaddress({'accountIndex': accountIndex, 'label': label}); + await store(); } Future setLabelForSubaddress( {required int accountIndex, required int addressIndex, required String label}) async { - await compute, void>(_setLabelForSubaddress, { + _setLabelForSubaddress({ 'accountIndex': accountIndex, 'addressIndex': addressIndex, 'label': label diff --git a/cw_monero/lib/api/transaction_history.dart b/cw_monero/lib/api/transaction_history.dart index 73c8de801..187921ff4 100644 --- a/cw_monero/lib/api/transaction_history.dart +++ b/cw_monero/lib/api/transaction_history.dart @@ -1,138 +1,132 @@ + import 'dart:ffi'; +import 'dart:isolate'; -import 'package:cw_monero/api/convert_utf8_to_string.dart'; +import 'package:cw_monero/api/account_list.dart'; import 'package:cw_monero/api/exceptions/creation_transaction_exception.dart'; -import 'package:cw_monero/api/monero_api.dart'; import 'package:cw_monero/api/monero_output.dart'; -import 'package:cw_monero/api/signatures.dart'; import 'package:cw_monero/api/structs/pending_transaction.dart'; -import 'package:cw_monero/api/structs/transaction_info_row.dart'; -import 'package:cw_monero/api/structs/ut8_box.dart'; -import 'package:cw_monero/api/types.dart'; import 'package:ffi/ffi.dart'; -import 'package:flutter/foundation.dart'; +import 'package:monero/monero.dart' as monero; +import 'package:monero/src/generated_bindings_monero.g.dart' as monero_gen; -final transactionsRefreshNative = moneroApi - .lookup>('transactions_refresh') - .asFunction(); - -final transactionsCountNative = moneroApi - .lookup>('transactions_count') - .asFunction(); - -final transactionsGetAllNative = moneroApi - .lookup>('transactions_get_all') - .asFunction(); - -final transactionCreateNative = moneroApi - .lookup>('transaction_create') - .asFunction(); - -final transactionCreateMultDestNative = moneroApi - .lookup>('transaction_create_mult_dest') - .asFunction(); - -final transactionCommitNative = moneroApi - .lookup>('transaction_commit') - .asFunction(); - -final getTxKeyNative = - moneroApi.lookup>('get_tx_key').asFunction(); - -final getTransactionNative = moneroApi - .lookup>('get_transaction') - .asFunction(); String getTxKey(String txId) { - final txIdPointer = txId.toNativeUtf8(); - final keyPointer = getTxKeyNative(txIdPointer); + return monero.Wallet_getTxKey(wptr!, txid: txId); +} - calloc.free(txIdPointer); +monero.TransactionHistory? txhistory; - if (keyPointer != null) { - return convertUTF8ToString(pointer: keyPointer); +void refreshTransactions() { + txhistory = monero.Wallet_history(wptr!); + monero.TransactionHistory_refresh(txhistory!); +} + +int countOfTransactions() => monero.TransactionHistory_count(txhistory!); + +List getAllTransactions() { + List dummyTxs = []; + + txhistory = monero.Wallet_history(wptr!); + monero.TransactionHistory_refresh(txhistory!); + int size = countOfTransactions(); + final list = List.generate(size, (index) => Transaction(txInfo: monero.TransactionHistory_transaction(txhistory!, index: index)))..addAll(dummyTxs); + + final accts = monero.Wallet_numSubaddressAccounts(wptr!); + for (var i = 0; i < accts; i++) { + final fullBalance = monero.Wallet_balance(wptr!, accountIndex: i); + final availBalance = monero.Wallet_unlockedBalance(wptr!, accountIndex: i); + if (fullBalance > availBalance) { + if (list.where((element) => element.accountIndex == i && element.isConfirmed == false).isNotEmpty) { + dummyTxs.add( + Transaction.dummy( + displayLabel: "", + description: "", + fee: 0, + confirmations: 0, + blockheight: 0, + accountIndex: i, + paymentId: "", + amount: fullBalance - availBalance, + isSpend: false, + hash: "pending", + key: "pending", + txInfo: Pointer.fromAddress(0), + )..timeStamp = DateTime.now() + ); + } + } } - - return ''; + list.addAll(dummyTxs); + return list; } -void refreshTransactions() => transactionsRefreshNative(); - -int countOfTransactions() => transactionsCountNative(); - -List getAllTransactions() { - final size = transactionsCountNative(); - final transactionsPointer = transactionsGetAllNative(); - final transactionsAddresses = transactionsPointer.asTypedList(size); - - return transactionsAddresses - .map((addr) => Pointer.fromAddress(addr).ref) - .toList(); +Transaction getTransaction(String txId) { + return Transaction(txInfo: monero.TransactionHistory_transactionById(txhistory!, txid: txId)); } -TransactionInfoRow getTransaction(String txId) { - final txIdPointer = txId.toNativeUtf8(); - return getTransactionNative(txIdPointer).ref; -} - -PendingTransactionDescription createTransactionSync( +Future createTransactionSync( {required String address, required String paymentId, required int priorityRaw, String? amount, int accountIndex = 0, - List preferredInputs = const []}) { - final addressPointer = address.toNativeUtf8(); - final paymentIdPointer = paymentId.toNativeUtf8(); - final amountPointer = amount != null ? amount.toNativeUtf8() : nullptr; + List preferredInputs = const []}) async { - final int preferredInputsSize = preferredInputs.length; - final List> preferredInputsPointers = - preferredInputs.map((output) => output.toNativeUtf8()).toList(); - final Pointer> preferredInputsPointerPointer = calloc(preferredInputsSize); + final amt = amount == null ? 0 : monero.Wallet_amountFromString(amount); + + final address_ = address.toNativeUtf8(); + final paymentId_ = paymentId.toNativeUtf8(); + final preferredInputs_ = preferredInputs.join(monero.defaultSeparatorStr).toNativeUtf8(); - for (int i = 0; i < preferredInputsSize; i++) { - preferredInputsPointerPointer[i] = preferredInputsPointers[i]; - } + final waddr = wptr!.address; + final addraddr = address_.address; + final paymentIdAddr = paymentId_.address; + final preferredInputsAddr = preferredInputs_.address; + final spaddr = monero.defaultSeparator.address; + final pendingTx = Pointer.fromAddress(await Isolate.run(() { + final tx = monero_gen.MoneroC(DynamicLibrary.open(monero.libPath)).MONERO_Wallet_createTransaction( + Pointer.fromAddress(waddr), + Pointer.fromAddress(addraddr).cast(), + Pointer.fromAddress(paymentIdAddr).cast(), + amt, + 1, + priorityRaw, + accountIndex, + Pointer.fromAddress(preferredInputsAddr).cast(), + Pointer.fromAddress(spaddr), + ); + return tx.address; + })); + calloc.free(address_); + calloc.free(paymentId_); + calloc.free(preferredInputs_); + final String? error = (() { + final status = monero.PendingTransaction_status(pendingTx); + if (status == 0) { + return null; + } + return monero.PendingTransaction_errorString(pendingTx); + })(); - final errorMessagePointer = calloc(); - final pendingTransactionRawPointer = calloc(); - final created = transactionCreateNative( - addressPointer, - paymentIdPointer, - amountPointer, - priorityRaw, - accountIndex, - preferredInputsPointerPointer, - preferredInputsSize, - errorMessagePointer, - pendingTransactionRawPointer) != - 0; - - calloc.free(preferredInputsPointerPointer); - - preferredInputsPointers.forEach((element) => calloc.free(element)); - - calloc.free(addressPointer); - calloc.free(paymentIdPointer); - - if (amountPointer != nullptr) { - calloc.free(amountPointer); - } - - if (!created) { - final message = errorMessagePointer.ref.getValue(); - calloc.free(errorMessagePointer); + if (error != null) { + final message = error; throw CreationTransactionException(message: message); } + final rAmt = monero.PendingTransaction_amount(pendingTx); + final rFee = monero.PendingTransaction_fee(pendingTx); + final rHash = monero.PendingTransaction_txid(pendingTx, ''); + final rTxKey = rHash; + return PendingTransactionDescription( - amount: pendingTransactionRawPointer.ref.amount, - fee: pendingTransactionRawPointer.ref.fee, - hash: pendingTransactionRawPointer.ref.getHash(), - hex: pendingTransactionRawPointer.ref.getHex(), - txKey: pendingTransactionRawPointer.ref.getKey(), - pointerAddress: pendingTransactionRawPointer.address); + amount: rAmt, + fee: rFee, + hash: rHash, + hex: '', + txKey: rTxKey, + pointerAddress: pendingTx.address, + ); } PendingTransactionDescription createTransactionMultDestSync( @@ -141,84 +135,50 @@ PendingTransactionDescription createTransactionMultDestSync( required int priorityRaw, int accountIndex = 0, List preferredInputs = const []}) { - final int size = outputs.length; - final List> addressesPointers = - outputs.map((output) => output.address.toNativeUtf8()).toList(); - final Pointer> addressesPointerPointer = calloc(size); - final List> amountsPointers = - outputs.map((output) => output.amount.toNativeUtf8()).toList(); - final Pointer> amountsPointerPointer = calloc(size); - - for (int i = 0; i < size; i++) { - addressesPointerPointer[i] = addressesPointers[i]; - amountsPointerPointer[i] = amountsPointers[i]; + + final txptr = monero.Wallet_createTransactionMultDest( + wptr!, + dstAddr: outputs.map((e) => e.address).toList(), + isSweepAll: false, + amounts: outputs.map((e) => monero.Wallet_amountFromString(e.amount)).toList(), + mixinCount: 0, + pendingTransactionPriority: priorityRaw, + subaddr_account: accountIndex, + ); + if (monero.PendingTransaction_status(txptr) != 0) { + throw CreationTransactionException(message: monero.PendingTransaction_errorString(txptr)); } - - final int preferredInputsSize = preferredInputs.length; - final List> preferredInputsPointers = - preferredInputs.map((output) => output.toNativeUtf8()).toList(); - final Pointer> preferredInputsPointerPointer = calloc(preferredInputsSize); - - for (int i = 0; i < preferredInputsSize; i++) { - preferredInputsPointerPointer[i] = preferredInputsPointers[i]; - } - - final paymentIdPointer = paymentId.toNativeUtf8(); - final errorMessagePointer = calloc(); - final pendingTransactionRawPointer = calloc(); - final created = transactionCreateMultDestNative( - addressesPointerPointer, - paymentIdPointer, - amountsPointerPointer, - size, - priorityRaw, - accountIndex, - preferredInputsPointerPointer, - preferredInputsSize, - errorMessagePointer, - pendingTransactionRawPointer) != - 0; - - calloc.free(addressesPointerPointer); - calloc.free(amountsPointerPointer); - calloc.free(preferredInputsPointerPointer); - - addressesPointers.forEach((element) => calloc.free(element)); - amountsPointers.forEach((element) => calloc.free(element)); - preferredInputsPointers.forEach((element) => calloc.free(element)); - - calloc.free(paymentIdPointer); - - if (!created) { - final message = errorMessagePointer.ref.getValue(); - calloc.free(errorMessagePointer); - throw CreationTransactionException(message: message); - } - return PendingTransactionDescription( - amount: pendingTransactionRawPointer.ref.amount, - fee: pendingTransactionRawPointer.ref.fee, - hash: pendingTransactionRawPointer.ref.getHash(), - hex: pendingTransactionRawPointer.ref.getHex(), - txKey: pendingTransactionRawPointer.ref.getKey(), - pointerAddress: pendingTransactionRawPointer.address); + amount: monero.PendingTransaction_amount(txptr), + fee: monero.PendingTransaction_fee(txptr), + hash: monero.PendingTransaction_txid(txptr, ''), + hex: monero.PendingTransaction_txid(txptr, ''), + txKey: monero.PendingTransaction_txid(txptr, ''), + pointerAddress: txptr.address, + ); } void commitTransactionFromPointerAddress({required int address}) => - commitTransaction(transactionPointer: Pointer.fromAddress(address)); + commitTransaction(transactionPointer: monero.PendingTransaction.fromAddress(address)); -void commitTransaction({required Pointer transactionPointer}) { - final errorMessagePointer = calloc(); - final isCommited = transactionCommitNative(transactionPointer, errorMessagePointer) != 0; +void commitTransaction({required monero.PendingTransaction transactionPointer}) { + + final txCommit = monero.PendingTransaction_commit(transactionPointer, filename: '', overwrite: false); - if (!isCommited) { - final message = errorMessagePointer.ref.getValue(); - calloc.free(errorMessagePointer); - throw CreationTransactionException(message: message); + final String? error = (() { + final status = monero.PendingTransaction_status(transactionPointer.cast()); + if (status == 0) { + return null; + } + return monero.Wallet_errorString(wptr!); + })(); + + if (error != null) { + throw CreationTransactionException(message: error); } } -PendingTransactionDescription _createTransactionSync(Map args) { +Future _createTransactionSync(Map args) async { final address = args['address'] as String; final paymentId = args['paymentId'] as String; final amount = args['amount'] as String?; @@ -256,8 +216,8 @@ Future createTransaction( String? amount, String paymentId = '', int accountIndex = 0, - List preferredInputs = const []}) => - compute(_createTransactionSync, { + List preferredInputs = const []}) async => + _createTransactionSync({ 'address': address, 'paymentId': paymentId, 'amount': amount, @@ -271,11 +231,94 @@ Future createTransactionMultDest( required int priorityRaw, String paymentId = '', int accountIndex = 0, - List preferredInputs = const []}) => - compute(_createTransactionMultDestSync, { + List preferredInputs = const []}) async => + _createTransactionMultDestSync({ 'outputs': outputs, 'paymentId': paymentId, 'priorityRaw': priorityRaw, 'accountIndex': accountIndex, 'preferredInputs': preferredInputs }); + + +class Transaction { + final String displayLabel; + String subaddressLabel = monero.Wallet_getSubaddressLabel(wptr!, accountIndex: 0, addressIndex: 0); + late final String address = monero.Wallet_address( + wptr!, + accountIndex: 0, + addressIndex: 0, + ); + final String description; + final int fee; + final int confirmations; + late final bool isPending = confirmations < 10; + final int blockheight; + final int addressIndex = 0; + final int accountIndex; + final String paymentId; + final int amount; + final bool isSpend; + late DateTime timeStamp; + late final bool isConfirmed = !isPending; + final String hash; + final String key; + + Map toJson() { + return { + "displayLabel": displayLabel, + "subaddressLabel": subaddressLabel, + "address": address, + "description": description, + "fee": fee, + "confirmations": confirmations, + "isPending": isPending, + "blockheight": blockheight, + "accountIndex": accountIndex, + "addressIndex": addressIndex, + "paymentId": paymentId, + "amount": amount, + "isSpend": isSpend, + "timeStamp": timeStamp.toIso8601String(), + "isConfirmed": isConfirmed, + "hash": hash, + }; + } + + // S finalubAddress? subAddress; + // List transfers = []; + // final int txIndex; + final monero.TransactionInfo txInfo; + Transaction({ + required this.txInfo, + }) : displayLabel = monero.TransactionInfo_label(txInfo), + hash = monero.TransactionInfo_hash(txInfo), + timeStamp = DateTime.fromMillisecondsSinceEpoch( + monero.TransactionInfo_timestamp(txInfo) * 1000, + ), + isSpend = monero.TransactionInfo_direction(txInfo) == + monero.TransactionInfo_Direction.Out, + amount = monero.TransactionInfo_amount(txInfo), + paymentId = monero.TransactionInfo_paymentId(txInfo), + accountIndex = monero.TransactionInfo_subaddrAccount(txInfo), + blockheight = monero.TransactionInfo_blockHeight(txInfo), + confirmations = monero.TransactionInfo_confirmations(txInfo), + fee = monero.TransactionInfo_fee(txInfo), + description = monero.TransactionInfo_description(txInfo), + key = monero.Wallet_getTxKey(wptr!, txid: monero.TransactionInfo_hash(txInfo)); + + Transaction.dummy({ + required this.displayLabel, + required this.description, + required this.fee, + required this.confirmations, + required this.blockheight, + required this.accountIndex, + required this.paymentId, + required this.amount, + required this.isSpend, + required this.hash, + required this.key, + required this.txInfo + }); +} \ No newline at end of file diff --git a/cw_monero/lib/api/types.dart b/cw_monero/lib/api/types.dart deleted file mode 100644 index 6b36ab5e3..000000000 --- a/cw_monero/lib/api/types.dart +++ /dev/null @@ -1,160 +0,0 @@ -import 'dart:ffi'; -import 'package:cw_monero/api/structs/coins_info_row.dart'; -import 'package:cw_monero/api/structs/pending_transaction.dart'; -import 'package:cw_monero/api/structs/transaction_info_row.dart'; -import 'package:cw_monero/api/structs/ut8_box.dart'; -import 'package:ffi/ffi.dart'; - -typedef CreateWallet = int Function( - Pointer, Pointer, Pointer, int, Pointer); - -typedef RestoreWalletFromSeed = int Function( - Pointer, Pointer, Pointer, int, int, Pointer); - -typedef RestoreWalletFromKeys = int Function(Pointer, Pointer, - Pointer, Pointer, Pointer, Pointer, int, int, Pointer); - -typedef RestoreWalletFromSpendKey = int Function(Pointer, Pointer, Pointer, - Pointer, Pointer, int, int, Pointer); - -typedef RestoreWalletFromDevice = int Function(Pointer, Pointer, Pointer, - int, int, Pointer); - -typedef IsWalletExist = int Function(Pointer); - -typedef LoadWallet = int Function(Pointer, Pointer, int); - -typedef ErrorString = Pointer Function(); - -typedef GetFilename = Pointer Function(); - -typedef GetSeed = Pointer Function(); - -typedef GetAddress = Pointer Function(int, int); - -typedef GetFullBalance = int Function(int); - -typedef GetUnlockedBalance = int Function(int); - -typedef GetCurrentHeight = int Function(); - -typedef GetNodeHeight = int Function(); - -typedef IsConnected = int Function(); - -typedef SetupNode = int Function( - Pointer, Pointer?, Pointer?, int, int, Pointer?, Pointer); - -typedef StartRefresh = void Function(); - -typedef ConnectToNode = int Function(); - -typedef SetRefreshFromBlockHeight = void Function(int); - -typedef SetRecoveringFromSeed = void Function(int); - -typedef Store = void Function(Pointer); - -typedef SetPassword = int Function(Pointer password, Pointer error); - -typedef SetListener = void Function(); - -typedef GetSyncingHeight = int Function(); - -typedef IsNeededToRefresh = int Function(); - -typedef IsNewTransactionExist = int Function(); - -typedef SubaddressSize = int Function(); - -typedef SubaddressRefresh = void Function(int); - -typedef SubaddressGetAll = Pointer Function(); - -typedef SubaddressAddNew = void Function(int accountIndex, Pointer label); - -typedef SubaddressSetLabel = void Function( - int accountIndex, int addressIndex, Pointer label); - -typedef AccountSize = int Function(); - -typedef AccountRefresh = void Function(); - -typedef AccountGetAll = Pointer Function(); - -typedef AccountAddNew = void Function(Pointer label); - -typedef AccountSetLabel = void Function(int accountIndex, Pointer label); - -typedef TransactionsRefresh = void Function(); - -typedef GetTransaction = Pointer Function(Pointer txId); - -typedef GetTxKey = Pointer? Function(Pointer txId); - -typedef TransactionsCount = int Function(); - -typedef TransactionsGetAll = Pointer Function(); - -typedef TransactionCreate = int Function( - Pointer address, - Pointer paymentId, - Pointer amount, - int priorityRaw, - int subaddrAccount, - Pointer> preferredInputs, - int preferredInputsSize, - Pointer error, - Pointer pendingTransaction); - -typedef TransactionCreateMultDest = int Function( - Pointer> addresses, - Pointer paymentId, - Pointer> amounts, - int size, - int priorityRaw, - int subaddrAccount, - Pointer> preferredInputs, - int preferredInputsSize, - Pointer error, - Pointer pendingTransaction); - -typedef TransactionCommit = int Function(Pointer, Pointer); - -typedef SecretViewKey = Pointer Function(); - -typedef PublicViewKey = Pointer Function(); - -typedef SecretSpendKey = Pointer Function(); - -typedef PublicSpendKey = Pointer Function(); - -typedef CloseCurrentWallet = void Function(); - -typedef OnStartup = void Function(); - -typedef RescanBlockchainAsync = void Function(); - -typedef GetSubaddressLabel = Pointer Function( - int accountIndex, - int addressIndex); - -typedef SetTrustedDaemon = void Function(int); - -typedef TrustedDaemon = int Function(); - -typedef RefreshCoins = void Function(int); - -typedef CoinsCount = int Function(); - -typedef GetCoin = Pointer Function(int); - -typedef FreezeCoin = void Function(int); - -typedef ThawCoin = void Function(int); - -typedef SignMessage = Pointer Function(Pointer, Pointer); - -typedef GetCacheAttribute = Pointer Function(Pointer); - -typedef SetCacheAttribute = int Function(Pointer, Pointer); diff --git a/cw_monero/lib/api/wallet.dart b/cw_monero/lib/api/wallet.dart index 448c661e6..59eeb1498 100644 --- a/cw_monero/lib/api/wallet.dart +++ b/cw_monero/lib/api/wallet.dart @@ -1,245 +1,155 @@ import 'dart:async'; import 'dart:ffi'; +import 'dart:isolate'; -import 'package:cw_monero/api/convert_utf8_to_string.dart'; +import 'package:cw_monero/api/account_list.dart'; import 'package:cw_monero/api/exceptions/setup_wallet_exception.dart'; -import 'package:cw_monero/api/monero_api.dart'; -import 'package:cw_monero/api/signatures.dart'; -import 'package:cw_monero/api/structs/ut8_box.dart'; -import 'package:cw_monero/api/types.dart'; -import 'package:ffi/ffi.dart'; -import 'package:flutter/foundation.dart'; +import 'package:monero/monero.dart' as monero; +import 'package:mutex/mutex.dart'; -int _boolToInt(bool value) => value ? 1 : 0; +int getSyncingHeight() { + // final height = monero.MONERO_cw_WalletListener_height(getWlptr()); + final h2 = monero.Wallet_blockChainHeight(wptr!); + // print("height: $height / $h2"); + return h2; +} -final getFileNameNative = - moneroApi.lookup>('get_filename').asFunction(); +bool isNeededToRefresh() { + final ret = monero.MONERO_cw_WalletListener_isNeedToRefresh(getWlptr()); + monero.MONERO_cw_WalletListener_resetNeedToRefresh(getWlptr()); + return ret; +} -final getSeedNative = moneroApi.lookup>('seed').asFunction(); +bool isNewTransactionExist() { + final ret = monero.MONERO_cw_WalletListener_isNewTransactionExist(getWlptr()); + monero.MONERO_cw_WalletListener_resetIsNewTransactionExist(getWlptr()); + return ret; +} -final getAddressNative = - moneroApi.lookup>('get_address').asFunction(); +String getFilename() => monero.Wallet_filename(wptr!); -final getFullBalanceNative = moneroApi - .lookup>('get_full_balance') - .asFunction(); +String getSeed() { + // monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed); + final cakepolyseed = + monero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.seed"); + if (cakepolyseed != "") { + return cakepolyseed; + } + final polyseed = monero.Wallet_getPolyseed(wptr!, passphrase: ''); + if (polyseed != "") { + return polyseed; + } + final legacy = monero.Wallet_seed(wptr!, seedOffset: ''); + return legacy; +} -final getUnlockedBalanceNative = moneroApi - .lookup>('get_unlocked_balance') - .asFunction(); - -final getCurrentHeightNative = moneroApi - .lookup>('get_current_height') - .asFunction(); - -final getNodeHeightNative = moneroApi - .lookup>('get_node_height') - .asFunction(); - -final isConnectedNative = - moneroApi.lookup>('is_connected').asFunction(); - -final setupNodeNative = - moneroApi.lookup>('setup_node').asFunction(); - -final startRefreshNative = - moneroApi.lookup>('start_refresh').asFunction(); - -final connecToNodeNative = moneroApi - .lookup>('connect_to_node') - .asFunction(); - -final setRefreshFromBlockHeightNative = moneroApi - .lookup>('set_refresh_from_block_height') - .asFunction(); - -final setRecoveringFromSeedNative = moneroApi - .lookup>('set_recovering_from_seed') - .asFunction(); - -final storeNative = moneroApi.lookup>('store').asFunction(); - -final setPasswordNative = - moneroApi.lookup>('set_password').asFunction(); - -final setListenerNative = - moneroApi.lookup>('set_listener').asFunction(); - -final getSyncingHeightNative = moneroApi - .lookup>('get_syncing_height') - .asFunction(); - -final isNeededToRefreshNative = moneroApi - .lookup>('is_needed_to_refresh') - .asFunction(); - -final isNewTransactionExistNative = moneroApi - .lookup>('is_new_transaction_exist') - .asFunction(); - -final getSecretViewKeyNative = moneroApi - .lookup>('secret_view_key') - .asFunction(); - -final getPublicViewKeyNative = moneroApi - .lookup>('public_view_key') - .asFunction(); - -final getSecretSpendKeyNative = moneroApi - .lookup>('secret_spend_key') - .asFunction(); - -final getPublicSpendKeyNative = moneroApi - .lookup>('public_spend_key') - .asFunction(); - -final closeCurrentWalletNative = moneroApi - .lookup>('close_current_wallet') - .asFunction(); - -final onStartupNative = - moneroApi.lookup>('on_startup').asFunction(); - -final rescanBlockchainAsyncNative = moneroApi - .lookup>('rescan_blockchain') - .asFunction(); - -final getSubaddressLabelNative = moneroApi - .lookup>('get_subaddress_label') - .asFunction(); - -final setTrustedDaemonNative = moneroApi - .lookup>('set_trusted_daemon') - .asFunction(); - -final trustedDaemonNative = - moneroApi.lookup>('trusted_daemon').asFunction(); - -final signMessageNative = - moneroApi.lookup>('sign_message').asFunction(); - -final getCacheAttributeNative = moneroApi - .lookup>('get_cache_attribute') - .asFunction(); - -final setCacheAttributeNative = moneroApi - .lookup>('set_cache_attribute') - .asFunction(); - -int getSyncingHeight() => getSyncingHeightNative(); - -bool isNeededToRefresh() => isNeededToRefreshNative() != 0; - -bool isNewTransactionExist() => isNewTransactionExistNative() != 0; - -String getFilename() => convertUTF8ToString(pointer: getFileNameNative()); - -String getSeed() => convertUTF8ToString(pointer: getSeedNative()); +String getSeedLegacy(String? language) { + var legacy = monero.Wallet_seed(wptr!, seedOffset: ''); + if (monero.Wallet_status(wptr!) != 0) { + monero.Wallet_setSeedLanguage(wptr!, language: language ?? "English"); + legacy = monero.Wallet_seed(wptr!, seedOffset: ''); + } + return legacy; +} String getAddress({int accountIndex = 0, int addressIndex = 0}) => - convertUTF8ToString(pointer: getAddressNative(accountIndex, addressIndex)); + monero.Wallet_address(wptr!, + accountIndex: accountIndex, addressIndex: addressIndex); -int getFullBalance({int accountIndex = 0}) => getFullBalanceNative(accountIndex); +int getFullBalance({int accountIndex = 0}) => + monero.Wallet_balance(wptr!, accountIndex: accountIndex); -int getUnlockedBalance({int accountIndex = 0}) => getUnlockedBalanceNative(accountIndex); +int getUnlockedBalance({int accountIndex = 0}) => + monero.Wallet_unlockedBalance(wptr!, accountIndex: accountIndex); -int getCurrentHeight() => getCurrentHeightNative(); +int getCurrentHeight() => monero.Wallet_blockChainHeight(wptr!); -int getNodeHeightSync() => getNodeHeightNative(); +int getNodeHeightSync() => monero.Wallet_daemonBlockChainHeight(wptr!); -bool isConnectedSync() => isConnectedNative() != 0; +bool isConnectedSync() => monero.Wallet_connected(wptr!) != 0; -bool setupNodeSync( +Future setupNodeSync( {required String address, String? login, String? password, bool useSSL = false, bool isLightWallet = false, - String? socksProxyAddress}) { - final addressPointer = address.toNativeUtf8(); - Pointer? loginPointer; - Pointer? socksProxyAddressPointer; - Pointer? passwordPointer; + String? socksProxyAddress}) async { + print(''' +{ + wptr!, + daemonAddress: $address, + useSsl: $useSSL, + proxyAddress: $socksProxyAddress ?? '', + daemonUsername: $login ?? '', + daemonPassword: $password ?? '' +} +'''); + final addr = wptr!.address; + await Isolate.run(() { + monero.Wallet_init(Pointer.fromAddress(addr), + daemonAddress: address, + useSsl: useSSL, + proxyAddress: socksProxyAddress ?? '', + daemonUsername: login ?? '', + daemonPassword: password ?? ''); + }); + // monero.Wallet_init3(wptr!, argv0: '', defaultLogBaseName: 'moneroc', console: true); - if (login != null) { - loginPointer = login.toNativeUtf8(); + final status = monero.Wallet_status(wptr!); + + if (status != 0) { + final error = monero.Wallet_errorString(wptr!); + print("error: $error"); + throw SetupWalletException(message: error); } - if (password != null) { - passwordPointer = password.toNativeUtf8(); - } - - if (socksProxyAddress != null) { - socksProxyAddressPointer = socksProxyAddress.toNativeUtf8(); - } - - final errorMessagePointer = ''.toNativeUtf8(); - final isSetupNode = setupNodeNative( - addressPointer, - loginPointer, - passwordPointer, - _boolToInt(useSSL), - _boolToInt(isLightWallet), - socksProxyAddressPointer, - errorMessagePointer) != - 0; - - calloc.free(addressPointer); - - if (loginPointer != null) { - calloc.free(loginPointer); - } - - if (passwordPointer != null) { - calloc.free(passwordPointer); - } - - if (!isSetupNode) { - throw SetupWalletException(message: convertUTF8ToString(pointer: errorMessagePointer)); - } - - return isSetupNode; + return status == 0; } -void startRefreshSync() => startRefreshNative(); +void startRefreshSync() { + monero.Wallet_refreshAsync(wptr!); + monero.Wallet_startRefresh(wptr!); +} -Future connectToNode() async => connecToNodeNative() != 0; -void setRefreshFromBlockHeight({required int height}) => setRefreshFromBlockHeightNative(height); +void setRefreshFromBlockHeight({required int height}) => + monero.Wallet_setRefreshFromBlockHeight(wptr!, + refresh_from_block_height: height); void setRecoveringFromSeed({required bool isRecovery}) => - setRecoveringFromSeedNative(_boolToInt(isRecovery)); + monero.Wallet_setRecoveringFromSeed(wptr!, recoveringFromSeed: isRecovery); -void storeSync() { - final pathPointer = ''.toNativeUtf8(); - storeNative(pathPointer); - calloc.free(pathPointer); +final storeMutex = Mutex(); +void storeSync() async { + await storeMutex.acquire(); + final addr = wptr!.address; + await Isolate.run(() { + monero.Wallet_store(Pointer.fromAddress(addr)); + }); + storeMutex.release(); } void setPasswordSync(String password) { - final passwordPointer = password.toNativeUtf8(); - final errorMessagePointer = calloc(); - final changed = setPasswordNative(passwordPointer, errorMessagePointer) != 0; - calloc.free(passwordPointer); + monero.Wallet_setPassword(wptr!, password: password); - if (!changed) { - final message = errorMessagePointer.ref.getValue(); - calloc.free(errorMessagePointer); - throw Exception(message); + final status = monero.Wallet_status(wptr!); + if (status != 0) { + throw Exception(monero.Wallet_errorString(wptr!)); } - - calloc.free(errorMessagePointer); } -void closeCurrentWallet() => closeCurrentWalletNative(); +void closeCurrentWallet() { + monero.Wallet_stop(wptr!); +} -String getSecretViewKey() => convertUTF8ToString(pointer: getSecretViewKeyNative()); +String getSecretViewKey() => monero.Wallet_secretViewKey(wptr!); -String getPublicViewKey() => convertUTF8ToString(pointer: getPublicViewKeyNative()); +String getPublicViewKey() => monero.Wallet_publicViewKey(wptr!); -String getSecretSpendKey() => convertUTF8ToString(pointer: getSecretSpendKeyNative()); +String getSecretSpendKey() => monero.Wallet_secretSpendKey(wptr!); -String getPublicSpendKey() => convertUTF8ToString(pointer: getPublicSpendKeyNative()); +String getPublicSpendKey() => monero.Wallet_publicSpendKey(wptr!); class SyncListener { SyncListener(this.onNewBlock, this.onNewTransaction) @@ -267,7 +177,8 @@ class SyncListener { _cachedBlockchainHeight = 0; _lastKnownBlockHeight = 0; _initialSyncHeight = 0; - _updateSyncInfoTimer ??= Timer.periodic(Duration(milliseconds: 1200), (_) async { + _updateSyncInfoTimer ??= + Timer.periodic(Duration(milliseconds: 1200), (_) async { if (isNewTransactionExist()) { onNewTransaction(); } @@ -306,18 +217,18 @@ class SyncListener { void stop() => _updateSyncInfoTimer?.cancel(); } -SyncListener setListeners( - void Function(int, int, double) onNewBlock, void Function() onNewTransaction) { +SyncListener setListeners(void Function(int, int, double) onNewBlock, + void Function() onNewTransaction) { final listener = SyncListener(onNewBlock, onNewTransaction); - setListenerNative(); + // setListenerNative(); return listener; } -void onStartup() => onStartupNative(); +void onStartup() {} void _storeSync(Object _) => storeSync(); -bool _setupNodeSync(Map args) { +Future _setupNodeSync(Map args) async { final address = args['address'] as String; final login = (args['login'] ?? '') as String; final password = (args['password'] ?? '') as String; @@ -346,8 +257,8 @@ Future setupNode( String? password, bool useSSL = false, String? socksProxyAddress, - bool isLightWallet = false}) => - compute, void>(_setupNodeSync, { + bool isLightWallet = false}) async => + _setupNodeSync({ 'address': address, 'login': login, 'password': password, @@ -356,49 +267,24 @@ Future setupNode( 'socksProxyAddress': socksProxyAddress }); -Future store() => compute(_storeSync, 0); +Future store() async => _storeSync(0); -Future isConnected() => compute(_isConnected, 0); +Future isConnected() async => _isConnected(0); -Future getNodeHeight() => compute(_getNodeHeight, 0); +Future getNodeHeight() async => _getNodeHeight(0); -void rescanBlockchainAsync() => rescanBlockchainAsyncNative(); +void rescanBlockchainAsync() => monero.Wallet_rescanBlockchainAsync(wptr!); String getSubaddressLabel(int accountIndex, int addressIndex) { - return convertUTF8ToString(pointer: getSubaddressLabelNative(accountIndex, addressIndex)); + return monero.Wallet_getSubaddressLabel(wptr!, + accountIndex: accountIndex, addressIndex: addressIndex); } -Future setTrustedDaemon(bool trusted) async => setTrustedDaemonNative(_boolToInt(trusted)); +Future setTrustedDaemon(bool trusted) async => + monero.Wallet_setTrustedDaemon(wptr!, arg: trusted); -Future trustedDaemon() async => trustedDaemonNative() != 0; +Future trustedDaemon() async => monero.Wallet_trustedDaemon(wptr!); String signMessage(String message, {String address = ""}) { - final messagePointer = message.toNativeUtf8(); - final addressPointer = address.toNativeUtf8(); - - final signature = convertUTF8ToString(pointer: signMessageNative(messagePointer, addressPointer)); - calloc.free(messagePointer); - calloc.free(addressPointer); - - return signature; -} - -bool setCacheAttribute(String name, String value) { - final namePointer = name.toNativeUtf8(); - final valuePointer = value.toNativeUtf8(); - - final isSet = setCacheAttributeNative(namePointer, valuePointer); - calloc.free(namePointer); - calloc.free(valuePointer); - - return isSet == 1; -} - -String getCacheAttribute(String name) { - final namePointer = name.toNativeUtf8(); - - final value = convertUTF8ToString(pointer: getCacheAttributeNative(namePointer)); - calloc.free(namePointer); - - return value; + return monero.Wallet_signMessage(wptr!, message: message, address: address); } diff --git a/cw_monero/lib/api/wallet_manager.dart b/cw_monero/lib/api/wallet_manager.dart index ae88f76ab..1873f734e 100644 --- a/cw_monero/lib/api/wallet_manager.dart +++ b/cw_monero/lib/api/wallet_manager.dart @@ -1,85 +1,50 @@ import 'dart:ffi'; +import 'dart:isolate'; -import 'package:cw_monero/api/convert_utf8_to_string.dart'; +import 'package:cw_monero/api/account_list.dart'; import 'package:cw_monero/api/exceptions/wallet_creation_exception.dart'; import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart'; import 'package:cw_monero/api/exceptions/wallet_restore_from_keys_exception.dart'; import 'package:cw_monero/api/exceptions/wallet_restore_from_seed_exception.dart'; -import 'package:cw_monero/api/monero_api.dart'; -import 'package:cw_monero/api/signatures.dart'; -import 'package:cw_monero/api/types.dart'; import 'package:cw_monero/api/wallet.dart'; -import 'package:ffi/ffi.dart'; -import 'package:flutter/foundation.dart'; +import 'package:monero/monero.dart' as monero; -final createWalletNative = moneroApi - .lookup>('create_wallet') - .asFunction(); - -final restoreWalletFromSeedNative = moneroApi - .lookup>( - 'restore_wallet_from_seed') - .asFunction(); - -final restoreWalletFromKeysNative = moneroApi - .lookup>( - 'restore_wallet_from_keys') - .asFunction(); - -final restoreWalletFromSpendKeyNative = moneroApi - .lookup>( - 'restore_wallet_from_spend_key') - .asFunction(); - -// final restoreWalletFromDeviceNative = moneroApi -// .lookup>( -// 'restore_wallet_from_device') -// .asFunction(); - -final isWalletExistNative = moneroApi - .lookup>('is_wallet_exist') - .asFunction(); - -final loadWalletNative = moneroApi - .lookup>('load_wallet') - .asFunction(); - -final errorStringNative = moneroApi - .lookup>('error_string') - .asFunction(); +monero.WalletManager? _wmPtr; +final monero.WalletManager wmPtr = Pointer.fromAddress((() { + try { + // Problems with the wallet? Crashes? Lags? this will print all calls to xmr + // codebase, so it will be easier to debug what happens. At least easier + // than plugging gdb in. Especially on windows/android. + monero.printStarts = false; + _wmPtr ??= monero.WalletManagerFactory_getWalletManager(); + print("ptr: $_wmPtr"); + } catch (e) { + print(e); + } + return _wmPtr!.address; +})()); void createWalletSync( {required String path, - required String password, - required String language, - int nettype = 0}) { - final pathPointer = path.toNativeUtf8(); - final passwordPointer = password.toNativeUtf8(); - final languagePointer = language.toNativeUtf8(); - final errorMessagePointer = ''.toNativeUtf8(); - final isWalletCreated = createWalletNative(pathPointer, passwordPointer, - languagePointer, nettype, errorMessagePointer) != - 0; + required String password, + required String language, + int nettype = 0}) { + wptr = monero.WalletManager_createWallet(wmPtr, + path: path, password: password, language: language, networkType: 0); - calloc.free(pathPointer); - calloc.free(passwordPointer); - calloc.free(languagePointer); - - if (!isWalletCreated) { - throw WalletCreationException( - message: convertUTF8ToString(pointer: errorMessagePointer)); + final status = monero.Wallet_status(wptr!); + if (status != 0) { + throw WalletCreationException(message: monero.Wallet_errorString(wptr!)); } + monero.Wallet_store(wptr!, path: path); + openedWalletsByPath[path] = wptr!; + // is the line below needed? // setupNodeSync(address: "node.moneroworld.com:18089"); } bool isWalletExistSync({required String path}) { - final pathPointer = path.toNativeUtf8(); - final isExist = isWalletExistNative(pathPointer) != 0; - - calloc.free(pathPointer); - - return isExist; + return monero.WalletManager_walletExists(wmPtr, path); } void restoreWalletFromSeedSync( @@ -88,27 +53,24 @@ void restoreWalletFromSeedSync( required String seed, int nettype = 0, int restoreHeight = 0}) { - final pathPointer = path.toNativeUtf8(); - final passwordPointer = password.toNativeUtf8(); - final seedPointer = seed.toNativeUtf8(); - final errorMessagePointer = ''.toNativeUtf8(); - final isWalletRestored = restoreWalletFromSeedNative( - pathPointer, - passwordPointer, - seedPointer, - nettype, - restoreHeight, - errorMessagePointer) != - 0; + wptr = monero.WalletManager_recoveryWallet( + wmPtr, + path: path, + password: password, + mnemonic: seed, + restoreHeight: restoreHeight, + seedOffset: '', + networkType: 0, + ); - calloc.free(pathPointer); - calloc.free(passwordPointer); - calloc.free(seedPointer); + final status = monero.Wallet_status(wptr!); - if (!isWalletRestored) { - throw WalletRestoreFromSeedException( - message: convertUTF8ToString(pointer: errorMessagePointer)); + if (status != 0) { + final error = monero.Wallet_errorString(wptr!); + throw WalletRestoreFromSeedException(message: error); } + + openedWalletsByPath[path] = wptr!; } void restoreWalletFromKeysSync( @@ -120,76 +82,72 @@ void restoreWalletFromKeysSync( required String spendKey, int nettype = 0, int restoreHeight = 0}) { - final pathPointer = path.toNativeUtf8(); - final passwordPointer = password.toNativeUtf8(); - final languagePointer = language.toNativeUtf8(); - final addressPointer = address.toNativeUtf8(); - final viewKeyPointer = viewKey.toNativeUtf8(); - final spendKeyPointer = spendKey.toNativeUtf8(); - final errorMessagePointer = ''.toNativeUtf8(); - final isWalletRestored = restoreWalletFromKeysNative( - pathPointer, - passwordPointer, - languagePointer, - addressPointer, - viewKeyPointer, - spendKeyPointer, - nettype, - restoreHeight, - errorMessagePointer) != - 0; + wptr = monero.WalletManager_createWalletFromKeys( + wmPtr, + path: path, + password: password, + restoreHeight: restoreHeight, + addressString: address, + viewKeyString: viewKey, + spendKeyString: spendKey, + nettype: 0, + ); - calloc.free(pathPointer); - calloc.free(passwordPointer); - calloc.free(languagePointer); - calloc.free(addressPointer); - calloc.free(viewKeyPointer); - calloc.free(spendKeyPointer); - - if (!isWalletRestored) { + final status = monero.Wallet_status(wptr!); + if (status != 0) { throw WalletRestoreFromKeysException( - message: convertUTF8ToString(pointer: errorMessagePointer)); + message: monero.Wallet_errorString(wptr!)); } + + openedWalletsByPath[path] = wptr!; } void restoreWalletFromSpendKeySync( {required String path, - required String password, - required String seed, - required String language, - required String spendKey, - int nettype = 0, - int restoreHeight = 0}) { - final pathPointer = path.toNativeUtf8(); - final passwordPointer = password.toNativeUtf8(); - final seedPointer = seed.toNativeUtf8(); - final languagePointer = language.toNativeUtf8(); - final spendKeyPointer = spendKey.toNativeUtf8(); - final errorMessagePointer = ''.toNativeUtf8(); - final isWalletRestored = restoreWalletFromSpendKeyNative( - pathPointer, - passwordPointer, - seedPointer, - languagePointer, - spendKeyPointer, - nettype, - restoreHeight, - errorMessagePointer) != - 0; + required String password, + required String seed, + required String language, + required String spendKey, + int nettype = 0, + int restoreHeight = 0}) { + // wptr = monero.WalletManager_createWalletFromKeys( + // wmPtr, + // path: path, + // password: password, + // restoreHeight: restoreHeight, + // addressString: '', + // spendKeyString: spendKey, + // viewKeyString: '', + // nettype: 0, + // ); - calloc.free(pathPointer); - calloc.free(passwordPointer); - calloc.free(languagePointer); - calloc.free(spendKeyPointer); + wptr = monero.WalletManager_createDeterministicWalletFromSpendKey( + wmPtr, + path: path, + password: password, + language: language, + spendKeyString: spendKey, + newWallet: true, // TODO(mrcyjanek): safe to remove + restoreHeight: restoreHeight, + ); + + final status = monero.Wallet_status(wptr!); + + if (status != 0) { + final err = monero.Wallet_errorString(wptr!); + print("err: $err"); + throw WalletRestoreFromKeysException(message: err); + } + + monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed); storeSync(); - if (!isWalletRestored) { - throw WalletRestoreFromKeysException( - message: convertUTF8ToString(pointer: errorMessagePointer)); - } + openedWalletsByPath[path] = wptr!; } +String _lastOpenedWallet = ""; + // void restoreMoneroWalletFromDevice( // {required String path, // required String password, @@ -221,20 +179,35 @@ void restoreWalletFromSpendKeySync( // } // } +Map openedWalletsByPath = {}; -void loadWallet({ - required String path, - required String password, - int nettype = 0}) { - final pathPointer = path.toNativeUtf8(); - final passwordPointer = password.toNativeUtf8(); - final loaded = loadWalletNative(pathPointer, passwordPointer, nettype) != 0; - calloc.free(pathPointer); - calloc.free(passwordPointer); - - if (!loaded) { - throw WalletOpeningException( - message: convertUTF8ToString(pointer: errorStringNative())); +void loadWallet( + {required String path, required String password, int nettype = 0}) { + if (openedWalletsByPath[path] != null) { + wptr = openedWalletsByPath[path]!; + return; + } + try { + if (wptr == null || path != _lastOpenedWallet) { + if (wptr != null) { + final addr = wptr!.address; + Isolate.run(() { + monero.Wallet_store(Pointer.fromAddress(addr)); + }); + } + wptr = monero.WalletManager_openWallet(wmPtr, + path: path, password: password); + openedWalletsByPath[path] = wptr!; + _lastOpenedWallet = path; + } + } catch (e) { + print(e); + } + final status = monero.Wallet_status(wptr!); + if (status != 0) { + final err = monero.Wallet_errorString(wptr!); + print(err); + throw WalletOpeningException(message: err); } } @@ -292,23 +265,26 @@ void _restoreFromSpendKey(Map args) { spendKey: spendKey); } -Future _openWallet(Map args) async => - loadWallet(path: args['path'] as String, password: args['password'] as String); +Future _openWallet(Map args) async => loadWallet( + path: args['path'] as String, password: args['password'] as String); bool _isWalletExist(String path) => isWalletExistSync(path: path); -void openWallet({required String path, required String password, int nettype = 0}) async => +void openWallet( + {required String path, + required String password, + int nettype = 0}) async => loadWallet(path: path, password: password, nettype: nettype); Future openWalletAsync(Map args) async => - compute(_openWallet, args); + _openWallet(args); Future createWallet( {required String path, required String password, required String language, int nettype = 0}) async => - compute(_createWallet, { + _createWallet({ 'path': path, 'password': password, 'language': language, @@ -321,7 +297,7 @@ Future restoreFromSeed( required String seed, int nettype = 0, int restoreHeight = 0}) async => - compute, void>(_restoreFromSeed, { + _restoreFromSeed({ 'path': path, 'password': password, 'seed': seed, @@ -338,7 +314,7 @@ Future restoreFromKeys( required String spendKey, int nettype = 0, int restoreHeight = 0}) async => - compute, void>(_restoreFromKeys, { + _restoreFromKeys({ 'path': path, 'password': password, 'language': language, @@ -350,14 +326,14 @@ Future restoreFromKeys( }); Future restoreFromSpendKey( - {required String path, - required String password, - required String seed, - required String language, - required String spendKey, - int nettype = 0, - int restoreHeight = 0}) async => - compute, void>(_restoreFromSpendKey, { + {required String path, + required String password, + required String seed, + required String language, + required String spendKey, + int nettype = 0, + int restoreHeight = 0}) async => + _restoreFromSpendKey({ 'path': path, 'password': password, 'seed': seed, @@ -367,4 +343,4 @@ Future restoreFromSpendKey( 'restoreHeight': restoreHeight }); -Future isWalletExist({required String path}) => compute(_isWalletExist, path); +bool isWalletExist({required String path}) => _isWalletExist(path); diff --git a/cw_monero/lib/monero_account_list.dart b/cw_monero/lib/monero_account_list.dart index 2fd11b3ba..29d096efd 100644 --- a/cw_monero/lib/monero_account_list.dart +++ b/cw_monero/lib/monero_account_list.dart @@ -2,7 +2,7 @@ import 'package:cw_core/monero_amount_format.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/account.dart'; import 'package:cw_monero/api/account_list.dart' as account_list; -import 'package:cw_monero/api/wallet.dart' as monero_wallet; +import 'package:monero/monero.dart' as monero; part 'monero_account_list.g.dart'; @@ -44,13 +44,12 @@ abstract class MoneroAccountListBase with Store { } List getAll() => account_list.getAllAccount().map((accountRow) { - final accountIndex = accountRow.getId(); - final balance = monero_wallet.getFullBalance(accountIndex: accountIndex); + final balance = monero.SubaddressAccountRow_getUnlockedBalance(accountRow); return Account( - id: accountRow.getId(), - label: accountRow.getLabel(), - balance: moneroAmountToString(amount: balance), + id: monero.SubaddressAccountRow_getRowId(accountRow), + label: monero.SubaddressAccountRow_getLabel(accountRow), + balance: moneroAmountToString(amount: monero.Wallet_amountFromString(balance)), ); }).toList(); diff --git a/cw_monero/lib/monero_subaddress_list.dart b/cw_monero/lib/monero_subaddress_list.dart index dbd1a89ae..676a9536c 100644 --- a/cw_monero/lib/monero_subaddress_list.dart +++ b/cw_monero/lib/monero_subaddress_list.dart @@ -1,8 +1,8 @@ -import 'package:flutter/services.dart'; -import 'package:mobx/mobx.dart'; +import 'package:cw_core/subaddress.dart'; import 'package:cw_monero/api/coins_info.dart'; import 'package:cw_monero/api/subaddress_list.dart' as subaddress_list; -import 'package:cw_core/subaddress.dart'; +import 'package:flutter/services.dart'; +import 'package:mobx/mobx.dart'; part 'monero_subaddress_list.g.dart'; @@ -50,19 +50,22 @@ abstract class MoneroSubaddressListBase with Store { subaddresses = [primary] + rest.toList(); } - return subaddresses.map((subaddressRow) { + return subaddresses.map((s) { + final address = s.address; + final label = s.label; + final id = s.addressIndex; final hasDefaultAddressName = - subaddressRow.getLabel().toLowerCase() == 'Primary account'.toLowerCase() || - subaddressRow.getLabel().toLowerCase() == 'Untitled account'.toLowerCase(); - final isPrimaryAddress = subaddressRow.getId() == 0 && hasDefaultAddressName; + label.toLowerCase() == 'Primary account'.toLowerCase() || + label.toLowerCase() == 'Untitled account'.toLowerCase(); + final isPrimaryAddress = id == 0 && hasDefaultAddressName; return Subaddress( - id: subaddressRow.getId(), - address: subaddressRow.getAddress(), + id: id, + address: address, label: isPrimaryAddress ? 'Primary address' : hasDefaultAddressName ? '' - : subaddressRow.getLabel()); + : label); }).toList(); } @@ -121,8 +124,8 @@ abstract class MoneroSubaddressListBase with Store { Future> _getAllUnusedAddresses( {required int accountIndex, required String label}) async { final allAddresses = subaddress_list.getAllSubaddresses(); - - if (allAddresses.isEmpty || _usedAddresses.contains(allAddresses.last.getAddress())) { + final lastAddress = allAddresses.last.address; + if (allAddresses.isEmpty || _usedAddresses.contains(lastAddress)) { final isAddressUnused = await _newSubaddress(accountIndex: accountIndex, label: label); if (!isAddressUnused) { return await _getAllUnusedAddresses(accountIndex: accountIndex, label: label); @@ -130,13 +133,18 @@ abstract class MoneroSubaddressListBase with Store { } return allAddresses - .map((subaddressRow) => Subaddress( - id: subaddressRow.getId(), - address: subaddressRow.getAddress(), - label: subaddressRow.getId() == 0 && - subaddressRow.getLabel().toLowerCase() == 'Primary account'.toLowerCase() + .map((s) { + final id = s.addressIndex; + final address = s.address; + final label = s.label; + return Subaddress( + id: id, + address: address, + label: id == 0 && + label.toLowerCase() == 'Primary account'.toLowerCase() ? 'Primary address' - : subaddressRow.getLabel())) + : label); + }) .toList(); } @@ -145,7 +153,10 @@ abstract class MoneroSubaddressListBase with Store { return subaddress_list .getAllSubaddresses() - .where((subaddressRow) => !_usedAddresses.contains(subaddressRow.getAddress())) + .where((s) { + final address = s.address; + return !_usedAddresses.contains(address); + }) .isNotEmpty; } } diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index 8af6b653c..4b596648e 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -1,5 +1,7 @@ import 'dart:async'; +import 'dart:ffi'; import 'dart:io'; +import 'dart:isolate'; import 'package:cw_core/account.dart'; import 'package:cw_core/crypto_currency.dart'; @@ -12,15 +14,18 @@ import 'package:cw_core/node.dart'; import 'package:cw_core/pathForWallet.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/transaction_priority.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_info.dart'; +import 'package:cw_monero/api/account_list.dart'; import 'package:cw_monero/api/coins_info.dart'; import 'package:cw_monero/api/monero_output.dart'; import 'package:cw_monero/api/structs/pending_transaction.dart'; import 'package:cw_monero/api/transaction_history.dart' as transaction_history; import 'package:cw_monero/api/wallet.dart' as monero_wallet; +import 'package:cw_monero/api/wallet_manager.dart'; import 'package:cw_monero/exceptions/monero_transaction_creation_exception.dart'; import 'package:cw_monero/exceptions/monero_transaction_no_inputs_exception.dart'; import 'package:cw_monero/monero_transaction_creation_credentials.dart'; @@ -32,6 +37,7 @@ import 'package:cw_monero/pending_monero_transaction.dart'; import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; +import 'package:monero/monero.dart' as monero; part 'monero_wallet.g.dart'; @@ -41,10 +47,11 @@ const MIN_RESTORE_HEIGHT = 1000; class MoneroWallet = MoneroWalletBase with _$MoneroWallet; -abstract class MoneroWalletBase - extends WalletBase with Store { +abstract class MoneroWalletBase extends WalletBase with Store { MoneroWalletBase( - {required WalletInfo walletInfo, required Box unspentCoinsInfo}) + {required WalletInfo walletInfo, + required Box unspentCoinsInfo}) : balance = ObservableMap.of({ CryptoCurrency.xmr: MoneroBalance( fullBalance: monero_wallet.getFullBalance(accountIndex: 0), @@ -60,13 +67,16 @@ abstract class MoneroWalletBase transactionHistory = MoneroTransactionHistory(); walletAddresses = MoneroWalletAddresses(walletInfo, transactionHistory); - _onAccountChangeReaction = reaction((_) => walletAddresses.account, (Account? account) { + _onAccountChangeReaction = + reaction((_) => walletAddresses.account, (Account? account) { if (account == null) return; - balance = ObservableMap.of({ + balance = ObservableMap.of({ currency: MoneroBalance( fullBalance: monero_wallet.getFullBalance(accountIndex: account.id), - unlockedBalance: monero_wallet.getUnlockedBalance(accountIndex: account.id)) + unlockedBalance: + monero_wallet.getUnlockedBalance(accountIndex: account.id)) }); _updateSubAddress(isEnabledAutoGenerateSubaddress, account: account); _askForUpdateTransactionHistory(); @@ -100,6 +110,9 @@ abstract class MoneroWalletBase @override String get seed => monero_wallet.getSeed(); + String seedLegacy(String? language) { + return monero_wallet.getSeedLegacy(language); + } @override MoneroWalletKeys get keys => MoneroWalletKeys( @@ -108,7 +121,8 @@ abstract class MoneroWalletBase publicSpendKey: monero_wallet.getPublicSpendKey(), publicViewKey: monero_wallet.getPublicViewKey()); - int? get restoreHeight => transactionHistory.transactions.values.firstOrNull?.height; + int? get restoreHeight => + transactionHistory.transactions.values.firstOrNull?.height; monero_wallet.SyncListener? _listener; ReactionDisposer? _onAccountChangeReaction; @@ -119,11 +133,13 @@ abstract class MoneroWalletBase Future init() async { await walletAddresses.init(); - balance = ObservableMap.of({ + balance = ObservableMap.of({ currency: MoneroBalance( - fullBalance: monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id), - unlockedBalance: - monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id)) + fullBalance: monero_wallet.getFullBalance( + accountIndex: walletAddresses.account!.id), + unlockedBalance: monero_wallet.getUnlockedBalance( + accountIndex: walletAddresses.account!.id)) }); _setListeners(); await updateTransactions(); @@ -132,19 +148,20 @@ abstract class MoneroWalletBase monero_wallet.setRecoveringFromSeed(isRecovery: walletInfo.isRecovery); if (monero_wallet.getCurrentHeight() <= 1) { - monero_wallet.setRefreshFromBlockHeight(height: walletInfo.restoreHeight); + monero_wallet.setRefreshFromBlockHeight( + height: walletInfo.restoreHeight); } } - _autoSaveTimer = - Timer.periodic(Duration(seconds: _autoSaveInterval), (_) async => await save()); + _autoSaveTimer = Timer.periodic( + Duration(seconds: _autoSaveInterval), (_) async => await save()); } @override Future? updateBalance() => null; @override - void close() { + void close() async { _listener?.stop(); _onAccountChangeReaction?.reaction.dispose(); _autoSaveTimer?.cancel(); @@ -209,8 +226,8 @@ abstract class MoneroWalletBase final inputs = []; final outputs = _credentials.outputs; final hasMultiDestination = outputs.length > 1; - final unlockedBalance = - monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id); + final unlockedBalance = monero_wallet.getUnlockedBalance( + accountIndex: walletAddresses.account!.id); var allInputsAmount = 0; PendingTransactionDescription pendingTransactionDescription; @@ -232,16 +249,20 @@ abstract class MoneroWalletBase final spendAllCoins = inputs.length == unspentCoins.length; if (hasMultiDestination) { - if (outputs.any((item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) { - throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.'); + if (outputs.any( + (item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) { + throw MoneroTransactionCreationException( + 'You do not have enough XMR to send this amount.'); } - final int totalAmount = - outputs.fold(0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0)); + final int totalAmount = outputs.fold( + 0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0)); - final estimatedFee = calculateEstimatedFee(_credentials.priority, totalAmount); + final estimatedFee = + calculateEstimatedFee(_credentials.priority, totalAmount); if (unlockedBalance < totalAmount) { - throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.'); + throw MoneroTransactionCreationException( + 'You do not have enough XMR to send this amount.'); } if (!spendAllCoins && (allInputsAmount < totalAmount + estimatedFee)) { @@ -249,22 +270,28 @@ abstract class MoneroWalletBase } final moneroOutputs = outputs.map((output) { - final outputAddress = output.isParsedAddress ? output.extractedAddress : output.address; + final outputAddress = + output.isParsedAddress ? output.extractedAddress : output.address; return MoneroOutput( - address: outputAddress!, amount: output.cryptoAmount!.replaceAll(',', '.')); + address: outputAddress!, + amount: output.cryptoAmount!.replaceAll(',', '.')); }).toList(); - pendingTransactionDescription = await transaction_history.createTransactionMultDest( - outputs: moneroOutputs, - priorityRaw: _credentials.priority.serialize(), - accountIndex: walletAddresses.account!.id, - preferredInputs: inputs); + pendingTransactionDescription = + await transaction_history.createTransactionMultDest( + outputs: moneroOutputs, + priorityRaw: _credentials.priority.serialize(), + accountIndex: walletAddresses.account!.id, + preferredInputs: inputs); } else { final output = outputs.first; - final address = output.isParsedAddress ? output.extractedAddress : output.address; - final amount = output.sendAll ? null : output.cryptoAmount!.replaceAll(',', '.'); - final formattedAmount = output.sendAll ? null : output.formattedCryptoAmount; + final address = + output.isParsedAddress ? output.extractedAddress : output.address; + final amount = + output.sendAll ? null : output.cryptoAmount!.replaceAll(',', '.'); + final formattedAmount = + output.sendAll ? null : output.formattedCryptoAmount; if ((formattedAmount != null && unlockedBalance < formattedAmount) || (formattedAmount == null && unlockedBalance <= 0)) { @@ -274,19 +301,22 @@ abstract class MoneroWalletBase 'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.'); } - final estimatedFee = calculateEstimatedFee(_credentials.priority, formattedAmount); + final estimatedFee = + calculateEstimatedFee(_credentials.priority, formattedAmount); if (!spendAllCoins && - ((formattedAmount != null && allInputsAmount < (formattedAmount + estimatedFee)) || + ((formattedAmount != null && + allInputsAmount < (formattedAmount + estimatedFee)) || formattedAmount == null)) { throw MoneroTransactionNoInputsException(inputs.length); } - pendingTransactionDescription = await transaction_history.createTransaction( - address: address!, - amount: amount, - priorityRaw: _credentials.priority.serialize(), - accountIndex: walletAddresses.account!.id, - preferredInputs: inputs); + pendingTransactionDescription = + await transaction_history.createTransaction( + address: address!, + amount: amount, + priorityRaw: _credentials.priority.serialize(), + accountIndex: walletAddresses.account!.id, + preferredInputs: inputs); } return PendingMoneroTransaction(pendingTransactionDescription); @@ -325,18 +355,36 @@ abstract class MoneroWalletBase } await walletAddresses.updateAddressesInBox(); - await backupWalletFiles(name); await monero_wallet.store(); + try { + await backupWalletFiles(name); + } catch (e) { + print("¯\\_(ツ)_/¯"); + print(e); + } } @override Future renameWalletFiles(String newWalletName) async { final currentWalletDirPath = await pathForWalletDir(name: name, type: type); - + if (openedWalletsByPath["$currentWalletDirPath/$name"] != null) { + // NOTE: this is realistically only required on windows. + print("closing wallet"); + final wmaddr = wmPtr.address; + final waddr = openedWalletsByPath["$currentWalletDirPath/$name"]!.address; + await Isolate.run(() { + monero.WalletManager_closeWallet( + Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true); + }); + openedWalletsByPath.remove("$currentWalletDirPath/$name"); + print("wallet closed"); + } try { // -- rename the waller folder -- - final currentWalletDir = Directory(await pathForWalletDir(name: name, type: type)); - final newWalletDirPath = await pathForWalletDir(name: newWalletName, type: type); + final currentWalletDir = + Directory(await pathForWalletDir(name: name, type: type)); + final newWalletDirPath = + await pathForWalletDir(name: newWalletName, type: type); await currentWalletDir.rename(newWalletDirPath); // -- use new waller folder to rename files with old names still -- @@ -346,7 +394,8 @@ abstract class MoneroWalletBase final currentKeysFile = File('$renamedWalletPath.keys'); final currentAddressListFile = File('$renamedWalletPath.address.txt'); - final newWalletPath = await pathForWallet(name: newWalletName, type: type); + final newWalletPath = + await pathForWallet(name: newWalletName, type: type); if (currentCacheFile.existsSync()) { await currentCacheFile.rename(newWalletPath); @@ -366,7 +415,8 @@ abstract class MoneroWalletBase final currentKeysFile = File('$currentWalletPath.keys'); final currentAddressListFile = File('$currentWalletPath.address.txt'); - final newWalletPath = await pathForWallet(name: newWalletName, type: type); + final newWalletPath = + await pathForWallet(name: newWalletName, type: type); // Copies current wallet files into new wallet name's dir and files if (currentCacheFile.existsSync()) { @@ -385,7 +435,8 @@ abstract class MoneroWalletBase } @override - Future changePassword(String password) async => monero_wallet.setPasswordSync(password); + Future changePassword(String password) async => + monero_wallet.setPasswordSync(password); Future getNodeHeight() async => monero_wallet.getNodeHeight(); @@ -419,10 +470,19 @@ abstract class MoneroWalletBase final coinCount = countOfCoins(); for (var i = 0; i < coinCount; i++) { final coin = getCoin(i); - if (coin.spent == 0) { - final unspent = MoneroUnspent.fromCoinsInfoRow(coin); + final coinSpent = monero.CoinsInfo_spent(coin); + if (coinSpent == false) { + final unspent = MoneroUnspent( + monero.CoinsInfo_address(coin), + monero.CoinsInfo_hash(coin), + monero.CoinsInfo_keyImage(coin), + monero.CoinsInfo_amount(coin), + monero.CoinsInfo_frozen(coin), + monero.CoinsInfo_unlocked(coin), + ); + // TODO: double-check the logic here if (unspent.hash.isNotEmpty) { - unspent.isChange = transaction_history.getTransaction(unspent.hash).direction == 1; + unspent.isChange = transaction_history.getTransaction(unspent.hash).isSpend == true; } unspentCoins.add(unspent); } @@ -484,13 +544,15 @@ abstract class MoneroWalletBase Future _refreshUnspentCoinsInfo() async { try { final List keys = []; - final currentWalletUnspentCoins = unspentCoinsInfo.values.where((element) => - element.walletId.contains(id) && element.accountIndex == walletAddresses.account!.id); + final currentWalletUnspentCoins = unspentCoinsInfo.values.where( + (element) => + element.walletId.contains(id) && + element.accountIndex == walletAddresses.account!.id); if (currentWalletUnspentCoins.isNotEmpty) { currentWalletUnspentCoins.forEach((element) { - final existUnspentCoins = - unspentCoins.where((coin) => element.keyImage!.contains(coin.keyImage!)); + final existUnspentCoins = unspentCoins + .where((coin) => element.keyImage!.contains(coin.keyImage!)); if (existUnspentCoins.isEmpty) { keys.add(element.key); @@ -507,13 +569,15 @@ abstract class MoneroWalletBase } String getTransactionAddress(int accountIndex, int addressIndex) => - monero_wallet.getAddress(accountIndex: accountIndex, addressIndex: addressIndex); + monero_wallet.getAddress( + accountIndex: accountIndex, addressIndex: addressIndex); @override Future> fetchTransactions() async { transaction_history.refreshTransactions(); return _getAllTransactionsOfAccount(walletAddresses.account?.id) - .fold>({}, + .fold>( + {}, (Map acc, MoneroTransactionInfo tx) { acc[tx.id] = tx; return acc; @@ -541,11 +605,31 @@ abstract class MoneroWalletBase String getSubaddressLabel(int accountIndex, int addressIndex) => monero_wallet.getSubaddressLabel(accountIndex, addressIndex); - List _getAllTransactionsOfAccount(int? accountIndex) => transaction_history - .getAllTransactions() - .map((row) => MoneroTransactionInfo.fromRow(row)) - .where((element) => element.accountIndex == (accountIndex ?? 0)) - .toList(); + List _getAllTransactionsOfAccount(int? accountIndex) => + transaction_history + .getAllTransactions() + .map( + (row) => MoneroTransactionInfo( + row.hash, + row.blockheight, + row.isSpend + ? TransactionDirection.outgoing + : TransactionDirection.incoming, + row.timeStamp, + row.isPending, + row.amount, + row.accountIndex, + 0, + row.fee, + row.confirmations, + )..additionalInfo = { + 'key': row.key, + 'accountIndex': row.accountIndex, + 'addressIndex': row.addressIndex + }, + ) + .where((element) => element.accountIndex == (accountIndex ?? 0)) + .toList(); void _setListeners() { _listener?.stop(); @@ -583,7 +667,8 @@ abstract class MoneroWalletBase } int _getHeightDistance(DateTime date) { - final distance = DateTime.now().millisecondsSinceEpoch - date.millisecondsSinceEpoch; + final distance = + DateTime.now().millisecondsSinceEpoch - date.millisecondsSinceEpoch; final daysTmp = (distance / 86400).round(); final days = daysTmp < 1 ? 1 : daysTmp; @@ -611,22 +696,27 @@ abstract class MoneroWalletBase balance[currency]!.unlockedBalance != unlockedBalance || balance[currency]!.frozenBalance != frozenBalance) { balance[currency] = MoneroBalance( - fullBalance: fullBalance, unlockedBalance: unlockedBalance, frozenBalance: frozenBalance); + fullBalance: fullBalance, + unlockedBalance: unlockedBalance, + frozenBalance: frozenBalance); } } - Future _askForUpdateTransactionHistory() async => await updateTransactions(); + Future _askForUpdateTransactionHistory() async => + await updateTransactions(); - int _getFullBalance() => monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id); + int _getFullBalance() => + monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id); - int _getUnlockedBalance() => - monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id); + int _getUnlockedBalance() => monero_wallet.getUnlockedBalance( + accountIndex: walletAddresses.account!.id); int _getFrozenBalance() { var frozenBalance = 0; for (var coin in unspentCoinsInfo.values.where((element) => - element.walletId == id && element.accountIndex == walletAddresses.account!.id)) { + element.walletId == id && + element.accountIndex == walletAddresses.account!.id)) { if (coin.isFrozen) frozenBalance += coin.value; } diff --git a/cw_monero/lib/monero_wallet_service.dart b/cw_monero/lib/monero_wallet_service.dart index bc59499f9..ea2f3b766 100644 --- a/cw_monero/lib/monero_wallet_service.dart +++ b/cw_monero/lib/monero_wallet_service.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:cw_core/monero_wallet_utils.dart'; import 'package:cw_core/pathForWallet.dart'; @@ -10,10 +11,12 @@ import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/get_height_by_date.dart'; import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart'; import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager; +import 'package:cw_monero/api/wallet_manager.dart'; import 'package:cw_monero/monero_wallet.dart'; import 'package:flutter/widgets.dart'; import 'package:hive/hive.dart'; import 'package:polyseed/polyseed.dart'; +import 'package:monero/monero.dart' as monero; class MoneroNewWalletCredentials extends WalletCredentials { MoneroNewWalletCredentials( @@ -174,6 +177,22 @@ class MoneroWalletService extends WalletService remove(String wallet) async { final path = await pathForWalletDir(name: wallet, type: getType()); + if (openedWalletsByPath["$path/$wallet"] != null) { + // NOTE: this is realistically only required on windows. + print("closing wallet"); + final wmaddr = wmPtr.address; + final waddr = openedWalletsByPath["$path/$wallet"]!.address; + // await Isolate.run(() { + monero.WalletManager_closeWallet( + Pointer.fromAddress(wmaddr), + Pointer.fromAddress(waddr), + false + ); + // }); + openedWalletsByPath.remove("$path/$wallet"); + print("wallet closed"); + } + final file = Directory(path); final isExist = file.existsSync(); diff --git a/cw_monero/macos/Classes/CwMoneroPlugin.swift b/cw_monero/macos/Classes/CwMoneroPlugin.swift deleted file mode 100644 index d4ff81e1c..000000000 --- a/cw_monero/macos/Classes/CwMoneroPlugin.swift +++ /dev/null @@ -1,19 +0,0 @@ -import Cocoa -import FlutterMacOS - -public class CwMoneroPlugin: NSObject, FlutterPlugin { - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "cw_monero", binaryMessenger: registrar.messenger) - let instance = CwMoneroPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - switch call.method { - case "getPlatformVersion": - result("macOS " + ProcessInfo.processInfo.operatingSystemVersionString) - default: - result(FlutterMethodNotImplemented) - } - } -} diff --git a/cw_monero/macos/Classes/CwWalletListener.h b/cw_monero/macos/Classes/CwWalletListener.h deleted file mode 100644 index cbfcb0c4e..000000000 --- a/cw_monero/macos/Classes/CwWalletListener.h +++ /dev/null @@ -1,23 +0,0 @@ -#include - -struct CWMoneroWalletListener; - -typedef int8_t (*on_new_block_callback)(uint64_t height); -typedef int8_t (*on_need_to_refresh_callback)(); - -typedef struct CWMoneroWalletListener -{ - // on_money_spent_callback *on_money_spent; - // on_money_received_callback *on_money_received; - // on_unconfirmed_money_received_callback *on_unconfirmed_money_received; - // on_new_block_callback *on_new_block; - // on_updated_callback *on_updated; - // on_refreshed_callback *on_refreshed; - - on_new_block_callback on_new_block; -} CWMoneroWalletListener; - -struct TestListener { - // int8_t x; - on_new_block_callback on_new_block; -}; \ No newline at end of file diff --git a/cw_monero/macos/Classes/monero_api.cpp b/cw_monero/macos/Classes/monero_api.cpp deleted file mode 100644 index fe75dea98..000000000 --- a/cw_monero/macos/Classes/monero_api.cpp +++ /dev/null @@ -1,1032 +0,0 @@ -#include -#include "cstdlib" -#include -#include -#include -#include -#include -#include -#include -#include "thread" -#include "CwWalletListener.h" -#if __APPLE__ -// Fix for randomx on ios -void __clear_cache(void* start, void* end) { } -#include "../External/macos/include/wallet2_api.h" -#else -#include "../External/android/include/wallet2_api.h" -#endif - -using namespace std::chrono_literals; -#ifdef __cplusplus -extern "C" -{ -#endif - const uint64_t MONERO_BLOCK_SIZE = 1000; - - struct Utf8Box - { - char *value; - - Utf8Box(char *_value) - { - value = _value; - } - }; - - struct SubaddressRow - { - uint64_t id; - char *address; - char *label; - - SubaddressRow(std::size_t _id, char *_address, char *_label) - { - id = static_cast(_id); - address = _address; - label = _label; - } - }; - - struct AccountRow - { - uint64_t id; - char *label; - - AccountRow(std::size_t _id, char *_label) - { - id = static_cast(_id); - label = _label; - } - }; - - struct MoneroWalletListener : Monero::WalletListener - { - uint64_t m_height; - bool m_need_to_refresh; - bool m_new_transaction; - - MoneroWalletListener() - { - m_height = 0; - m_need_to_refresh = false; - m_new_transaction = false; - } - - void moneySpent(const std::string &txId, uint64_t amount) - { - m_new_transaction = true; - } - - void moneyReceived(const std::string &txId, uint64_t amount) - { - m_new_transaction = true; - } - - void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) - { - m_new_transaction = true; - } - - void newBlock(uint64_t height) - { - m_height = height; - } - - void updated() - { - m_new_transaction = true; - } - - void refreshed() - { - m_need_to_refresh = true; - } - - void resetNeedToRefresh() - { - m_need_to_refresh = false; - } - - bool isNeedToRefresh() - { - return m_need_to_refresh; - } - - bool isNewTransactionExist() - { - return m_new_transaction; - } - - void resetIsNewTransactionExist() - { - m_new_transaction = false; - } - - uint64_t height() - { - return m_height; - } - }; - - struct TransactionInfoRow - { - uint64_t amount; - uint64_t fee; - uint64_t blockHeight; - uint64_t confirmations; - uint32_t subaddrAccount; - int8_t direction; - int8_t isPending; - uint32_t subaddrIndex; - - char *hash; - char *paymentId; - - int64_t datetime; - - TransactionInfoRow(Monero::TransactionInfo *transaction) - { - amount = transaction->amount(); - fee = transaction->fee(); - blockHeight = transaction->blockHeight(); - subaddrAccount = transaction->subaddrAccount(); - std::set::iterator it = transaction->subaddrIndex().begin(); - subaddrIndex = *it; - confirmations = transaction->confirmations(); - datetime = static_cast(transaction->timestamp()); - direction = transaction->direction(); - isPending = static_cast(transaction->isPending()); - std::string *hash_str = new std::string(transaction->hash()); - hash = strdup(hash_str->c_str()); - paymentId = strdup(transaction->paymentId().c_str()); - } - }; - - struct PendingTransactionRaw - { - uint64_t amount; - uint64_t fee; - char *hash; - char *hex; - char *txKey; - Monero::PendingTransaction *transaction; - - PendingTransactionRaw(Monero::PendingTransaction *_transaction) - { - transaction = _transaction; - amount = _transaction->amount(); - fee = _transaction->fee(); - hash = strdup(_transaction->txid()[0].c_str()); - hex = strdup(_transaction->hex()[0].c_str()); - txKey = strdup(_transaction->txKey()[0].c_str()); - } - }; - - struct CoinsInfoRow - { - uint64_t blockHeight; - char *hash; - uint64_t internalOutputIndex; - uint64_t globalOutputIndex; - bool spent; - bool frozen; - uint64_t spentHeight; - uint64_t amount; - bool rct; - bool keyImageKnown; - uint64_t pkIndex; - uint32_t subaddrIndex; - uint32_t subaddrAccount; - char *address; - char *addressLabel; - char *keyImage; - uint64_t unlockTime; - bool unlocked; - char *pubKey; - bool coinbase; - char *description; - - CoinsInfoRow(Monero::CoinsInfo *coinsInfo) - { - blockHeight = coinsInfo->blockHeight(); - std::string *hash_str = new std::string(coinsInfo->hash()); - hash = strdup(hash_str->c_str()); - internalOutputIndex = coinsInfo->internalOutputIndex(); - globalOutputIndex = coinsInfo->globalOutputIndex(); - spent = coinsInfo->spent(); - frozen = coinsInfo->frozen(); - spentHeight = coinsInfo->spentHeight(); - amount = coinsInfo->amount(); - rct = coinsInfo->rct(); - keyImageKnown = coinsInfo->keyImageKnown(); - pkIndex = coinsInfo->pkIndex(); - subaddrIndex = coinsInfo->subaddrIndex(); - subaddrAccount = coinsInfo->subaddrAccount(); - address = strdup(coinsInfo->address().c_str()) ; - addressLabel = strdup(coinsInfo->addressLabel().c_str()); - keyImage = strdup(coinsInfo->keyImage().c_str()); - unlockTime = coinsInfo->unlockTime(); - unlocked = coinsInfo->unlocked(); - pubKey = strdup(coinsInfo->pubKey().c_str()); - coinbase = coinsInfo->coinbase(); - description = strdup(coinsInfo->description().c_str()); - } - - void setUnlocked(bool unlocked); - }; - - Monero::Coins *m_coins; - - Monero::Wallet *m_wallet; - Monero::TransactionHistory *m_transaction_history; - MoneroWalletListener *m_listener; - Monero::Subaddress *m_subaddress; - Monero::SubaddressAccount *m_account; - uint64_t m_last_known_wallet_height; - uint64_t m_cached_syncing_blockchain_height = 0; - std::list m_coins_info; - std::mutex store_lock; - bool is_storing = false; - - void change_current_wallet(Monero::Wallet *wallet) - { - m_wallet = wallet; - m_listener = nullptr; - - - if (wallet != nullptr) - { - m_transaction_history = wallet->history(); - } - else - { - m_transaction_history = nullptr; - } - - if (wallet != nullptr) - { - m_account = wallet->subaddressAccount(); - } - else - { - m_account = nullptr; - } - - if (wallet != nullptr) - { - m_subaddress = wallet->subaddress(); - } - else - { - m_subaddress = nullptr; - } - - m_coins_info = std::list(); - - if (wallet != nullptr) - { - m_coins = wallet->coins(); - } - else - { - m_coins = nullptr; - } - } - - Monero::Wallet *get_current_wallet() - { - return m_wallet; - } - - bool create_wallet(char *path, char *password, char *language, int32_t networkType, char *error) - { - Monero::NetworkType _networkType = static_cast(networkType); - Monero::WalletManager *walletManager = Monero::WalletManagerFactory::getWalletManager(); - Monero::Wallet *wallet = walletManager->createWallet(path, password, language, _networkType); - - int status; - std::string errorString; - - wallet->statusWithErrorString(status, errorString); - - if (wallet->status() != Monero::Wallet::Status_Ok) - { - error = strdup(wallet->errorString().c_str()); - return false; - } - - change_current_wallet(wallet); - - return true; - } - - bool restore_wallet_from_seed(char *path, char *password, char *seed, int32_t networkType, uint64_t restoreHeight, char *error) - { - Monero::NetworkType _networkType = static_cast(networkType); - Monero::Wallet *wallet = Monero::WalletManagerFactory::getWalletManager()->recoveryWallet( - std::string(path), - std::string(password), - std::string(seed), - _networkType, - (uint64_t)restoreHeight); - - int status; - std::string errorString; - - wallet->statusWithErrorString(status, errorString); - - if (status != Monero::Wallet::Status_Ok || !errorString.empty()) - { - error = strdup(errorString.c_str()); - return false; - } - - change_current_wallet(wallet); - return true; - } - - bool restore_wallet_from_keys(char *path, char *password, char *language, char *address, char *viewKey, char *spendKey, int32_t networkType, uint64_t restoreHeight, char *error) - { - Monero::NetworkType _networkType = static_cast(networkType); - Monero::Wallet *wallet = Monero::WalletManagerFactory::getWalletManager()->createWalletFromKeys( - std::string(path), - std::string(password), - std::string(language), - _networkType, - (uint64_t)restoreHeight, - std::string(address), - std::string(viewKey), - std::string(spendKey)); - - int status; - std::string errorString; - - wallet->statusWithErrorString(status, errorString); - - if (status != Monero::Wallet::Status_Ok || !errorString.empty()) - { - error = strdup(errorString.c_str()); - return false; - } - - change_current_wallet(wallet); - return true; - } - - bool restore_wallet_from_spend_key(char *path, char *password, char *seed, char *language, char *spendKey, int32_t networkType, uint64_t restoreHeight, char *error) - { - Monero::NetworkType _networkType = static_cast(networkType); - Monero::Wallet *wallet = Monero::WalletManagerFactory::getWalletManager()->createDeterministicWalletFromSpendKey( - std::string(path), - std::string(password), - std::string(language), - _networkType, - (uint64_t)restoreHeight, - std::string(spendKey)); - - // Cache Raw to support Polyseed - wallet->setCacheAttribute("cakewallet.seed", std::string(seed)); - - int status; - std::string errorString; - - wallet->statusWithErrorString(status, errorString); - - if (status != Monero::Wallet::Status_Ok || !errorString.empty()) - { - error = strdup(errorString.c_str()); - return false; - } - - change_current_wallet(wallet); - return true; - } - - bool load_wallet(char *path, char *password, int32_t nettype) - { - nice(19); - Monero::NetworkType networkType = static_cast(nettype); - Monero::WalletManager *walletManager = Monero::WalletManagerFactory::getWalletManager(); - Monero::Wallet *wallet = walletManager->openWallet(std::string(path), std::string(password), networkType); - int status; - std::string errorString; - - wallet->statusWithErrorString(status, errorString); - change_current_wallet(wallet); - - return !(status != Monero::Wallet::Status_Ok || !errorString.empty()); - } - - char *error_string() { - return strdup(get_current_wallet()->errorString().c_str()); - } - - - bool is_wallet_exist(char *path) - { - return Monero::WalletManagerFactory::getWalletManager()->walletExists(std::string(path)); - } - - void close_current_wallet() - { - Monero::WalletManagerFactory::getWalletManager()->closeWallet(get_current_wallet()); - change_current_wallet(nullptr); - } - - char *get_filename() - { - return strdup(get_current_wallet()->filename().c_str()); - } - - char *secret_view_key() - { - return strdup(get_current_wallet()->secretViewKey().c_str()); - } - - char *public_view_key() - { - return strdup(get_current_wallet()->publicViewKey().c_str()); - } - - char *secret_spend_key() - { - return strdup(get_current_wallet()->secretSpendKey().c_str()); - } - - char *public_spend_key() - { - return strdup(get_current_wallet()->publicSpendKey().c_str()); - } - - char *get_address(uint32_t account_index, uint32_t address_index) - { - return strdup(get_current_wallet()->address(account_index, address_index).c_str()); - } - - - const char *seed() - { - std::string _rawSeed = get_current_wallet()->getCacheAttribute("cakewallet.seed"); - if (!_rawSeed.empty()) - { - return strdup(_rawSeed.c_str()); - } - return strdup(get_current_wallet()->seed().c_str()); - } - - uint64_t get_full_balance(uint32_t account_index) - { - return get_current_wallet()->balance(account_index); - } - - uint64_t get_unlocked_balance(uint32_t account_index) - { - return get_current_wallet()->unlockedBalance(account_index); - } - - uint64_t get_current_height() - { - return get_current_wallet()->blockChainHeight(); - } - - uint64_t get_node_height() - { - return get_current_wallet()->daemonBlockChainHeight(); - } - - bool connect_to_node(char *error) - { - nice(19); - bool is_connected = get_current_wallet()->connectToDaemon(); - - if (!is_connected) - { - error = strdup(get_current_wallet()->errorString().c_str()); - } - - return is_connected; - } - - bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *socksProxyAddress, char *error) - { - nice(19); - Monero::Wallet *wallet = get_current_wallet(); - - std::string _login = ""; - std::string _password = ""; - std::string _socksProxyAddress = ""; - - if (login != nullptr) - { - _login = std::string(login); - } - - if (password != nullptr) - { - _password = std::string(password); - } - - if (socksProxyAddress != nullptr) - { - _socksProxyAddress = std::string(socksProxyAddress); - } - - bool inited = wallet->init(std::string(address), 0, _login, _password, use_ssl, is_light_wallet, _socksProxyAddress); - - if (!inited) - { - error = strdup(wallet->errorString().c_str()); - } else if (!wallet->connectToDaemon()) { - error = strdup(wallet->errorString().c_str()); - } - - return inited; - } - - bool is_connected() - { - return get_current_wallet()->connected(); - } - - void start_refresh() - { - get_current_wallet()->refreshAsync(); - get_current_wallet()->startRefresh(); - } - - void set_refresh_from_block_height(uint64_t height) - { - get_current_wallet()->setRefreshFromBlockHeight(height); - } - - void set_recovering_from_seed(bool is_recovery) - { - get_current_wallet()->setRecoveringFromSeed(is_recovery); - } - - void store(char *path) - { - store_lock.lock(); - if (is_storing) { - return; - } - - is_storing = true; - get_current_wallet()->store(std::string(path)); - is_storing = false; - store_lock.unlock(); - } - - bool set_password(char *password, Utf8Box &error) { - bool is_changed = get_current_wallet()->setPassword(std::string(password)); - - if (!is_changed) { - error = Utf8Box(strdup(get_current_wallet()->errorString().c_str())); - } - - return is_changed; - } - - bool transaction_create(char *address, char *payment_id, char *amount, - uint8_t priority_raw, uint32_t subaddr_account, - char **preferred_inputs, uint32_t preferred_inputs_size, - Utf8Box &error, PendingTransactionRaw &pendingTransaction) - { - nice(19); - - std::set _preferred_inputs; - - for (int i = 0; i < preferred_inputs_size; i++) { - _preferred_inputs.insert(std::string(*preferred_inputs)); - preferred_inputs++; - } - - auto priority = static_cast(priority_raw); - std::string _payment_id; - Monero::PendingTransaction *transaction; - - if (payment_id != nullptr) - { - _payment_id = std::string(payment_id); - } - - if (amount != nullptr) - { - uint64_t _amount = Monero::Wallet::amountFromString(std::string(amount)); - transaction = m_wallet->createTransaction(std::string(address), _payment_id, _amount, m_wallet->defaultMixin(), priority, subaddr_account, {}, _preferred_inputs); - } - else - { - transaction = m_wallet->createTransaction(std::string(address), _payment_id, Monero::optional(), m_wallet->defaultMixin(), priority, subaddr_account, {}, _preferred_inputs); - } - - int status = transaction->status(); - - if (status == Monero::PendingTransaction::Status::Status_Error || status == Monero::PendingTransaction::Status::Status_Critical) - { - error = Utf8Box(strdup(transaction->errorString().c_str())); - return false; - } - - if (m_listener != nullptr) { - m_listener->m_new_transaction = true; - } - - pendingTransaction = PendingTransactionRaw(transaction); - return true; - } - - bool transaction_create_mult_dest(char **addresses, char *payment_id, char **amounts, uint32_t size, - uint8_t priority_raw, uint32_t subaddr_account, - char **preferred_inputs, uint32_t preferred_inputs_size, - Utf8Box &error, PendingTransactionRaw &pendingTransaction) - { - nice(19); - - std::vector _addresses; - std::vector _amounts; - - for (int i = 0; i < size; i++) { - _addresses.push_back(std::string(*addresses)); - _amounts.push_back(Monero::Wallet::amountFromString(std::string(*amounts))); - addresses++; - amounts++; - } - - std::set _preferred_inputs; - - for (int i = 0; i < preferred_inputs_size; i++) { - _preferred_inputs.insert(std::string(*preferred_inputs)); - preferred_inputs++; - } - - auto priority = static_cast(priority_raw); - std::string _payment_id; - Monero::PendingTransaction *transaction; - - if (payment_id != nullptr) - { - _payment_id = std::string(payment_id); - } - - transaction = m_wallet->createTransactionMultDest(_addresses, _payment_id, _amounts, m_wallet->defaultMixin(), priority, subaddr_account); - - int status = transaction->status(); - - if (status == Monero::PendingTransaction::Status::Status_Error || status == Monero::PendingTransaction::Status::Status_Critical) - { - error = Utf8Box(strdup(transaction->errorString().c_str())); - return false; - } - - if (m_listener != nullptr) { - m_listener->m_new_transaction = true; - } - - pendingTransaction = PendingTransactionRaw(transaction); - return true; - } - - bool transaction_commit(PendingTransactionRaw *transaction, Utf8Box &error) - { - bool committed = transaction->transaction->commit(); - - if (!committed) - { - error = Utf8Box(strdup(transaction->transaction->errorString().c_str())); - } else if (m_listener != nullptr) { - m_listener->m_new_transaction = true; - } - - return committed; - } - - uint64_t get_node_height_or_update(uint64_t base_eight) - { - if (m_cached_syncing_blockchain_height < base_eight) { - m_cached_syncing_blockchain_height = base_eight; - } - - return m_cached_syncing_blockchain_height; - } - - uint64_t get_syncing_height() - { - if (m_listener == nullptr) { - return 0; - } - - uint64_t height = m_listener->height(); - - if (height <= 1) { - return 0; - } - - if (height != m_last_known_wallet_height) - { - m_last_known_wallet_height = height; - } - - return height; - } - - uint64_t is_needed_to_refresh() - { - if (m_listener == nullptr) { - return false; - } - - bool should_refresh = m_listener->isNeedToRefresh(); - - if (should_refresh) { - m_listener->resetNeedToRefresh(); - } - - return should_refresh; - } - - uint8_t is_new_transaction_exist() - { - if (m_listener == nullptr) { - return false; - } - - bool is_new_transaction_exist = m_listener->isNewTransactionExist(); - - if (is_new_transaction_exist) - { - m_listener->resetIsNewTransactionExist(); - } - - return is_new_transaction_exist; - } - - void set_listener() - { - m_last_known_wallet_height = 0; - - if (m_listener != nullptr) - { - free(m_listener); - } - - m_listener = new MoneroWalletListener(); - get_current_wallet()->setListener(m_listener); - } - - int64_t *subaddrress_get_all() - { - std::vector _subaddresses = m_subaddress->getAll(); - size_t size = _subaddresses.size(); - int64_t *subaddresses = (int64_t *)malloc(size * sizeof(int64_t)); - - for (int i = 0; i < size; i++) - { - Monero::SubaddressRow *row = _subaddresses[i]; - SubaddressRow *_row = new SubaddressRow(row->getRowId(), strdup(row->getAddress().c_str()), strdup(row->getLabel().c_str())); - subaddresses[i] = reinterpret_cast(_row); - } - - return subaddresses; - } - - int32_t subaddrress_size() - { - std::vector _subaddresses = m_subaddress->getAll(); - return _subaddresses.size(); - } - - void subaddress_add_row(uint32_t accountIndex, char *label) - { - m_subaddress->addRow(accountIndex, std::string(label)); - } - - void subaddress_set_label(uint32_t accountIndex, uint32_t addressIndex, char *label) - { - m_subaddress->setLabel(accountIndex, addressIndex, std::string(label)); - } - - void subaddress_refresh(uint32_t accountIndex) - { - m_subaddress->refresh(accountIndex); - } - - int32_t account_size() - { - std::vector _accocunts = m_account->getAll(); - return _accocunts.size(); - } - - int64_t *account_get_all() - { - std::vector _accocunts = m_account->getAll(); - size_t size = _accocunts.size(); - int64_t *accocunts = (int64_t *)malloc(size * sizeof(int64_t)); - - for (int i = 0; i < size; i++) - { - Monero::SubaddressAccountRow *row = _accocunts[i]; - AccountRow *_row = new AccountRow(row->getRowId(), strdup(row->getLabel().c_str())); - accocunts[i] = reinterpret_cast(_row); - } - - return accocunts; - } - - void account_add_row(char *label) - { - m_account->addRow(std::string(label)); - } - - void account_set_label_row(uint32_t account_index, char *label) - { - m_account->setLabel(account_index, label); - } - - void account_refresh() - { - m_account->refresh(); - } - - int64_t *transactions_get_all() - { - std::vector transactions = m_transaction_history->getAll(); - size_t size = transactions.size(); - int64_t *transactionAddresses = (int64_t *)malloc(size * sizeof(int64_t)); - - for (int i = 0; i < size; i++) - { - Monero::TransactionInfo *row = transactions[i]; - TransactionInfoRow *tx = new TransactionInfoRow(row); - transactionAddresses[i] = reinterpret_cast(tx); - } - - return transactionAddresses; - } - - void transactions_refresh() - { - m_transaction_history->refresh(); - } - - int64_t transactions_count() - { - return m_transaction_history->count(); - } - - TransactionInfoRow* get_transaction(char * txId) - { - Monero::TransactionInfo *row = m_transaction_history->transaction(std::string(txId)); - return new TransactionInfoRow(row); - } - - int LedgerExchange( - unsigned char *command, - unsigned int cmd_len, - unsigned char *response, - unsigned int max_resp_len) - { - return -1; - } - - int LedgerFind(char *buffer, size_t len) - { - return -1; - } - - void on_startup() - { - Monero::Utils::onStartup(); - Monero::WalletManagerFactory::setLogLevel(0); - } - - void rescan_blockchain() - { - m_wallet->rescanBlockchainAsync(); - } - - char * get_tx_key(char * txId) - { - return strdup(m_wallet->getTxKey(std::string(txId)).c_str()); - } - - char *get_subaddress_label(uint32_t accountIndex, uint32_t addressIndex) - { - return strdup(get_current_wallet()->getSubaddressLabel(accountIndex, addressIndex).c_str()); - } - - void set_trusted_daemon(bool arg) - { - m_wallet->setTrustedDaemon(arg); - } - - bool trusted_daemon() - { - return m_wallet->trustedDaemon(); - } - - CoinsInfoRow* coin(int index) - { - if (index >= 0 && index < m_coins_info.size()) { - std::list::iterator it = m_coins_info.begin(); - std::advance(it, index); - Monero::CoinsInfo* element = *it; - std::cout << "Element at index " << index << ": " << element << std::endl; - return new CoinsInfoRow(element); - } else { - std::cout << "Invalid index." << std::endl; - return nullptr; // Return a default value (nullptr) for invalid index - } - } - - void refresh_coins(uint32_t accountIndex) - { - m_coins_info.clear(); - - m_coins->refresh(); - for (const auto i : m_coins->getAll()) { - if (i->subaddrAccount() == accountIndex && !(i->spent())) { - m_coins_info.push_back(i); - } - } - } - - uint64_t coins_count() - { - return m_coins_info.size(); - } - - CoinsInfoRow** coins_from_account(uint32_t accountIndex) - { - std::vector matchingCoins; - - for (int i = 0; i < coins_count(); i++) { - CoinsInfoRow* coinInfo = coin(i); - if (coinInfo->subaddrAccount == accountIndex) { - matchingCoins.push_back(coinInfo); - } - } - - CoinsInfoRow** result = new CoinsInfoRow*[matchingCoins.size()]; - std::copy(matchingCoins.begin(), matchingCoins.end(), result); - return result; - } - - CoinsInfoRow** coins_from_txid(const char* txid, size_t* count) - { - std::vector matchingCoins; - - for (int i = 0; i < coins_count(); i++) { - CoinsInfoRow* coinInfo = coin(i); - if (std::string(coinInfo->hash) == txid) { - matchingCoins.push_back(coinInfo); - } - } - - *count = matchingCoins.size(); - CoinsInfoRow** result = new CoinsInfoRow*[*count]; - std::copy(matchingCoins.begin(), matchingCoins.end(), result); - return result; - } - - CoinsInfoRow** coins_from_key_image(const char** keyimages, size_t keyimageCount, size_t* count) - { - std::vector matchingCoins; - - for (int i = 0; i < coins_count(); i++) { - CoinsInfoRow* coinsInfoRow = coin(i); - for (size_t j = 0; j < keyimageCount; j++) { - if (coinsInfoRow->keyImageKnown && std::string(coinsInfoRow->keyImage) == keyimages[j]) { - matchingCoins.push_back(coinsInfoRow); - break; - } - } - } - - *count = matchingCoins.size(); - CoinsInfoRow** result = new CoinsInfoRow*[*count]; - std::copy(matchingCoins.begin(), matchingCoins.end(), result); - return result; - } - - void freeze_coin(int index) - { - m_coins->setFrozen(index); - } - - void thaw_coin(int index) - { - m_coins->thaw(index); - } - - // Sign Messages // - - char *sign_message(char *message, char *address = "") - { - return strdup(get_current_wallet()->signMessage(std::string(message), std::string(address)).c_str()); - } - -#ifdef __cplusplus -} -#endif diff --git a/cw_monero/macos/Classes/monero_api.h b/cw_monero/macos/Classes/monero_api.h deleted file mode 100644 index fa92a038d..000000000 --- a/cw_monero/macos/Classes/monero_api.h +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include -#include -#include "CwWalletListener.h" - -#ifdef __cplusplus -extern "C" { -#endif - -bool create_wallet(char *path, char *password, char *language, int32_t networkType, char *error); -bool restore_wallet_from_seed(char *path, char *password, char *seed, int32_t networkType, uint64_t restoreHeight, char *error); -bool restore_wallet_from_keys(char *path, char *password, char *language, char *address, char *viewKey, char *spendKey, int32_t networkType, uint64_t restoreHeight, char *error); -void load_wallet(char *path, char *password, int32_t nettype); -bool is_wallet_exist(char *path); - -char *get_filename(); -const char *seed(); -char *get_address(uint32_t account_index, uint32_t address_index); -uint64_t get_full_balance(uint32_t account_index); -uint64_t get_unlocked_balance(uint32_t account_index); -uint64_t get_current_height(); -uint64_t get_node_height(); - -bool is_connected(); - -bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *error); -bool connect_to_node(char *error); -void start_refresh(); -void set_refresh_from_block_height(uint64_t height); -void set_recovering_from_seed(bool is_recovery); -void store(char *path); - -void set_trusted_daemon(bool arg); -bool trusted_daemon(); -char *sign_message(char *message, char *address); - -#ifdef __cplusplus -} -#endif diff --git a/cw_monero/macos/cw_monero_base.podspec b/cw_monero/macos/cw_monero_base.podspec deleted file mode 100644 index aac972c0f..000000000 --- a/cw_monero/macos/cw_monero_base.podspec +++ /dev/null @@ -1,56 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint cw_monero.podspec` to validate before publishing. -# -Pod::Spec.new do |s| - s.name = 'cw_monero' - s.version = '0.0.1' - s.summary = 'CW Monero' - s.description = 'Cake Wallet wrapper over Monero project.' - s.homepage = 'http://cakewallet.com' - s.license = { :file => '../LICENSE' } - s.author = { 'CakeWallet' => 'support@cakewallet.com' } - s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - s.dependency 'FlutterMacOS' - s.public_header_files = 'Classes/**/*.h, Classes/*.h, External/macos/libs/monero/include/External/ios/**/*.h' - s.platform = :osx, '10.11' - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS' => '#___VALID_ARCHS___#', 'ENABLE_BITCODE' => 'NO' } - s.swift_version = '5.0' - s.libraries = 'iconv' - - s.subspec 'OpenSSL' do |openssl| - openssl.preserve_paths = '../../../../../cw_shared_external/ios/External/macos/include/**/*.h' - openssl.vendored_libraries = '../../../../../cw_shared_external/ios/External/macos/lib/libcrypto.a', '../../../../../cw_shared_external/ios/External/ios/lib/libssl.a' - openssl.libraries = 'ssl', 'crypto' - openssl.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/macos/include/**" } - end - - s.subspec 'Sodium' do |sodium| - sodium.preserve_paths = '../../../../../cw_shared_external/ios/External/macos/include/**/*.h' - sodium.vendored_libraries = '../../../../../cw_shared_external/ios/External/macos/lib/libsodium.a' - sodium.libraries = 'sodium' - sodium.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/macos/include/**" } - end - - s.subspec 'Unbound' do |unbound| - unbound.preserve_paths = '../../../../../cw_shared_external/ios/External/macos/include/**/*.h' - unbound.vendored_libraries = '../../../../../cw_shared_external/ios/External/macos/lib/libunbound.a' - unbound.libraries = 'unbound' - unbound.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/macos/include/**" } - end - - s.subspec 'Boost' do |boost| - boost.preserve_paths = '../../../../../cw_shared_external/ios/External/macos/include/**/*.h', - boost.vendored_libraries = '../../../../../cw_shared_external/ios/External/macos/lib/libboost.a', - boost.libraries = 'boost' - boost.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/macos/include/**" } - end - - s.subspec 'Monero' do |monero| - monero.preserve_paths = 'External/macos/include/**/*.h' - monero.vendored_libraries = 'External/macos/lib/libmonero.a' - monero.libraries = 'monero' - monero.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/macos/include" } - end -end diff --git a/cw_monero/pubspec.lock b/cw_monero/pubspec.lock index adb50bd02..f5dc3de3f 100644 --- a/cw_monero/pubspec.lock +++ b/cw_monero/pubspec.lock @@ -204,10 +204,10 @@ packages: dependency: "direct main" description: name: ffi - sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" file: dependency: transitive description: @@ -434,6 +434,23 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + 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: "8827da25de792088eb33e572115a5eb0d61d61a3c01acbc8bcbe76ed78f1a1f2" + url: "https://pub.dev" + source: hosted + version: "3.1.0" package_config: dependency: transitive description: @@ -526,10 +543,10 @@ packages: dependency: "direct main" description: name: polyseed - sha256: "9b48ec535b10863f78f6354ec983b4cc0c88ca69ff48fee469d0fd1954b01d4f" + sha256: a340962242d7917b0f3e6bd02c4acc3f90eae8ff766f1244f793ae7a6414dd68 url: "https://pub.dev" source: hosted - version: "0.0.2" + version: "0.0.4" pool: dependency: transitive description: diff --git a/cw_monero/pubspec.yaml b/cw_monero/pubspec.yaml index 56f5d2fa6..53e50877f 100644 --- a/cw_monero/pubspec.yaml +++ b/cw_monero/pubspec.yaml @@ -6,7 +6,7 @@ author: Cake Wallet homepage: https://cakewallet.com environment: - sdk: ">=2.17.5 <3.0.0" + sdk: ">=2.19.0 <3.0.0" flutter: ">=1.20.0" dependencies: @@ -22,6 +22,11 @@ dependencies: polyseed: ^0.0.5 cw_core: path: ../cw_core + monero: + git: + url: https://github.com/mrcyjanek/monero.dart + ref: d46753eca865e9e56c2f0ef6fe485c42e11982c5 + mutex: ^3.1.0 dev_dependencies: flutter_test: @@ -43,16 +48,7 @@ flutter: # The androidPackage and pluginClass identifiers should not ordinarily # be modified. They are used by the tooling to maintain consistency when # adding or updating assets for this project. - plugin: - platforms: - android: - package: com.cakewallet.monero - pluginClass: CwMoneroPlugin - ios: - pluginClass: CwMoneroPlugin - macos: - pluginClass: CwMoneroPlugin # To add assets to your plugin package, add an assets section, like this: # assets: diff --git a/cw_monero/test/cw_monero_method_channel_test.dart b/cw_monero/test/cw_monero_method_channel_test.dart deleted file mode 100644 index 8c1f329f0..000000000 --- a/cw_monero/test/cw_monero_method_channel_test.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:cw_monero/cw_monero_method_channel.dart'; - -void main() { - MethodChannelCwMonero platform = MethodChannelCwMonero(); - const MethodChannel channel = MethodChannel('cw_monero'); - - TestWidgetsFlutterBinding.ensureInitialized(); - - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return '42'; - }); - }); - - tearDown(() { - channel.setMockMethodCallHandler(null); - }); - - test('getPlatformVersion', () async { - expect(await platform.getPlatformVersion(), '42'); - }); -} diff --git a/cw_monero/test/cw_monero_test.dart b/cw_monero/test/cw_monero_test.dart deleted file mode 100644 index 1eb8d6f79..000000000 --- a/cw_monero/test/cw_monero_test.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:cw_monero/cw_monero.dart'; -import 'package:cw_monero/cw_monero_platform_interface.dart'; -import 'package:cw_monero/cw_monero_method_channel.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -class MockCwMoneroPlatform - with MockPlatformInterfaceMixin - implements CwMoneroPlatform { - - @override - Future getPlatformVersion() => Future.value('42'); -} - -void main() { - final CwMoneroPlatform initialPlatform = CwMoneroPlatform.instance; - - test('$MethodChannelCwMonero is the default instance', () { - expect(initialPlatform, isInstanceOf()); - }); - - test('getPlatformVersion', () async { - CwMonero cwMoneroPlugin = CwMonero(); - MockCwMoneroPlatform fakePlatform = MockCwMoneroPlatform(); - CwMoneroPlatform.instance = fakePlatform; - - expect(await cwMoneroPlugin.getPlatformVersion(), '42'); - }); -} diff --git a/cw_wownero/.gitignore b/cw_wownero/.gitignore new file mode 100644 index 000000000..03f8ac268 --- /dev/null +++ b/cw_wownero/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +.dart_tool/ + +.packages +.pub/ diff --git a/cw_wownero/.metadata b/cw_wownero/.metadata new file mode 100644 index 000000000..46a2f7f6f --- /dev/null +++ b/cw_wownero/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + channel: stable + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + - platform: macos + create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/cw_wownero/CHANGELOG.md b/cw_wownero/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/cw_wownero/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/cw_wownero/LICENSE b/cw_wownero/LICENSE new file mode 100644 index 000000000..6a825df76 --- /dev/null +++ b/cw_wownero/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Cake Technologies LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/cw_wownero/README.md b/cw_wownero/README.md new file mode 100644 index 000000000..191316437 --- /dev/null +++ b/cw_wownero/README.md @@ -0,0 +1,5 @@ +# cw_wownero + +This project is part of Cake Wallet app. + +Copyright (c) 2020 Cake Technologies LLC. \ No newline at end of file diff --git a/cw_wownero/analysis_options.yaml b/cw_wownero/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/cw_wownero/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/cw_wownero/lib/api/account_list.dart b/cw_wownero/lib/api/account_list.dart new file mode 100644 index 000000000..a73e4dcd2 --- /dev/null +++ b/cw_wownero/lib/api/account_list.dart @@ -0,0 +1,72 @@ +import 'package:cw_wownero/api/wallet.dart'; +import 'package:monero/wownero.dart' as wownero; + +wownero.wallet? wptr = null; + +int _wlptrForW = 0; +wownero.WalletListener? _wlptr = null; + +wownero.WalletListener getWlptr() { + if (wptr!.address == _wlptrForW) return _wlptr!; + _wlptrForW = wptr!.address; + _wlptr = wownero.WOWNERO_cw_getWalletListener(wptr!); + return _wlptr!; +} + + +wownero.SubaddressAccount? subaddressAccount; + +bool isUpdating = false; + +void refreshAccounts() { + try { + isUpdating = true; + subaddressAccount = wownero.Wallet_subaddressAccount(wptr!); + wownero.SubaddressAccount_refresh(subaddressAccount!); + isUpdating = false; + } catch (e) { + isUpdating = false; + rethrow; + } +} + +List getAllAccount() { + // final size = wownero.Wallet_numSubaddressAccounts(wptr!); + refreshAccounts(); + int size = wownero.SubaddressAccount_getAll_size(subaddressAccount!); + if (size == 0) { + wownero.Wallet_addSubaddressAccount(wptr!); + return getAllAccount(); + } + return List.generate(size, (index) { + return wownero.SubaddressAccount_getAll_byIndex(subaddressAccount!, index: index); + }); +} + +void addAccountSync({required String label}) { + wownero.Wallet_addSubaddressAccount(wptr!, label: label); +} + +void setLabelForAccountSync({required int accountIndex, required String label}) { + // TODO(mrcyjanek): this may be wrong function? + wownero.Wallet_setSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: 0, label: label); +} + +void _addAccount(String label) => addAccountSync(label: label); + +void _setLabelForAccount(Map args) { + final label = args['label'] as String; + final accountIndex = args['accountIndex'] as int; + + setLabelForAccountSync(label: label, accountIndex: accountIndex); +} + +Future addAccount({required String label}) async { + _addAccount(label); + await store(); +} + +Future setLabelForAccount({required int accountIndex, required String label}) async { + _setLabelForAccount({'accountIndex': accountIndex, 'label': label}); + await store(); +} \ No newline at end of file diff --git a/cw_wownero/lib/api/coins_info.dart b/cw_wownero/lib/api/coins_info.dart new file mode 100644 index 000000000..2fd6c097d --- /dev/null +++ b/cw_wownero/lib/api/coins_info.dart @@ -0,0 +1,17 @@ +import 'package:cw_wownero/api/account_list.dart'; +import 'package:monero/wownero.dart' as wownero; + +wownero.Coins? coins = null; + +void refreshCoins(int accountIndex) { + coins = wownero.Wallet_coins(wptr!); + wownero.Coins_refresh(coins!); +} + +int countOfCoins() => wownero.Coins_count(coins!); + +wownero.CoinsInfo getCoin(int index) => wownero.Coins_coin(coins!, index); + +void freezeCoin(int index) => wownero.Coins_setFrozen(coins!, index: index); + +void thawCoin(int index) => wownero.Coins_thaw(coins!, index: index); diff --git a/cw_wownero/lib/api/exceptions/connection_to_node_exception.dart b/cw_wownero/lib/api/exceptions/connection_to_node_exception.dart new file mode 100644 index 000000000..483b0a174 --- /dev/null +++ b/cw_wownero/lib/api/exceptions/connection_to_node_exception.dart @@ -0,0 +1,5 @@ +class ConnectionToNodeException implements Exception { + ConnectionToNodeException({required this.message}); + + final String message; +} \ No newline at end of file diff --git a/cw_wownero/lib/api/exceptions/creation_transaction_exception.dart b/cw_wownero/lib/api/exceptions/creation_transaction_exception.dart new file mode 100644 index 000000000..7b55ec074 --- /dev/null +++ b/cw_wownero/lib/api/exceptions/creation_transaction_exception.dart @@ -0,0 +1,8 @@ +class CreationTransactionException implements Exception { + CreationTransactionException({required this.message}); + + final String message; + + @override + String toString() => message; +} \ No newline at end of file diff --git a/cw_wownero/lib/api/exceptions/setup_wallet_exception.dart b/cw_wownero/lib/api/exceptions/setup_wallet_exception.dart new file mode 100644 index 000000000..b6e0c1f18 --- /dev/null +++ b/cw_wownero/lib/api/exceptions/setup_wallet_exception.dart @@ -0,0 +1,5 @@ +class SetupWalletException implements Exception { + SetupWalletException({required this.message}); + + final String message; +} \ No newline at end of file diff --git a/cw_wownero/lib/api/exceptions/wallet_creation_exception.dart b/cw_wownero/lib/api/exceptions/wallet_creation_exception.dart new file mode 100644 index 000000000..6052366b9 --- /dev/null +++ b/cw_wownero/lib/api/exceptions/wallet_creation_exception.dart @@ -0,0 +1,8 @@ +class WalletCreationException implements Exception { + WalletCreationException({required this.message}); + + final String message; + + @override + String toString() => message; +} \ No newline at end of file diff --git a/cw_wownero/lib/api/exceptions/wallet_opening_exception.dart b/cw_wownero/lib/api/exceptions/wallet_opening_exception.dart new file mode 100644 index 000000000..df7a850a4 --- /dev/null +++ b/cw_wownero/lib/api/exceptions/wallet_opening_exception.dart @@ -0,0 +1,8 @@ +class WalletOpeningException implements Exception { + WalletOpeningException({required this.message}); + + final String message; + + @override + String toString() => message; +} \ No newline at end of file diff --git a/cw_wownero/lib/api/exceptions/wallet_restore_from_keys_exception.dart b/cw_wownero/lib/api/exceptions/wallet_restore_from_keys_exception.dart new file mode 100644 index 000000000..c6b6c6ef7 --- /dev/null +++ b/cw_wownero/lib/api/exceptions/wallet_restore_from_keys_exception.dart @@ -0,0 +1,5 @@ +class WalletRestoreFromKeysException implements Exception { + WalletRestoreFromKeysException({required this.message}); + + final String message; +} \ No newline at end of file diff --git a/cw_wownero/lib/api/exceptions/wallet_restore_from_seed_exception.dart b/cw_wownero/lib/api/exceptions/wallet_restore_from_seed_exception.dart new file mode 100644 index 000000000..004cd7958 --- /dev/null +++ b/cw_wownero/lib/api/exceptions/wallet_restore_from_seed_exception.dart @@ -0,0 +1,5 @@ +class WalletRestoreFromSeedException implements Exception { + WalletRestoreFromSeedException({required this.message}); + + final String message; +} \ No newline at end of file diff --git a/cw_wownero/lib/api/structs/account_row.dart b/cw_wownero/lib/api/structs/account_row.dart new file mode 100644 index 000000000..aa492ee0f --- /dev/null +++ b/cw_wownero/lib/api/structs/account_row.dart @@ -0,0 +1,12 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +class AccountRow extends Struct { + @Int64() + external int id; + + external Pointer label; + + String getLabel() => label.toDartString(); + int getId() => id; +} diff --git a/cw_wownero/lib/api/structs/coins_info_row.dart b/cw_wownero/lib/api/structs/coins_info_row.dart new file mode 100644 index 000000000..ff6f6ce73 --- /dev/null +++ b/cw_wownero/lib/api/structs/coins_info_row.dart @@ -0,0 +1,73 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +class CoinsInfoRow extends Struct { + @Int64() + external int blockHeight; + + external Pointer hash; + + @Uint64() + external int internalOutputIndex; + + @Uint64() + external int globalOutputIndex; + + @Int8() + external int spent; + + @Int8() + external int frozen; + + @Uint64() + external int spentHeight; + + @Uint64() + external int amount; + + @Int8() + external int rct; + + @Int8() + external int keyImageKnown; + + @Uint64() + external int pkIndex; + + @Uint32() + external int subaddrIndex; + + @Uint32() + external int subaddrAccount; + + external Pointer address; + + external Pointer addressLabel; + + external Pointer keyImage; + + @Uint64() + external int unlockTime; + + @Int8() + external int unlocked; + + external Pointer pubKey; + + @Int8() + external int coinbase; + + external Pointer description; + + String getHash() => hash.toDartString(); + + String getAddress() => address.toDartString(); + + String getAddressLabel() => addressLabel.toDartString(); + + String getKeyImage() => keyImage.toDartString(); + + String getPubKey() => pubKey.toDartString(); + + String getDescription() => description.toDartString(); +} diff --git a/cw_wownero/lib/api/structs/pending_transaction.dart b/cw_wownero/lib/api/structs/pending_transaction.dart new file mode 100644 index 000000000..dc5fbddd0 --- /dev/null +++ b/cw_wownero/lib/api/structs/pending_transaction.dart @@ -0,0 +1,17 @@ + +class PendingTransactionDescription { + PendingTransactionDescription({ + required this.amount, + required this.fee, + required this.hash, + required this.hex, + required this.txKey, + required this.pointerAddress}); + + final int amount; + final int fee; + final String hash; + final String hex; + final String txKey; + final int pointerAddress; +} \ No newline at end of file diff --git a/cw_wownero/lib/api/structs/subaddress_row.dart b/cw_wownero/lib/api/structs/subaddress_row.dart new file mode 100644 index 000000000..d593a793d --- /dev/null +++ b/cw_wownero/lib/api/structs/subaddress_row.dart @@ -0,0 +1,15 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +class SubaddressRow extends Struct { + @Int64() + external int id; + + external Pointer address; + + external Pointer label; + + String getLabel() => label.toDartString(); + String getAddress() => address.toDartString(); + int getId() => id; +} \ No newline at end of file diff --git a/cw_wownero/lib/api/structs/transaction_info_row.dart b/cw_wownero/lib/api/structs/transaction_info_row.dart new file mode 100644 index 000000000..bdcc64d3f --- /dev/null +++ b/cw_wownero/lib/api/structs/transaction_info_row.dart @@ -0,0 +1,41 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +class TransactionInfoRow extends Struct { + @Uint64() + external int amount; + + @Uint64() + external int fee; + + @Uint64() + external int blockHeight; + + @Uint64() + external int confirmations; + + @Uint32() + external int subaddrAccount; + + @Int8() + external int direction; + + @Int8() + external int isPending; + + @Uint32() + external int subaddrIndex; + + external Pointer hash; + + external Pointer paymentId; + + @Int64() + external int datetime; + + int getDatetime() => datetime; + int getAmount() => amount >= 0 ? amount : amount * -1; + bool getIsPending() => isPending != 0; + String getHash() => hash.toDartString(); + String getPaymentId() => paymentId.toDartString(); +} diff --git a/cw_wownero/lib/api/structs/ut8_box.dart b/cw_wownero/lib/api/structs/ut8_box.dart new file mode 100644 index 000000000..53e678c88 --- /dev/null +++ b/cw_wownero/lib/api/structs/ut8_box.dart @@ -0,0 +1,8 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +class Utf8Box extends Struct { + external Pointer value; + + String getValue() => value.toDartString(); +} diff --git a/cw_wownero/lib/api/subaddress_list.dart b/cw_wownero/lib/api/subaddress_list.dart new file mode 100644 index 000000000..cec7d94cb --- /dev/null +++ b/cw_wownero/lib/api/subaddress_list.dart @@ -0,0 +1,91 @@ +import 'package:cw_wownero/api/account_list.dart'; +import 'package:cw_wownero/api/wallet.dart'; +import 'package:monero/wownero.dart' as wownero; + +bool isUpdating = false; + +class SubaddressInfoMetadata { + SubaddressInfoMetadata({ + required this.accountIndex, + }); + int accountIndex; +} + +SubaddressInfoMetadata? subaddress = null; + +void refreshSubaddresses({required int accountIndex}) { + try { + isUpdating = true; + subaddress = SubaddressInfoMetadata(accountIndex: accountIndex); + isUpdating = false; + } catch (e) { + isUpdating = false; + rethrow; + } +} + +class Subaddress { + Subaddress({ + required this.addressIndex, + required this.accountIndex, + }); + String get address => wownero.Wallet_address( + wptr!, + accountIndex: accountIndex, + addressIndex: addressIndex, + ); + final int addressIndex; + final int accountIndex; + String get label => wownero.Wallet_getSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: addressIndex); +} + +List getAllSubaddresses() { + final size = wownero.Wallet_numSubaddresses(wptr!, accountIndex: subaddress!.accountIndex); + return List.generate(size, (index) { + return Subaddress( + accountIndex: subaddress!.accountIndex, + addressIndex: index, + ); + }).reversed.toList(); +} + +void addSubaddressSync({required int accountIndex, required String label}) { + wownero.Wallet_addSubaddress(wptr!, accountIndex: accountIndex, label: label); + refreshSubaddresses(accountIndex: accountIndex); +} + +void setLabelForSubaddressSync( + {required int accountIndex, required int addressIndex, required String label}) { + wownero.Wallet_setSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: addressIndex, label: label); +} + +void _addSubaddress(Map args) { + final label = args['label'] as String; + final accountIndex = args['accountIndex'] as int; + + addSubaddressSync(accountIndex: accountIndex, label: label); +} + +void _setLabelForSubaddress(Map args) { + final label = args['label'] as String; + final accountIndex = args['accountIndex'] as int; + final addressIndex = args['addressIndex'] as int; + + setLabelForSubaddressSync( + accountIndex: accountIndex, addressIndex: addressIndex, label: label); +} + +Future addSubaddress({required int accountIndex, required String label}) async { + _addSubaddress({'accountIndex': accountIndex, 'label': label}); + await store(); +} + +Future setLabelForSubaddress( + {required int accountIndex, required int addressIndex, required String label}) async { + _setLabelForSubaddress({ + 'accountIndex': accountIndex, + 'addressIndex': addressIndex, + 'label': label + }); + await store(); +} diff --git a/cw_wownero/lib/api/transaction_history.dart b/cw_wownero/lib/api/transaction_history.dart new file mode 100644 index 000000000..42b2ef6f3 --- /dev/null +++ b/cw_wownero/lib/api/transaction_history.dart @@ -0,0 +1,324 @@ +import 'dart:ffi'; +import 'dart:isolate'; + +import 'package:cw_wownero/api/account_list.dart'; +import 'package:cw_wownero/api/exceptions/creation_transaction_exception.dart'; +import 'package:cw_wownero/api/wownero_output.dart'; +import 'package:cw_wownero/api/structs/pending_transaction.dart'; +import 'package:ffi/ffi.dart'; +import 'package:monero/wownero.dart' as wownero; +import 'package:monero/src/generated_bindings_wownero.g.dart' as wownero_gen; + + +String getTxKey(String txId) { + return wownero.Wallet_getTxKey(wptr!, txid: txId); +} + +wownero.TransactionHistory? txhistory; + +void refreshTransactions() { + txhistory = wownero.Wallet_history(wptr!); + wownero.TransactionHistory_refresh(txhistory!); +} + +int countOfTransactions() => wownero.TransactionHistory_count(txhistory!); + +List getAllTransactions() { + List dummyTxs = []; + + txhistory = wownero.Wallet_history(wptr!); + wownero.TransactionHistory_refresh(txhistory!); + int size = countOfTransactions(); + final list = List.generate(size, (index) => Transaction(txInfo: wownero.TransactionHistory_transaction(txhistory!, index: index)))..addAll(dummyTxs); + + final accts = wownero.Wallet_numSubaddressAccounts(wptr!); + for (var i = 0; i < accts; i++) { + final fullBalance = wownero.Wallet_balance(wptr!, accountIndex: i); + final availBalance = wownero.Wallet_unlockedBalance(wptr!, accountIndex: i); + if (fullBalance > availBalance) { + if (list.where((element) => element.accountIndex == i && element.isConfirmed == false).isNotEmpty) { + dummyTxs.add( + Transaction.dummy( + displayLabel: "", + description: "", + fee: 0, + confirmations: 0, + blockheight: 0, + accountIndex: i, + paymentId: "", + amount: fullBalance - availBalance, + isSpend: false, + hash: "pending", + key: "pending", + txInfo: Pointer.fromAddress(0), + )..timeStamp = DateTime.now() + ); + } + } + } + list.addAll(dummyTxs); + return list; +} + +// TODO(mrcyjanek): ... +Transaction getTransaction(String txId) { + return Transaction(txInfo: wownero.TransactionHistory_transactionById(txhistory!, txid: txId)); +} + +Future createTransactionSync( + {required String address, + required String paymentId, + required int priorityRaw, + String? amount, + int accountIndex = 0, + List preferredInputs = const []}) async { + + final amt = amount == null ? 0 : wownero.Wallet_amountFromString(amount); + + final address_ = address.toNativeUtf8(); + final paymentId_ = paymentId.toNativeUtf8(); + final preferredInputs_ = preferredInputs.join(wownero.defaultSeparatorStr).toNativeUtf8(); + + final waddr = wptr!.address; + final addraddr = address_.address; + final paymentIdAddr = paymentId_.address; + final preferredInputsAddr = preferredInputs_.address; + final spaddr = wownero.defaultSeparator.address; + final pendingTx = Pointer.fromAddress(await Isolate.run(() { + final tx = wownero_gen.WowneroC(DynamicLibrary.open(wownero.libPath)).WOWNERO_Wallet_createTransaction( + Pointer.fromAddress(waddr), + Pointer.fromAddress(addraddr).cast(), + Pointer.fromAddress(paymentIdAddr).cast(), + amt, + 1, + priorityRaw, + accountIndex, + Pointer.fromAddress(preferredInputsAddr).cast(), + Pointer.fromAddress(spaddr), + ); + return tx.address; + })); + calloc.free(address_); + calloc.free(paymentId_); + calloc.free(preferredInputs_); + final String? error = (() { + final status = wownero.PendingTransaction_status(pendingTx); + if (status == 0) { + return null; + } + return wownero.PendingTransaction_errorString(pendingTx); + })(); + + if (error != null) { + final message = error; + throw CreationTransactionException(message: message); + } + + final rAmt = wownero.PendingTransaction_amount(pendingTx); + final rFee = wownero.PendingTransaction_fee(pendingTx); + final rHash = wownero.PendingTransaction_txid(pendingTx, ''); + final rTxKey = rHash; + + return PendingTransactionDescription( + amount: rAmt, + fee: rFee, + hash: rHash, + hex: '', + txKey: rTxKey, + pointerAddress: pendingTx.address, + ); +} + +PendingTransactionDescription createTransactionMultDestSync( + {required List outputs, + required String paymentId, + required int priorityRaw, + int accountIndex = 0, + List preferredInputs = const []}) { + + final txptr = wownero.Wallet_createTransactionMultDest( + wptr!, + dstAddr: outputs.map((e) => e.address).toList(), + isSweepAll: false, + amounts: outputs.map((e) => wownero.Wallet_amountFromString(e.amount)).toList(), + mixinCount: 0, + pendingTransactionPriority: priorityRaw, + subaddr_account: accountIndex, + ); + if (wownero.PendingTransaction_status(txptr) != 0) { + throw CreationTransactionException(message: wownero.PendingTransaction_errorString(txptr)); + } + return PendingTransactionDescription( + amount: wownero.PendingTransaction_amount(txptr), + fee: wownero.PendingTransaction_fee(txptr), + hash: wownero.PendingTransaction_txid(txptr, ''), + hex: wownero.PendingTransaction_txid(txptr, ''), + txKey: wownero.PendingTransaction_txid(txptr, ''), + pointerAddress: txptr.address, + ); +} + +void commitTransactionFromPointerAddress({required int address}) => + commitTransaction(transactionPointer: wownero.PendingTransaction.fromAddress(address)); + +void commitTransaction({required wownero.PendingTransaction transactionPointer}) { + + final txCommit = wownero.PendingTransaction_commit(transactionPointer, filename: '', overwrite: false); + + final String? error = (() { + final status = wownero.PendingTransaction_status(transactionPointer.cast()); + if (status == 0) { + return null; + } + return wownero.Wallet_errorString(wptr!); + })(); + + if (error != null) { + throw CreationTransactionException(message: error); + } +} + +Future _createTransactionSync(Map args) async { + final address = args['address'] as String; + final paymentId = args['paymentId'] as String; + final amount = args['amount'] as String?; + final priorityRaw = args['priorityRaw'] as int; + final accountIndex = args['accountIndex'] as int; + final preferredInputs = args['preferredInputs'] as List; + + return createTransactionSync( + address: address, + paymentId: paymentId, + amount: amount, + priorityRaw: priorityRaw, + accountIndex: accountIndex, + preferredInputs: preferredInputs); +} + +PendingTransactionDescription _createTransactionMultDestSync(Map args) { + final outputs = args['outputs'] as List; + final paymentId = args['paymentId'] as String; + final priorityRaw = args['priorityRaw'] as int; + final accountIndex = args['accountIndex'] as int; + final preferredInputs = args['preferredInputs'] as List; + + return createTransactionMultDestSync( + outputs: outputs, + paymentId: paymentId, + priorityRaw: priorityRaw, + accountIndex: accountIndex, + preferredInputs: preferredInputs); +} + +Future createTransaction( + {required String address, + required int priorityRaw, + String? amount, + String paymentId = '', + int accountIndex = 0, + List preferredInputs = const []}) async => + _createTransactionSync({ + 'address': address, + 'paymentId': paymentId, + 'amount': amount, + 'priorityRaw': priorityRaw, + 'accountIndex': accountIndex, + 'preferredInputs': preferredInputs + }); + +Future createTransactionMultDest( + {required List outputs, + required int priorityRaw, + String paymentId = '', + int accountIndex = 0, + List preferredInputs = const []}) async => + _createTransactionMultDestSync({ + 'outputs': outputs, + 'paymentId': paymentId, + 'priorityRaw': priorityRaw, + 'accountIndex': accountIndex, + 'preferredInputs': preferredInputs + }); + + +class Transaction { + final String displayLabel; + String subaddressLabel = wownero.Wallet_getSubaddressLabel(wptr!, accountIndex: 0, addressIndex: 0); + late final String address = wownero.Wallet_address( + wptr!, + accountIndex: 0, + addressIndex: 0, + ); + final String description; + final int fee; + final int confirmations; + late final bool isPending = confirmations < 3; + final int blockheight; + final int addressIndex = 0; + final int accountIndex; + final String paymentId; + final int amount; + final bool isSpend; + late DateTime timeStamp; + late final bool isConfirmed = !isPending; + final String hash; + final String key; + + Map toJson() { + return { + "displayLabel": displayLabel, + "subaddressLabel": subaddressLabel, + "address": address, + "description": description, + "fee": fee, + "confirmations": confirmations, + "isPending": isPending, + "blockheight": blockheight, + "accountIndex": accountIndex, + "addressIndex": addressIndex, + "paymentId": paymentId, + "amount": amount, + "isSpend": isSpend, + "timeStamp": timeStamp.toIso8601String(), + "isConfirmed": isConfirmed, + "hash": hash, + }; + } + + // S finalubAddress? subAddress; + // List transfers = []; + // final int txIndex; + final wownero.TransactionInfo txInfo; + Transaction({ + required this.txInfo, + }) : displayLabel = wownero.TransactionInfo_label(txInfo), + hash = wownero.TransactionInfo_hash(txInfo), + timeStamp = DateTime.fromMillisecondsSinceEpoch( + wownero.TransactionInfo_timestamp(txInfo) * 1000, + ), + isSpend = wownero.TransactionInfo_direction(txInfo) == + wownero.TransactionInfo_Direction.Out, + amount = wownero.TransactionInfo_amount(txInfo), + paymentId = wownero.TransactionInfo_paymentId(txInfo), + accountIndex = wownero.TransactionInfo_subaddrAccount(txInfo), + blockheight = wownero.TransactionInfo_blockHeight(txInfo), + confirmations = wownero.TransactionInfo_confirmations(txInfo), + fee = wownero.TransactionInfo_fee(txInfo), + description = wownero.TransactionInfo_description(txInfo), + key = wownero.Wallet_getTxKey(wptr!, txid: wownero.TransactionInfo_hash(txInfo)); + + Transaction.dummy({ + required this.displayLabel, + required this.description, + required this.fee, + required this.confirmations, + required this.blockheight, + required this.accountIndex, + required this.paymentId, + required this.amount, + required this.isSpend, + required this.hash, + required this.key, + required this.txInfo + }); +} \ No newline at end of file diff --git a/cw_wownero/lib/api/wallet.dart b/cw_wownero/lib/api/wallet.dart new file mode 100644 index 000000000..6a7550d84 --- /dev/null +++ b/cw_wownero/lib/api/wallet.dart @@ -0,0 +1,295 @@ +import 'dart:async'; +import 'dart:ffi'; +import 'dart:isolate'; + +import 'package:cw_wownero/api/account_list.dart'; +import 'package:cw_wownero/api/exceptions/setup_wallet_exception.dart'; +import 'package:monero/wownero.dart' as wownero; +import 'package:mutex/mutex.dart'; + +int getSyncingHeight() { + // final height = wownero.WOWNERO_cw_WalletListener_height(getWlptr()); + final h2 = wownero.Wallet_blockChainHeight(wptr!); + // print("height: $height / $h2"); + return h2; +} + +bool isNeededToRefresh() { + // final ret = wownero.WOWNERO_cw_WalletListener_isNeedToRefresh(getWlptr()); + // wownero.WOWNERO_cw_WalletListener_resetNeedToRefresh(getWlptr()); + return true; +} + +bool isNewTransactionExist() { + // final ret = + // wownero.WOWNERO_cw_WalletListener_isNewTransactionExist(getWlptr()); + // wownero.WOWNERO_cw_WalletListener_resetIsNewTransactionExist(getWlptr()); + // NOTE: I don't know why wownero is being funky, but + return true; +} + +String getFilename() => wownero.Wallet_filename(wptr!); + +String getSeed() { + // wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed); + final cakepolyseed = + wownero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.seed"); + if (cakepolyseed != "") { + return cakepolyseed; + } + final polyseed = wownero.Wallet_getPolyseed(wptr!, passphrase: ''); + if (polyseed != "") { + return polyseed; + } + final legacy = wownero.Wallet_seed(wptr!, seedOffset: ''); + return legacy; +} + +String getSeedLegacy(String? language) { + var legacy = wownero.Wallet_seed(wptr!, seedOffset: ''); + if (wownero.Wallet_status(wptr!) != 0) { + wownero.Wallet_setSeedLanguage(wptr!, language: language ?? "English"); + legacy = wownero.Wallet_seed(wptr!, seedOffset: ''); + } + return legacy; +} + +String getAddress({int accountIndex = 0, int addressIndex = 1}) => + wownero.Wallet_address(wptr!, + accountIndex: accountIndex, addressIndex: addressIndex); + +int getFullBalance({int accountIndex = 0}) => + wownero.Wallet_balance(wptr!, accountIndex: accountIndex); + +int getUnlockedBalance({int accountIndex = 0}) => + wownero.Wallet_unlockedBalance(wptr!, accountIndex: accountIndex); + +int getCurrentHeight() => wownero.Wallet_blockChainHeight(wptr!); + +int getNodeHeightSync() => wownero.Wallet_daemonBlockChainHeight(wptr!); + +bool isConnectedSync() => wownero.Wallet_connected(wptr!) != 0; + +Future setupNodeSync( + {required String address, + String? login, + String? password, + bool useSSL = false, + bool isLightWallet = false, + String? socksProxyAddress}) async { + print(''' +{ + wptr!, + daemonAddress: $address, + useSsl: $useSSL, + proxyAddress: $socksProxyAddress ?? '', + daemonUsername: $login ?? '', + daemonPassword: $password ?? '' +} +'''); + final addr = wptr!.address; + await Isolate.run(() { + wownero.Wallet_init(Pointer.fromAddress(addr), + daemonAddress: address, + useSsl: useSSL, + proxyAddress: socksProxyAddress ?? '', + daemonUsername: login ?? '', + daemonPassword: password ?? ''); + }); + // wownero.Wallet_init3(wptr!, argv0: '', defaultLogBaseName: 'wowneroc', console: true); + + final status = wownero.Wallet_status(wptr!); + + if (status != 0) { + final error = wownero.Wallet_errorString(wptr!); + print("error: $error"); + throw SetupWalletException(message: error); + } + + return status == 0; +} + +void startRefreshSync() { + wownero.Wallet_refreshAsync(wptr!); + wownero.Wallet_startRefresh(wptr!); +} + +Future connectToNode() async { + return true; +} + +void setRefreshFromBlockHeight({required int height}) => + wownero.Wallet_setRefreshFromBlockHeight(wptr!, + refresh_from_block_height: height); + +void setRecoveringFromSeed({required bool isRecovery}) => + wownero.Wallet_setRecoveringFromSeed(wptr!, recoveringFromSeed: isRecovery); + +final storeMutex = Mutex(); +void storeSync() async { + await storeMutex.acquire(); + final addr = wptr!.address; + Isolate.run(() { + wownero.Wallet_store(Pointer.fromAddress(addr)); + }); + storeMutex.release(); +} + +void setPasswordSync(String password) { + wownero.Wallet_setPassword(wptr!, password: password); + + final status = wownero.Wallet_status(wptr!); + if (status != 0) { + throw Exception(wownero.Wallet_errorString(wptr!)); + } +} + +void closeCurrentWallet() { + wownero.Wallet_stop(wptr!); +} + +String getSecretViewKey() => wownero.Wallet_secretViewKey(wptr!); + +String getPublicViewKey() => wownero.Wallet_publicViewKey(wptr!); + +String getSecretSpendKey() => wownero.Wallet_secretSpendKey(wptr!); + +String getPublicSpendKey() => wownero.Wallet_publicSpendKey(wptr!); + +class SyncListener { + SyncListener(this.onNewBlock, this.onNewTransaction) + : _cachedBlockchainHeight = 0, + _lastKnownBlockHeight = 0, + _initialSyncHeight = 0; + + void Function(int, int, double) onNewBlock; + void Function() onNewTransaction; + + Timer? _updateSyncInfoTimer; + int _cachedBlockchainHeight; + int _lastKnownBlockHeight; + int _initialSyncHeight; + + Future getNodeHeightOrUpdate(int baseHeight) async { + if (_cachedBlockchainHeight < baseHeight || _cachedBlockchainHeight == 0) { + _cachedBlockchainHeight = await getNodeHeight(); + } + + return _cachedBlockchainHeight; + } + + void start() { + _cachedBlockchainHeight = 0; + _lastKnownBlockHeight = 0; + _initialSyncHeight = 0; + _updateSyncInfoTimer ??= + Timer.periodic(Duration(milliseconds: 1200), (_) async { + if (isNewTransactionExist()) { + onNewTransaction(); + } + + var syncHeight = getSyncingHeight(); + + if (syncHeight <= 0) { + syncHeight = getCurrentHeight(); + } + + if (_initialSyncHeight <= 0) { + _initialSyncHeight = syncHeight; + } + + final bchHeight = await getNodeHeightOrUpdate(syncHeight); + + if (_lastKnownBlockHeight == syncHeight) { + return; + } + + _lastKnownBlockHeight = syncHeight; + final track = bchHeight - _initialSyncHeight; + final diff = track - (bchHeight - syncHeight); + final ptc = diff <= 0 ? 0.0 : diff / track; + final left = bchHeight - syncHeight; + + if (syncHeight < 0 || left < 0) { + return; + } + + // 1. Actual new height; 2. Blocks left to finish; 3. Progress in percents; + onNewBlock.call(syncHeight, left, ptc); + }); + } + + void stop() => _updateSyncInfoTimer?.cancel(); +} + +SyncListener setListeners(void Function(int, int, double) onNewBlock, + void Function() onNewTransaction) { + final listener = SyncListener(onNewBlock, onNewTransaction); + // setListenerNative(); + return listener; +} + +void onStartup() {} + +void _storeSync(Object _) => storeSync(); + +Future _setupNodeSync(Map args) async { + final address = args['address'] as String; + final login = (args['login'] ?? '') as String; + final password = (args['password'] ?? '') as String; + final useSSL = args['useSSL'] as bool; + final isLightWallet = args['isLightWallet'] as bool; + final socksProxyAddress = (args['socksProxyAddress'] ?? '') as String; + + return setupNodeSync( + address: address, + login: login, + password: password, + useSSL: useSSL, + isLightWallet: isLightWallet, + socksProxyAddress: socksProxyAddress); +} + +bool _isConnected(Object _) => isConnectedSync(); + +int _getNodeHeight(Object _) => getNodeHeightSync(); + +void startRefresh() => startRefreshSync(); + +Future setupNode( + {required String address, + String? login, + String? password, + bool useSSL = false, + String? socksProxyAddress, + bool isLightWallet = false}) async => + _setupNodeSync({ + 'address': address, + 'login': login, + 'password': password, + 'useSSL': useSSL, + 'isLightWallet': isLightWallet, + 'socksProxyAddress': socksProxyAddress + }); + +Future store() async => _storeSync(0); + +Future isConnected() async => _isConnected(0); + +Future getNodeHeight() async => _getNodeHeight(0); + +void rescanBlockchainAsync() => wownero.Wallet_rescanBlockchainAsync(wptr!); + +String getSubaddressLabel(int accountIndex, int addressIndex) { + return wownero.Wallet_getSubaddressLabel(wptr!, + accountIndex: accountIndex, addressIndex: addressIndex); +} + +Future setTrustedDaemon(bool trusted) async => + wownero.Wallet_setTrustedDaemon(wptr!, arg: trusted); + +Future trustedDaemon() async => wownero.Wallet_trustedDaemon(wptr!); + +String signMessage(String message, {String address = ""}) { + return wownero.Wallet_signMessage(wptr!, message: message, address: address); +} diff --git a/cw_wownero/lib/api/wallet_manager.dart b/cw_wownero/lib/api/wallet_manager.dart new file mode 100644 index 000000000..53d62f1cf --- /dev/null +++ b/cw_wownero/lib/api/wallet_manager.dart @@ -0,0 +1,359 @@ +import 'dart:ffi'; +import 'dart:isolate'; + +import 'package:cw_wownero/api/account_list.dart'; +import 'package:cw_wownero/api/exceptions/wallet_creation_exception.dart'; +import 'package:cw_wownero/api/exceptions/wallet_opening_exception.dart'; +import 'package:cw_wownero/api/exceptions/wallet_restore_from_keys_exception.dart'; +import 'package:cw_wownero/api/exceptions/wallet_restore_from_seed_exception.dart'; +import 'package:cw_wownero/api/wallet.dart'; +import 'package:monero/wownero.dart' as wownero; + +wownero.WalletManager? _wmPtr; +final wownero.WalletManager wmPtr = Pointer.fromAddress((() { + try { + // Problems with the wallet? Crashes? Lags? this will print all calls to wow + // codebase, so it will be easier to debug what happens. At least easier + // than plugging gdb in. Especially on windows/android. + wownero.printStarts = false; + _wmPtr ??= wownero.WalletManagerFactory_getWalletManager(); + print("ptr: $_wmPtr"); + } catch (e) { + print(e); + } + return _wmPtr!.address; +})()); + +void createWalletSync( + {required String path, + required String password, + required String language, + int nettype = 0}) { + wptr = wownero.WalletManager_createWallet(wmPtr, + path: path, password: password, language: language, networkType: 0); + + final status = wownero.Wallet_status(wptr!); + if (status != 0) { + throw WalletCreationException(message: wownero.Wallet_errorString(wptr!)); + } + wownero.Wallet_store(wptr!, path: path); + openedWalletsByPath[path] = wptr!; + + // is the line below needed? + // setupNodeSync(address: "node.wowneroworld.com:18089"); +} + +bool isWalletExistSync({required String path}) { + return wownero.WalletManager_walletExists(wmPtr, path); +} + +void restoreWalletFromSeedSync( + {required String path, + required String password, + required String seed, + int nettype = 0, + int restoreHeight = 0}) { + if (seed.split(" ").length == 14) { + wptr = wownero.WOWNERO_deprecated_restore14WordSeed( + path: path, + password: password, + language: seed, // I KNOW - this is supposed to be called seed + networkType: 0, + ); + + setRefreshFromBlockHeight( + height: wownero.WOWNERO_deprecated_14WordSeedHeight(seed: seed), + ); + } else { + wptr = wownero.WalletManager_recoveryWallet( + wmPtr, + path: path, + password: password, + mnemonic: seed, + restoreHeight: restoreHeight, + seedOffset: '', + networkType: 0, + ); + } + + final status = wownero.Wallet_status(wptr!); + + if (status != 0) { + final error = wownero.Wallet_errorString(wptr!); + throw WalletRestoreFromSeedException(message: error); + } + + openedWalletsByPath[path] = wptr!; +} + +void restoreWalletFromKeysSync( + {required String path, + required String password, + required String language, + required String address, + required String viewKey, + required String spendKey, + int nettype = 0, + int restoreHeight = 0}) { + wptr = wownero.WalletManager_createWalletFromKeys( + wmPtr, + path: path, + password: password, + restoreHeight: restoreHeight, + addressString: address, + viewKeyString: viewKey, + spendKeyString: spendKey, + nettype: 0, + ); + + final status = wownero.Wallet_status(wptr!); + if (status != 0) { + throw WalletRestoreFromKeysException( + message: wownero.Wallet_errorString(wptr!)); + } + + openedWalletsByPath[path] = wptr!; +} + +void restoreWalletFromSpendKeySync( + {required String path, + required String password, + required String seed, + required String language, + required String spendKey, + int nettype = 0, + int restoreHeight = 0}) { + // wptr = wownero.WalletManager_createWalletFromKeys( + // wmPtr, + // path: path, + // password: password, + // restoreHeight: restoreHeight, + // addressString: '', + // spendKeyString: spendKey, + // viewKeyString: '', + // nettype: 0, + // ); + + wptr = wownero.WalletManager_createDeterministicWalletFromSpendKey( + wmPtr, + path: path, + password: password, + language: language, + spendKeyString: spendKey, + newWallet: true, // TODO(mrcyjanek): safe to remove + restoreHeight: restoreHeight, + ); + + final status = wownero.Wallet_status(wptr!); + + if (status != 0) { + final err = wownero.Wallet_errorString(wptr!); + print("err: $err"); + throw WalletRestoreFromKeysException(message: err); + } + + wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed); + + storeSync(); + + openedWalletsByPath[path] = wptr!; +} + +String _lastOpenedWallet = ""; + +// void restoreWowneroWalletFromDevice( +// {required String path, +// required String password, +// required String deviceName, +// int nettype = 0, +// int restoreHeight = 0}) { +// +// final pathPointer = path.toNativeUtf8(); +// final passwordPointer = password.toNativeUtf8(); +// final deviceNamePointer = deviceName.toNativeUtf8(); +// final errorMessagePointer = ''.toNativeUtf8(); +// +// final isWalletRestored = restoreWalletFromDeviceNative( +// pathPointer, +// passwordPointer, +// deviceNamePointer, +// nettype, +// restoreHeight, +// errorMessagePointer) != 0; +// +// calloc.free(pathPointer); +// calloc.free(passwordPointer); +// +// storeSync(); +// +// if (!isWalletRestored) { +// throw WalletRestoreFromKeysException( +// message: convertUTF8ToString(pointer: errorMessagePointer)); +// } +// } + +Map openedWalletsByPath = {}; + +void loadWallet( + {required String path, required String password, int nettype = 0}) { + if (openedWalletsByPath[path] != null) { + wptr = openedWalletsByPath[path]!; + return; + } + try { + if (wptr == null || path != _lastOpenedWallet) { + if (wptr != null) { + final addr = wptr!.address; + Isolate.run(() { + wownero.Wallet_store(Pointer.fromAddress(addr)); + }); + } + wptr = wownero.WalletManager_openWallet(wmPtr, + path: path, password: password); + openedWalletsByPath[path] = wptr!; + _lastOpenedWallet = path; + } + } catch (e) { + print(e); + } + final status = wownero.Wallet_status(wptr!); + if (status != 0) { + final err = wownero.Wallet_errorString(wptr!); + print(err); + throw WalletOpeningException(message: err); + } +} + +void _createWallet(Map args) { + final path = args['path'] as String; + final password = args['password'] as String; + final language = args['language'] as String; + + createWalletSync(path: path, password: password, language: language); +} + +void _restoreFromSeed(Map args) { + final path = args['path'] as String; + final password = args['password'] as String; + final seed = args['seed'] as String; + final restoreHeight = args['restoreHeight'] as int; + + restoreWalletFromSeedSync( + path: path, password: password, seed: seed, restoreHeight: restoreHeight); +} + +void _restoreFromKeys(Map args) { + final path = args['path'] as String; + final password = args['password'] as String; + final language = args['language'] as String; + final restoreHeight = args['restoreHeight'] as int; + final address = args['address'] as String; + final viewKey = args['viewKey'] as String; + final spendKey = args['spendKey'] as String; + + restoreWalletFromKeysSync( + path: path, + password: password, + language: language, + restoreHeight: restoreHeight, + address: address, + viewKey: viewKey, + spendKey: spendKey); +} + +void _restoreFromSpendKey(Map args) { + final path = args['path'] as String; + final password = args['password'] as String; + final seed = args['seed'] as String; + final language = args['language'] as String; + final spendKey = args['spendKey'] as String; + final restoreHeight = args['restoreHeight'] as int; + + restoreWalletFromSpendKeySync( + path: path, + password: password, + seed: seed, + language: language, + restoreHeight: restoreHeight, + spendKey: spendKey); +} + +Future _openWallet(Map args) async => loadWallet( + path: args['path'] as String, password: args['password'] as String); + +Future _isWalletExist(String path) async => isWalletExistSync(path: path); + +void openWallet( + {required String path, + required String password, + int nettype = 0}) async => + loadWallet(path: path, password: password, nettype: nettype); + +Future openWalletAsync(Map args) async => + _openWallet(args); + +Future createWallet( + {required String path, + required String password, + required String language, + int nettype = 0}) async => + _createWallet({ + 'path': path, + 'password': password, + 'language': language, + 'nettype': nettype + }); + +Future restoreFromSeed( + {required String path, + required String password, + required String seed, + int nettype = 0, + int restoreHeight = 0}) async => + _restoreFromSeed({ + 'path': path, + 'password': password, + 'seed': seed, + 'nettype': nettype, + 'restoreHeight': restoreHeight + }); + +Future restoreFromKeys( + {required String path, + required String password, + required String language, + required String address, + required String viewKey, + required String spendKey, + int nettype = 0, + int restoreHeight = 0}) async => + _restoreFromKeys({ + 'path': path, + 'password': password, + 'language': language, + 'address': address, + 'viewKey': viewKey, + 'spendKey': spendKey, + 'nettype': nettype, + 'restoreHeight': restoreHeight + }); + +Future restoreFromSpendKey( + {required String path, + required String password, + required String seed, + required String language, + required String spendKey, + int nettype = 0, + int restoreHeight = 0}) async => + _restoreFromSpendKey({ + 'path': path, + 'password': password, + 'seed': seed, + 'language': language, + 'spendKey': spendKey, + 'nettype': nettype, + 'restoreHeight': restoreHeight + }); + +Future isWalletExist({required String path}) => _isWalletExist(path); diff --git a/cw_wownero/lib/api/wownero_output.dart b/cw_wownero/lib/api/wownero_output.dart new file mode 100644 index 000000000..a4756c4c5 --- /dev/null +++ b/cw_wownero/lib/api/wownero_output.dart @@ -0,0 +1,6 @@ +class WowneroOutput { + WowneroOutput({required this.address, required this.amount}); + + final String address; + final String amount; +} \ No newline at end of file diff --git a/cw_wownero/lib/cw_wownero.dart b/cw_wownero/lib/cw_wownero.dart new file mode 100644 index 000000000..33a55e305 --- /dev/null +++ b/cw_wownero/lib/cw_wownero.dart @@ -0,0 +1,8 @@ + +import 'cw_wownero_platform_interface.dart'; + +class CwWownero { + Future getPlatformVersion() { + return CwWowneroPlatform.instance.getPlatformVersion(); + } +} diff --git a/cw_wownero/lib/cw_wownero_method_channel.dart b/cw_wownero/lib/cw_wownero_method_channel.dart new file mode 100644 index 000000000..d797f5f81 --- /dev/null +++ b/cw_wownero/lib/cw_wownero_method_channel.dart @@ -0,0 +1,17 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'cw_wownero_platform_interface.dart'; + +/// An implementation of [CwWowneroPlatform] that uses method channels. +class MethodChannelCwWownero extends CwWowneroPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final methodChannel = const MethodChannel('cw_wownero'); + + @override + Future getPlatformVersion() async { + final version = await methodChannel.invokeMethod('getPlatformVersion'); + return version; + } +} diff --git a/cw_wownero/lib/cw_wownero_platform_interface.dart b/cw_wownero/lib/cw_wownero_platform_interface.dart new file mode 100644 index 000000000..78b21592c --- /dev/null +++ b/cw_wownero/lib/cw_wownero_platform_interface.dart @@ -0,0 +1,29 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'cw_wownero_method_channel.dart'; + +abstract class CwWowneroPlatform extends PlatformInterface { + /// Constructs a CwWowneroPlatform. + CwWowneroPlatform() : super(token: _token); + + static final Object _token = Object(); + + static CwWowneroPlatform _instance = MethodChannelCwWownero(); + + /// The default instance of [CwWowneroPlatform] to use. + /// + /// Defaults to [MethodChannelCwWownero]. + static CwWowneroPlatform get instance => _instance; + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [CwWowneroPlatform] when + /// they register themselves. + static set instance(CwWowneroPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + Future getPlatformVersion() { + throw UnimplementedError('platformVersion() has not been implemented.'); + } +} diff --git a/cw_wownero/lib/exceptions/wownero_transaction_creation_exception.dart b/cw_wownero/lib/exceptions/wownero_transaction_creation_exception.dart new file mode 100644 index 000000000..3b24be87b --- /dev/null +++ b/cw_wownero/lib/exceptions/wownero_transaction_creation_exception.dart @@ -0,0 +1,8 @@ +class WowneroTransactionCreationException implements Exception { + WowneroTransactionCreationException(this.message); + + final String message; + + @override + String toString() => message; +} \ No newline at end of file diff --git a/cw_wownero/lib/exceptions/wownero_transaction_no_inputs_exception.dart b/cw_wownero/lib/exceptions/wownero_transaction_no_inputs_exception.dart new file mode 100644 index 000000000..e42344abd --- /dev/null +++ b/cw_wownero/lib/exceptions/wownero_transaction_no_inputs_exception.dart @@ -0,0 +1,8 @@ +class WowneroTransactionNoInputsException implements Exception { + WowneroTransactionNoInputsException(this.inputsSize); + + int inputsSize; + + @override + String toString() => 'Not enough inputs ($inputsSize) selected. Please select more under Coin Control'; +} diff --git a/cw_wownero/lib/mnemonics/chinese_simplified.dart b/cw_wownero/lib/mnemonics/chinese_simplified.dart new file mode 100644 index 000000000..da3225041 --- /dev/null +++ b/cw_wownero/lib/mnemonics/chinese_simplified.dart @@ -0,0 +1,1630 @@ +class ChineseSimplifiedMnemonics { + static const words = [ + "的", + "一", + "是", + "在", + "不", + "了", + "有", + "和", + "人", + "这", + "中", + "大", + "为", + "上", + "个", + "国", + "我", + "以", + "要", + "他", + "时", + "来", + "用", + "们", + "生", + "到", + "作", + "地", + "于", + "出", + "就", + "分", + "对", + "成", + "会", + "可", + "主", + "发", + "年", + "动", + "同", + "工", + "也", + "能", + "下", + "过", + "子", + "说", + "产", + "种", + "面", + "而", + "方", + "后", + "多", + "定", + "行", + "学", + "法", + "所", + "民", + "得", + "经", + "十", + "三", + "之", + "进", + "着", + "等", + "部", + "度", + "家", + "电", + "力", + "里", + "如", + "水", + "化", + "高", + "自", + "二", + "理", + "起", + "小", + "物", + "现", + "实", + "加", + "量", + "都", + "两", + "体", + "制", + "机", + "当", + "使", + "点", + "从", + "业", + "本", + "去", + "把", + "性", + "好", + "应", + "开", + "它", + "合", + "还", + "因", + "由", + "其", + "些", + "然", + "前", + "外", + "天", + "政", + "四", + "日", + "那", + "社", + "义", + "事", + "平", + "形", + "相", + "全", + "表", + "间", + "样", + "与", + "关", + "各", + "重", + "新", + "线", + "内", + "数", + "正", + "心", + "反", + "你", + "明", + "看", + "原", + "又", + "么", + "利", + "比", + "或", + "但", + "质", + "气", + "第", + "向", + "道", + "命", + "此", + "变", + "条", + "只", + "没", + "结", + "解", + "问", + "意", + "建", + "月", + "公", + "无", + "系", + "军", + "很", + "情", + "者", + "最", + "立", + "代", + "想", + "已", + "通", + "并", + "提", + "直", + "题", + "党", + "程", + "展", + "五", + "果", + "料", + "象", + "员", + "革", + "位", + "入", + "常", + "文", + "总", + "次", + "品", + "式", + "活", + "设", + "及", + "管", + "特", + "件", + "长", + "求", + "老", + "头", + "基", + "资", + "边", + "流", + "路", + "级", + "少", + "图", + "山", + "统", + "接", + "知", + "较", + "将", + "组", + "见", + "计", + "别", + "她", + "手", + "角", + "期", + "根", + "论", + "运", + "农", + "指", + "几", + "九", + "区", + "强", + "放", + "决", + "西", + "被", + "干", + "做", + "必", + "战", + "先", + "回", + "则", + "任", + "取", + "据", + "处", + "队", + "南", + "给", + "色", + "光", + "门", + "即", + "保", + "治", + "北", + "造", + "百", + "规", + "热", + "领", + "七", + "海", + "口", + "东", + "导", + "器", + "压", + "志", + "世", + "金", + "增", + "争", + "济", + "阶", + "油", + "思", + "术", + "极", + "交", + "受", + "联", + "什", + "认", + "六", + "共", + "权", + "收", + "证", + "改", + "清", + "美", + "再", + "采", + "转", + "更", + "单", + "风", + "切", + "打", + "白", + "教", + "速", + "花", + "带", + "安", + "场", + "身", + "车", + "例", + "真", + "务", + "具", + "万", + "每", + "目", + "至", + "达", + "走", + "积", + "示", + "议", + "声", + "报", + "斗", + "完", + "类", + "八", + "离", + "华", + "名", + "确", + "才", + "科", + "张", + "信", + "马", + "节", + "话", + "米", + "整", + "空", + "元", + "况", + "今", + "集", + "温", + "传", + "土", + "许", + "步", + "群", + "广", + "石", + "记", + "需", + "段", + "研", + "界", + "拉", + "林", + "律", + "叫", + "且", + "究", + "观", + "越", + "织", + "装", + "影", + "算", + "低", + "持", + "音", + "众", + "书", + "布", + "复", + "容", + "儿", + "须", + "际", + "商", + "非", + "验", + "连", + "断", + "深", + "难", + "近", + "矿", + "千", + "周", + "委", + "素", + "技", + "备", + "半", + "办", + "青", + "省", + "列", + "习", + "响", + "约", + "支", + "般", + "史", + "感", + "劳", + "便", + "团", + "往", + "酸", + "历", + "市", + "克", + "何", + "除", + "消", + "构", + "府", + "称", + "太", + "准", + "精", + "值", + "号", + "率", + "族", + "维", + "划", + "选", + "标", + "写", + "存", + "候", + "毛", + "亲", + "快", + "效", + "斯", + "院", + "查", + "江", + "型", + "眼", + "王", + "按", + "格", + "养", + "易", + "置", + "派", + "层", + "片", + "始", + "却", + "专", + "状", + "育", + "厂", + "京", + "识", + "适", + "属", + "圆", + "包", + "火", + "住", + "调", + "满", + "县", + "局", + "照", + "参", + "红", + "细", + "引", + "听", + "该", + "铁", + "价", + "严", + "首", + "底", + "液", + "官", + "德", + "随", + "病", + "苏", + "失", + "尔", + "死", + "讲", + "配", + "女", + "黄", + "推", + "显", + "谈", + "罪", + "神", + "艺", + "呢", + "席", + "含", + "企", + "望", + "密", + "批", + "营", + "项", + "防", + "举", + "球", + "英", + "氧", + "势", + "告", + "李", + "台", + "落", + "木", + "帮", + "轮", + "破", + "亚", + "师", + "围", + "注", + "远", + "字", + "材", + "排", + "供", + "河", + "态", + "封", + "另", + "施", + "减", + "树", + "溶", + "怎", + "止", + "案", + "言", + "士", + "均", + "武", + "固", + "叶", + "鱼", + "波", + "视", + "仅", + "费", + "紧", + "爱", + "左", + "章", + "早", + "朝", + "害", + "续", + "轻", + "服", + "试", + "食", + "充", + "兵", + "源", + "判", + "护", + "司", + "足", + "某", + "练", + "差", + "致", + "板", + "田", + "降", + "黑", + "犯", + "负", + "击", + "范", + "继", + "兴", + "似", + "余", + "坚", + "曲", + "输", + "修", + "故", + "城", + "夫", + "够", + "送", + "笔", + "船", + "占", + "右", + "财", + "吃", + "富", + "春", + "职", + "觉", + "汉", + "画", + "功", + "巴", + "跟", + "虽", + "杂", + "飞", + "检", + "吸", + "助", + "升", + "阳", + "互", + "初", + "创", + "抗", + "考", + "投", + "坏", + "策", + "古", + "径", + "换", + "未", + "跑", + "留", + "钢", + "曾", + "端", + "责", + "站", + "简", + "述", + "钱", + "副", + "尽", + "帝", + "射", + "草", + "冲", + "承", + "独", + "令", + "限", + "阿", + "宣", + "环", + "双", + "请", + "超", + "微", + "让", + "控", + "州", + "良", + "轴", + "找", + "否", + "纪", + "益", + "依", + "优", + "顶", + "础", + "载", + "倒", + "房", + "突", + "坐", + "粉", + "敌", + "略", + "客", + "袁", + "冷", + "胜", + "绝", + "析", + "块", + "剂", + "测", + "丝", + "协", + "诉", + "念", + "陈", + "仍", + "罗", + "盐", + "友", + "洋", + "错", + "苦", + "夜", + "刑", + "移", + "频", + "逐", + "靠", + "混", + "母", + "短", + "皮", + "终", + "聚", + "汽", + "村", + "云", + "哪", + "既", + "距", + "卫", + "停", + "烈", + "央", + "察", + "烧", + "迅", + "境", + "若", + "印", + "洲", + "刻", + "括", + "激", + "孔", + "搞", + "甚", + "室", + "待", + "核", + "校", + "散", + "侵", + "吧", + "甲", + "游", + "久", + "菜", + "味", + "旧", + "模", + "湖", + "货", + "损", + "预", + "阻", + "毫", + "普", + "稳", + "乙", + "妈", + "植", + "息", + "扩", + "银", + "语", + "挥", + "酒", + "守", + "拿", + "序", + "纸", + "医", + "缺", + "雨", + "吗", + "针", + "刘", + "啊", + "急", + "唱", + "误", + "训", + "愿", + "审", + "附", + "获", + "茶", + "鲜", + "粮", + "斤", + "孩", + "脱", + "硫", + "肥", + "善", + "龙", + "演", + "父", + "渐", + "血", + "欢", + "械", + "掌", + "歌", + "沙", + "刚", + "攻", + "谓", + "盾", + "讨", + "晚", + "粒", + "乱", + "燃", + "矛", + "乎", + "杀", + "药", + "宁", + "鲁", + "贵", + "钟", + "煤", + "读", + "班", + "伯", + "香", + "介", + "迫", + "句", + "丰", + "培", + "握", + "兰", + "担", + "弦", + "蛋", + "沉", + "假", + "穿", + "执", + "答", + "乐", + "谁", + "顺", + "烟", + "缩", + "征", + "脸", + "喜", + "松", + "脚", + "困", + "异", + "免", + "背", + "星", + "福", + "买", + "染", + "井", + "概", + "慢", + "怕", + "磁", + "倍", + "祖", + "皇", + "促", + "静", + "补", + "评", + "翻", + "肉", + "践", + "尼", + "衣", + "宽", + "扬", + "棉", + "希", + "伤", + "操", + "垂", + "秋", + "宜", + "氢", + "套", + "督", + "振", + "架", + "亮", + "末", + "宪", + "庆", + "编", + "牛", + "触", + "映", + "雷", + "销", + "诗", + "座", + "居", + "抓", + "裂", + "胞", + "呼", + "娘", + "景", + "威", + "绿", + "晶", + "厚", + "盟", + "衡", + "鸡", + "孙", + "延", + "危", + "胶", + "屋", + "乡", + "临", + "陆", + "顾", + "掉", + "呀", + "灯", + "岁", + "措", + "束", + "耐", + "剧", + "玉", + "赵", + "跳", + "哥", + "季", + "课", + "凯", + "胡", + "额", + "款", + "绍", + "卷", + "齐", + "伟", + "蒸", + "殖", + "永", + "宗", + "苗", + "川", + "炉", + "岩", + "弱", + "零", + "杨", + "奏", + "沿", + "露", + "杆", + "探", + "滑", + "镇", + "饭", + "浓", + "航", + "怀", + "赶", + "库", + "夺", + "伊", + "灵", + "税", + "途", + "灭", + "赛", + "归", + "召", + "鼓", + "播", + "盘", + "裁", + "险", + "康", + "唯", + "录", + "菌", + "纯", + "借", + "糖", + "盖", + "横", + "符", + "私", + "努", + "堂", + "域", + "枪", + "润", + "幅", + "哈", + "竟", + "熟", + "虫", + "泽", + "脑", + "壤", + "碳", + "欧", + "遍", + "侧", + "寨", + "敢", + "彻", + "虑", + "斜", + "薄", + "庭", + "纳", + "弹", + "饲", + "伸", + "折", + "麦", + "湿", + "暗", + "荷", + "瓦", + "塞", + "床", + "筑", + "恶", + "户", + "访", + "塔", + "奇", + "透", + "梁", + "刀", + "旋", + "迹", + "卡", + "氯", + "遇", + "份", + "毒", + "泥", + "退", + "洗", + "摆", + "灰", + "彩", + "卖", + "耗", + "夏", + "择", + "忙", + "铜", + "献", + "硬", + "予", + "繁", + "圈", + "雪", + "函", + "亦", + "抽", + "篇", + "阵", + "阴", + "丁", + "尺", + "追", + "堆", + "雄", + "迎", + "泛", + "爸", + "楼", + "避", + "谋", + "吨", + "野", + "猪", + "旗", + "累", + "偏", + "典", + "馆", + "索", + "秦", + "脂", + "潮", + "爷", + "豆", + "忽", + "托", + "惊", + "塑", + "遗", + "愈", + "朱", + "替", + "纤", + "粗", + "倾", + "尚", + "痛", + "楚", + "谢", + "奋", + "购", + "磨", + "君", + "池", + "旁", + "碎", + "骨", + "监", + "捕", + "弟", + "暴", + "割", + "贯", + "殊", + "释", + "词", + "亡", + "壁", + "顿", + "宝", + "午", + "尘", + "闻", + "揭", + "炮", + "残", + "冬", + "桥", + "妇", + "警", + "综", + "招", + "吴", + "付", + "浮", + "遭", + "徐", + "您", + "摇", + "谷", + "赞", + "箱", + "隔", + "订", + "男", + "吹", + "园", + "纷", + "唐", + "败", + "宋", + "玻", + "巨", + "耕", + "坦", + "荣", + "闭", + "湾", + "键", + "凡", + "驻", + "锅", + "救", + "恩", + "剥", + "凝", + "碱", + "齿", + "截", + "炼", + "麻", + "纺", + "禁", + "废", + "盛", + "版", + "缓", + "净", + "睛", + "昌", + "婚", + "涉", + "筒", + "嘴", + "插", + "岸", + "朗", + "庄", + "街", + "藏", + "姑", + "贸", + "腐", + "奴", + "啦", + "惯", + "乘", + "伙", + "恢", + "匀", + "纱", + "扎", + "辩", + "耳", + "彪", + "臣", + "亿", + "璃", + "抵", + "脉", + "秀", + "萨", + "俄", + "网", + "舞", + "店", + "喷", + "纵", + "寸", + "汗", + "挂", + "洪", + "贺", + "闪", + "柬", + "爆", + "烯", + "津", + "稻", + "墙", + "软", + "勇", + "像", + "滚", + "厘", + "蒙", + "芳", + "肯", + "坡", + "柱", + "荡", + "腿", + "仪", + "旅", + "尾", + "轧", + "冰", + "贡", + "登", + "黎", + "削", + "钻", + "勒", + "逃", + "障", + "氨", + "郭", + "峰", + "币", + "港", + "伏", + "轨", + "亩", + "毕", + "擦", + "莫", + "刺", + "浪", + "秘", + "援", + "株", + "健", + "售", + "股", + "岛", + "甘", + "泡", + "睡", + "童", + "铸", + "汤", + "阀", + "休", + "汇", + "舍", + "牧", + "绕", + "炸", + "哲", + "磷", + "绩", + "朋", + "淡", + "尖", + "启", + "陷", + "柴", + "呈", + "徒", + "颜", + "泪", + "稍", + "忘", + "泵", + "蓝", + "拖", + "洞", + "授", + "镜", + "辛", + "壮", + "锋", + "贫", + "虚", + "弯", + "摩", + "泰", + "幼", + "廷", + "尊", + "窗", + "纲", + "弄", + "隶", + "疑", + "氏", + "宫", + "姐", + "震", + "瑞", + "怪", + "尤", + "琴", + "循", + "描", + "膜", + "违", + "夹", + "腰", + "缘", + "珠", + "穷", + "森", + "枝", + "竹", + "沟", + "催", + "绳", + "忆", + "邦", + "剩", + "幸", + "浆", + "栏", + "拥", + "牙", + "贮", + "礼", + "滤", + "钠", + "纹", + "罢", + "拍", + "咱", + "喊", + "袖", + "埃", + "勤", + "罚", + "焦", + "潜", + "伍", + "墨", + "欲", + "缝", + "姓", + "刊", + "饱", + "仿", + "奖", + "铝", + "鬼", + "丽", + "跨", + "默", + "挖", + "链", + "扫", + "喝", + "袋", + "炭", + "污", + "幕", + "诸", + "弧", + "励", + "梅", + "奶", + "洁", + "灾", + "舟", + "鉴", + "苯", + "讼", + "抱", + "毁", + "懂", + "寒", + "智", + "埔", + "寄", + "届", + "跃", + "渡", + "挑", + "丹", + "艰", + "贝", + "碰", + "拔", + "爹", + "戴", + "码", + "梦", + "芽", + "熔", + "赤", + "渔", + "哭", + "敬", + "颗", + "奔", + "铅", + "仲", + "虎", + "稀", + "妹", + "乏", + "珍", + "申", + "桌", + "遵", + "允", + "隆", + "螺", + "仓", + "魏", + "锐", + "晓", + "氮", + "兼", + "隐", + "碍", + "赫", + "拨", + "忠", + "肃", + "缸", + "牵", + "抢", + "博", + "巧", + "壳", + "兄", + "杜", + "讯", + "诚", + "碧", + "祥", + "柯", + "页", + "巡", + "矩", + "悲", + "灌", + "龄", + "伦", + "票", + "寻", + "桂", + "铺", + "圣", + "恐", + "恰", + "郑", + "趣", + "抬", + "荒", + "腾", + "贴", + "柔", + "滴", + "猛", + "阔", + "辆", + "妻", + "填", + "撤", + "储", + "签", + "闹", + "扰", + "紫", + "砂", + "递", + "戏", + "吊", + "陶", + "伐", + "喂", + "疗", + "瓶", + "婆", + "抚", + "臂", + "摸", + "忍", + "虾", + "蜡", + "邻", + "胸", + "巩", + "挤", + "偶", + "弃", + "槽", + "劲", + "乳", + "邓", + "吉", + "仁", + "烂", + "砖", + "租", + "乌", + "舰", + "伴", + "瓜", + "浅", + "丙", + "暂", + "燥", + "橡", + "柳", + "迷", + "暖", + "牌", + "秧", + "胆", + "详", + "簧", + "踏", + "瓷", + "谱", + "呆", + "宾", + "糊", + "洛", + "辉", + "愤", + "竞", + "隙", + "怒", + "粘", + "乃", + "绪", + "肩", + "籍", + "敏", + "涂", + "熙", + "皆", + "侦", + "悬", + "掘", + "享", + "纠", + "醒", + "狂", + "锁", + "淀", + "恨", + "牲", + "霸", + "爬", + "赏", + "逆", + "玩", + "陵", + "祝", + "秒", + "浙", + "貌" + ]; +} \ No newline at end of file diff --git a/cw_wownero/lib/mnemonics/dutch.dart b/cw_wownero/lib/mnemonics/dutch.dart new file mode 100644 index 000000000..3a1d00cfc --- /dev/null +++ b/cw_wownero/lib/mnemonics/dutch.dart @@ -0,0 +1,1630 @@ +class DutchMnemonics { + static const words = [ + "aalglad", + "aalscholver", + "aambeeld", + "aangeef", + "aanlandig", + "aanvaard", + "aanwakker", + "aapmens", + "aarten", + "abdicatie", + "abnormaal", + "abrikoos", + "accu", + "acuut", + "adjudant", + "admiraal", + "advies", + "afbidding", + "afdracht", + "affaire", + "affiche", + "afgang", + "afkick", + "afknap", + "aflees", + "afmijner", + "afname", + "afpreekt", + "afrader", + "afspeel", + "aftocht", + "aftrek", + "afzijdig", + "ahornboom", + "aktetas", + "akzo", + "alchemist", + "alcohol", + "aldaar", + "alexander", + "alfabet", + "alfredo", + "alice", + "alikruik", + "allrisk", + "altsax", + "alufolie", + "alziend", + "amai", + "ambacht", + "ambieer", + "amina", + "amnestie", + "amok", + "ampul", + "amuzikaal", + "angela", + "aniek", + "antje", + "antwerpen", + "anya", + "aorta", + "apache", + "apekool", + "appelaar", + "arganolie", + "argeloos", + "armoede", + "arrenslee", + "artritis", + "arubaan", + "asbak", + "ascii", + "asgrauw", + "asjes", + "asml", + "aspunt", + "asurn", + "asveld", + "aterling", + "atomair", + "atrium", + "atsma", + "atypisch", + "auping", + "aura", + "avifauna", + "axiaal", + "azoriaan", + "azteek", + "azuur", + "bachelor", + "badderen", + "badhotel", + "badmantel", + "badsteden", + "balie", + "ballans", + "balvers", + "bamibal", + "banneling", + "barracuda", + "basaal", + "batelaan", + "batje", + "beambte", + "bedlamp", + "bedwelmd", + "befaamd", + "begierd", + "begraaf", + "behield", + "beijaard", + "bejaagd", + "bekaaid", + "beks", + "bektas", + "belaad", + "belboei", + "belderbos", + "beloerd", + "beluchten", + "bemiddeld", + "benadeeld", + "benijd", + "berechten", + "beroemd", + "besef", + "besseling", + "best", + "betichten", + "bevind", + "bevochten", + "bevraagd", + "bewust", + "bidplaats", + "biefstuk", + "biemans", + "biezen", + "bijbaan", + "bijeenkom", + "bijfiguur", + "bijkaart", + "bijlage", + "bijpaard", + "bijtgaar", + "bijweg", + "bimmel", + "binck", + "bint", + "biobak", + "biotisch", + "biseks", + "bistro", + "bitter", + "bitumen", + "bizar", + "blad", + "bleken", + "blender", + "bleu", + "blief", + "blijven", + "blozen", + "bock", + "boef", + "boei", + "boks", + "bolder", + "bolus", + "bolvormig", + "bomaanval", + "bombarde", + "bomma", + "bomtapijt", + "bookmaker", + "boos", + "borg", + "bosbes", + "boshuizen", + "bosloop", + "botanicus", + "bougie", + "bovag", + "boxspring", + "braad", + "brasem", + "brevet", + "brigade", + "brinckman", + "bruid", + "budget", + "buffel", + "buks", + "bulgaar", + "buma", + "butaan", + "butler", + "buuf", + "cactus", + "cafeetje", + "camcorder", + "cannabis", + "canyon", + "capoeira", + "capsule", + "carkit", + "casanova", + "catalaan", + "ceintuur", + "celdeling", + "celplasma", + "cement", + "censeren", + "ceramisch", + "cerberus", + "cerebraal", + "cesium", + "cirkel", + "citeer", + "civiel", + "claxon", + "clenbuterol", + "clicheren", + "clijsen", + "coalitie", + "coassistentschap", + "coaxiaal", + "codetaal", + "cofinanciering", + "cognac", + "coltrui", + "comfort", + "commandant", + "condensaat", + "confectie", + "conifeer", + "convector", + "copier", + "corfu", + "correct", + "coup", + "couvert", + "creatie", + "credit", + "crematie", + "cricket", + "croupier", + "cruciaal", + "cruijff", + "cuisine", + "culemborg", + "culinair", + "curve", + "cyrano", + "dactylus", + "dading", + "dagblind", + "dagje", + "daglicht", + "dagprijs", + "dagranden", + "dakdekker", + "dakpark", + "dakterras", + "dalgrond", + "dambord", + "damkat", + "damlengte", + "damman", + "danenberg", + "debbie", + "decibel", + "defect", + "deformeer", + "degelijk", + "degradant", + "dejonghe", + "dekken", + "deppen", + "derek", + "derf", + "derhalve", + "detineren", + "devalueer", + "diaken", + "dicht", + "dictaat", + "dief", + "digitaal", + "dijbreuk", + "dijkmans", + "dimbaar", + "dinsdag", + "diode", + "dirigeer", + "disbalans", + "dobermann", + "doenbaar", + "doerak", + "dogma", + "dokhaven", + "dokwerker", + "doling", + "dolphijn", + "dolven", + "dombo", + "dooraderd", + "dopeling", + "doping", + "draderig", + "drama", + "drenkbak", + "dreumes", + "drol", + "drug", + "duaal", + "dublin", + "duplicaat", + "durven", + "dusdanig", + "dutchbat", + "dutje", + "dutten", + "duur", + "duwwerk", + "dwaal", + "dweil", + "dwing", + "dyslexie", + "ecostroom", + "ecotaks", + "educatie", + "eeckhout", + "eede", + "eemland", + "eencellig", + "eeneiig", + "eenruiter", + "eenwinter", + "eerenberg", + "eerrover", + "eersel", + "eetmaal", + "efteling", + "egaal", + "egtberts", + "eickhoff", + "eidooier", + "eiland", + "eind", + "eisden", + "ekster", + "elburg", + "elevatie", + "elfkoppig", + "elfrink", + "elftal", + "elimineer", + "elleboog", + "elma", + "elodie", + "elsa", + "embleem", + "embolie", + "emoe", + "emonds", + "emplooi", + "enduro", + "enfin", + "engageer", + "entourage", + "entstof", + "epileer", + "episch", + "eppo", + "erasmus", + "erboven", + "erebaan", + "erelijst", + "ereronden", + "ereteken", + "erfhuis", + "erfwet", + "erger", + "erica", + "ermitage", + "erna", + "ernie", + "erts", + "ertussen", + "eruitzien", + "ervaar", + "erven", + "erwt", + "esbeek", + "escort", + "esdoorn", + "essing", + "etage", + "eter", + "ethanol", + "ethicus", + "etholoog", + "eufonisch", + "eurocent", + "evacuatie", + "exact", + "examen", + "executant", + "exen", + "exit", + "exogeen", + "exotherm", + "expeditie", + "expletief", + "expres", + "extase", + "extinctie", + "faal", + "faam", + "fabel", + "facultair", + "fakir", + "fakkel", + "faliekant", + "fallisch", + "famke", + "fanclub", + "fase", + "fatsoen", + "fauna", + "federaal", + "feedback", + "feest", + "feilbaar", + "feitelijk", + "felblauw", + "figurante", + "fiod", + "fitheid", + "fixeer", + "flap", + "fleece", + "fleur", + "flexibel", + "flits", + "flos", + "flow", + "fluweel", + "foezelen", + "fokkelman", + "fokpaard", + "fokvee", + "folder", + "follikel", + "folmer", + "folteraar", + "fooi", + "foolen", + "forfait", + "forint", + "formule", + "fornuis", + "fosfaat", + "foxtrot", + "foyer", + "fragiel", + "frater", + "freak", + "freddie", + "fregat", + "freon", + "frijnen", + "fructose", + "frunniken", + "fuiven", + "funshop", + "furieus", + "fysica", + "gadget", + "galder", + "galei", + "galg", + "galvlieg", + "galzuur", + "ganesh", + "gaswet", + "gaza", + "gazelle", + "geaaid", + "gebiecht", + "gebufferd", + "gedijd", + "geef", + "geflanst", + "gefreesd", + "gegaan", + "gegijzeld", + "gegniffel", + "gegraaid", + "gehikt", + "gehobbeld", + "gehucht", + "geiser", + "geiten", + "gekaakt", + "gekheid", + "gekijf", + "gekmakend", + "gekocht", + "gekskap", + "gekte", + "gelubberd", + "gemiddeld", + "geordend", + "gepoederd", + "gepuft", + "gerda", + "gerijpt", + "geseald", + "geshockt", + "gesierd", + "geslaagd", + "gesnaaid", + "getracht", + "getwijfel", + "geuit", + "gevecht", + "gevlagd", + "gewicht", + "gezaagd", + "gezocht", + "ghanees", + "giebelen", + "giechel", + "giepmans", + "gips", + "giraal", + "gistachtig", + "gitaar", + "glaasje", + "gletsjer", + "gleuf", + "glibberen", + "glijbaan", + "gloren", + "gluipen", + "gluren", + "gluur", + "gnoe", + "goddelijk", + "godgans", + "godschalk", + "godzalig", + "goeierd", + "gogme", + "goklustig", + "gokwereld", + "gonggrijp", + "gonje", + "goor", + "grabbel", + "graf", + "graveer", + "grif", + "grolleman", + "grom", + "groosman", + "grubben", + "gruijs", + "grut", + "guacamole", + "guido", + "guppy", + "haazen", + "hachelijk", + "haex", + "haiku", + "hakhout", + "hakken", + "hanegem", + "hans", + "hanteer", + "harrie", + "hazebroek", + "hedonist", + "heil", + "heineken", + "hekhuis", + "hekman", + "helbig", + "helga", + "helwegen", + "hengelaar", + "herkansen", + "hermafrodiet", + "hertaald", + "hiaat", + "hikspoors", + "hitachi", + "hitparade", + "hobo", + "hoeve", + "holocaust", + "hond", + "honnepon", + "hoogacht", + "hotelbed", + "hufter", + "hugo", + "huilbier", + "hulk", + "humus", + "huwbaar", + "huwelijk", + "hype", + "iconisch", + "idema", + "ideogram", + "idolaat", + "ietje", + "ijker", + "ijkheid", + "ijklijn", + "ijkmaat", + "ijkwezen", + "ijmuiden", + "ijsbox", + "ijsdag", + "ijselijk", + "ijskoud", + "ilse", + "immuun", + "impliceer", + "impuls", + "inbijten", + "inbuigen", + "indijken", + "induceer", + "indy", + "infecteer", + "inhaak", + "inkijk", + "inluiden", + "inmijnen", + "inoefenen", + "inpolder", + "inrijden", + "inslaan", + "invitatie", + "inwaaien", + "ionisch", + "isaac", + "isolatie", + "isotherm", + "isra", + "italiaan", + "ivoor", + "jacobs", + "jakob", + "jammen", + "jampot", + "jarig", + "jehova", + "jenever", + "jezus", + "joana", + "jobdienst", + "josua", + "joule", + "juich", + "jurk", + "juut", + "kaas", + "kabelaar", + "kabinet", + "kagenaar", + "kajuit", + "kalebas", + "kalm", + "kanjer", + "kapucijn", + "karregat", + "kart", + "katvanger", + "katwijk", + "kegelaar", + "keiachtig", + "keizer", + "kenletter", + "kerdijk", + "keus", + "kevlar", + "kezen", + "kickback", + "kieviet", + "kijken", + "kikvors", + "kilheid", + "kilobit", + "kilsdonk", + "kipschnitzel", + "kissebis", + "klad", + "klagelijk", + "klak", + "klapbaar", + "klaver", + "klene", + "klets", + "klijnhout", + "klit", + "klok", + "klonen", + "klotefilm", + "kluif", + "klumper", + "klus", + "knabbel", + "knagen", + "knaven", + "kneedbaar", + "knmi", + "knul", + "knus", + "kokhals", + "komiek", + "komkommer", + "kompaan", + "komrij", + "komvormig", + "koning", + "kopbal", + "kopklep", + "kopnagel", + "koppejan", + "koptekst", + "kopwand", + "koraal", + "kosmisch", + "kostbaar", + "kram", + "kraneveld", + "kras", + "kreling", + "krengen", + "kribbe", + "krik", + "kruid", + "krulbol", + "kuijper", + "kuipbank", + "kuit", + "kuiven", + "kutsmoes", + "kuub", + "kwak", + "kwatong", + "kwetsbaar", + "kwezelaar", + "kwijnen", + "kwik", + "kwinkslag", + "kwitantie", + "lading", + "lakbeits", + "lakken", + "laklaag", + "lakmoes", + "lakwijk", + "lamheid", + "lamp", + "lamsbout", + "lapmiddel", + "larve", + "laser", + "latijn", + "latuw", + "lawaai", + "laxeerpil", + "lebberen", + "ledeboer", + "leefbaar", + "leeman", + "lefdoekje", + "lefhebber", + "legboor", + "legsel", + "leguaan", + "leiplaat", + "lekdicht", + "lekrijden", + "leksteen", + "lenen", + "leraar", + "lesbienne", + "leugenaar", + "leut", + "lexicaal", + "lezing", + "lieten", + "liggeld", + "lijdzaam", + "lijk", + "lijmstang", + "lijnschip", + "likdoorn", + "likken", + "liksteen", + "limburg", + "link", + "linoleum", + "lipbloem", + "lipman", + "lispelen", + "lissabon", + "litanie", + "liturgie", + "lochem", + "loempia", + "loesje", + "logheid", + "lonen", + "lonneke", + "loom", + "loos", + "losbaar", + "loslaten", + "losplaats", + "loting", + "lotnummer", + "lots", + "louie", + "lourdes", + "louter", + "lowbudget", + "luijten", + "luikenaar", + "luilak", + "luipaard", + "luizenbos", + "lulkoek", + "lumen", + "lunzen", + "lurven", + "lutjeboer", + "luttel", + "lutz", + "luuk", + "luwte", + "luyendijk", + "lyceum", + "lynx", + "maakbaar", + "magdalena", + "malheid", + "manchet", + "manfred", + "manhaftig", + "mank", + "mantel", + "marion", + "marxist", + "masmeijer", + "massaal", + "matsen", + "matverf", + "matze", + "maude", + "mayonaise", + "mechanica", + "meifeest", + "melodie", + "meppelink", + "midvoor", + "midweeks", + "midzomer", + "miezel", + "mijnraad", + "minus", + "mirck", + "mirte", + "mispakken", + "misraden", + "miswassen", + "mitella", + "moker", + "molecule", + "mombakkes", + "moonen", + "mopperaar", + "moraal", + "morgana", + "mormel", + "mosselaar", + "motregen", + "mouw", + "mufheid", + "mutueel", + "muzelman", + "naaidoos", + "naald", + "nadeel", + "nadruk", + "nagy", + "nahon", + "naima", + "nairobi", + "napalm", + "napels", + "napijn", + "napoleon", + "narigheid", + "narratief", + "naseizoen", + "nasibal", + "navigatie", + "nawijn", + "negatief", + "nekletsel", + "nekwervel", + "neolatijn", + "neonataal", + "neptunus", + "nerd", + "nest", + "neuzelaar", + "nihiliste", + "nijenhuis", + "nijging", + "nijhoff", + "nijl", + "nijptang", + "nippel", + "nokkenas", + "noordam", + "noren", + "normaal", + "nottelman", + "notulant", + "nout", + "nuance", + "nuchter", + "nudorp", + "nulde", + "nullijn", + "nulmeting", + "nunspeet", + "nylon", + "obelisk", + "object", + "oblie", + "obsceen", + "occlusie", + "oceaan", + "ochtend", + "ockhuizen", + "oerdom", + "oergezond", + "oerlaag", + "oester", + "okhuijsen", + "olifant", + "olijfboer", + "omaans", + "ombudsman", + "omdat", + "omdijken", + "omdoen", + "omgebouwd", + "omkeer", + "omkomen", + "ommegaand", + "ommuren", + "omroep", + "omruil", + "omslaan", + "omsmeden", + "omvaar", + "onaardig", + "onedel", + "onenig", + "onheilig", + "onrecht", + "onroerend", + "ontcijfer", + "onthaal", + "ontvallen", + "ontzadeld", + "onzacht", + "onzin", + "onzuiver", + "oogappel", + "ooibos", + "ooievaar", + "ooit", + "oorarts", + "oorhanger", + "oorijzer", + "oorklep", + "oorschelp", + "oorworm", + "oorzaak", + "opdagen", + "opdien", + "opdweilen", + "opel", + "opgebaard", + "opinie", + "opjutten", + "opkijken", + "opklaar", + "opkuisen", + "opkwam", + "opnaaien", + "opossum", + "opsieren", + "opsmeer", + "optreden", + "opvijzel", + "opvlammen", + "opwind", + "oraal", + "orchidee", + "orkest", + "ossuarium", + "ostendorf", + "oublie", + "oudachtig", + "oudbakken", + "oudnoors", + "oudshoorn", + "oudtante", + "oven", + "over", + "oxidant", + "pablo", + "pacht", + "paktafel", + "pakzadel", + "paljas", + "panharing", + "papfles", + "paprika", + "parochie", + "paus", + "pauze", + "paviljoen", + "peek", + "pegel", + "peigeren", + "pekela", + "pendant", + "penibel", + "pepmiddel", + "peptalk", + "periferie", + "perron", + "pessarium", + "peter", + "petfles", + "petgat", + "peuk", + "pfeifer", + "picknick", + "pief", + "pieneman", + "pijlkruid", + "pijnacker", + "pijpelink", + "pikdonker", + "pikeer", + "pilaar", + "pionier", + "pipet", + "piscine", + "pissebed", + "pitchen", + "pixel", + "plamuren", + "plan", + "plausibel", + "plegen", + "plempen", + "pleonasme", + "plezant", + "podoloog", + "pofmouw", + "pokdalig", + "ponywagen", + "popachtig", + "popidool", + "porren", + "positie", + "potten", + "pralen", + "prezen", + "prijzen", + "privaat", + "proef", + "prooi", + "prozawerk", + "pruik", + "prul", + "publiceer", + "puck", + "puilen", + "pukkelig", + "pulveren", + "pupil", + "puppy", + "purmerend", + "pustjens", + "putemmer", + "puzzelaar", + "queenie", + "quiche", + "raam", + "raar", + "raat", + "raes", + "ralf", + "rally", + "ramona", + "ramselaar", + "ranonkel", + "rapen", + "rapunzel", + "rarekiek", + "rarigheid", + "rattenhol", + "ravage", + "reactie", + "recreant", + "redacteur", + "redster", + "reewild", + "regie", + "reijnders", + "rein", + "replica", + "revanche", + "rigide", + "rijbaan", + "rijdansen", + "rijgen", + "rijkdom", + "rijles", + "rijnwijn", + "rijpma", + "rijstafel", + "rijtaak", + "rijzwepen", + "rioleer", + "ripdeal", + "riphagen", + "riskant", + "rits", + "rivaal", + "robbedoes", + "robot", + "rockact", + "rodijk", + "rogier", + "rohypnol", + "rollaag", + "rolpaal", + "roltafel", + "roof", + "roon", + "roppen", + "rosbief", + "rosharig", + "rosielle", + "rotan", + "rotleven", + "rotten", + "rotvaart", + "royaal", + "royeer", + "rubato", + "ruby", + "ruche", + "rudge", + "ruggetje", + "rugnummer", + "rugpijn", + "rugtitel", + "rugzak", + "ruilbaar", + "ruis", + "ruit", + "rukwind", + "rulijs", + "rumoeren", + "rumsdorp", + "rumtaart", + "runnen", + "russchen", + "ruwkruid", + "saboteer", + "saksisch", + "salade", + "salpeter", + "sambabal", + "samsam", + "satelliet", + "satineer", + "saus", + "scampi", + "scarabee", + "scenario", + "schobben", + "schubben", + "scout", + "secessie", + "secondair", + "seculair", + "sediment", + "seeland", + "settelen", + "setwinst", + "sheriff", + "shiatsu", + "siciliaan", + "sidderaal", + "sigma", + "sijben", + "silvana", + "simkaart", + "sinds", + "situatie", + "sjaak", + "sjardijn", + "sjezen", + "sjor", + "skinhead", + "skylab", + "slamixen", + "sleijpen", + "slijkerig", + "slordig", + "slowaak", + "sluieren", + "smadelijk", + "smiecht", + "smoel", + "smos", + "smukken", + "snackcar", + "snavel", + "sneaker", + "sneu", + "snijdbaar", + "snit", + "snorder", + "soapbox", + "soetekouw", + "soigneren", + "sojaboon", + "solo", + "solvabel", + "somber", + "sommatie", + "soort", + "soppen", + "sopraan", + "soundbar", + "spanen", + "spawater", + "spijgat", + "spinaal", + "spionage", + "spiraal", + "spleet", + "splijt", + "spoed", + "sporen", + "spul", + "spuug", + "spuw", + "stalen", + "standaard", + "star", + "stefan", + "stencil", + "stijf", + "stil", + "stip", + "stopdas", + "stoten", + "stoven", + "straat", + "strobbe", + "strubbel", + "stucadoor", + "stuif", + "stukadoor", + "subhoofd", + "subregent", + "sudoku", + "sukade", + "sulfaat", + "surinaams", + "suus", + "syfilis", + "symboliek", + "sympathie", + "synagoge", + "synchroon", + "synergie", + "systeem", + "taanderij", + "tabak", + "tachtig", + "tackelen", + "taiwanees", + "talman", + "tamheid", + "tangaslip", + "taps", + "tarkan", + "tarwe", + "tasman", + "tatjana", + "taxameter", + "teil", + "teisman", + "telbaar", + "telco", + "telganger", + "telstar", + "tenant", + "tepel", + "terzet", + "testament", + "ticket", + "tiesinga", + "tijdelijk", + "tika", + "tiksel", + "tilleman", + "timbaal", + "tinsteen", + "tiplijn", + "tippelaar", + "tjirpen", + "toezeggen", + "tolbaas", + "tolgeld", + "tolhek", + "tolo", + "tolpoort", + "toltarief", + "tolvrij", + "tomaat", + "tondeuse", + "toog", + "tooi", + "toonbaar", + "toos", + "topclub", + "toppen", + "toptalent", + "topvrouw", + "toque", + "torment", + "tornado", + "tosti", + "totdat", + "toucheer", + "toulouse", + "tournedos", + "tout", + "trabant", + "tragedie", + "trailer", + "traject", + "traktaat", + "trauma", + "tray", + "trechter", + "tred", + "tref", + "treur", + "troebel", + "tros", + "trucage", + "truffel", + "tsaar", + "tucht", + "tuenter", + "tuitelig", + "tukje", + "tuktuk", + "tulp", + "tuma", + "tureluurs", + "twijfel", + "twitteren", + "tyfoon", + "typograaf", + "ugandees", + "uiachtig", + "uier", + "uisnipper", + "ultiem", + "unitair", + "uranium", + "urbaan", + "urendag", + "ursula", + "uurcirkel", + "uurglas", + "uzelf", + "vaat", + "vakantie", + "vakleraar", + "valbijl", + "valpartij", + "valreep", + "valuatie", + "vanmiddag", + "vanonder", + "varaan", + "varken", + "vaten", + "veenbes", + "veeteler", + "velgrem", + "vellekoop", + "velvet", + "veneberg", + "venlo", + "vent", + "venusberg", + "venw", + "veredeld", + "verf", + "verhaaf", + "vermaak", + "vernaaid", + "verraad", + "vers", + "veruit", + "verzaagd", + "vetachtig", + "vetlok", + "vetmesten", + "veto", + "vetrek", + "vetstaart", + "vetten", + "veurink", + "viaduct", + "vibrafoon", + "vicariaat", + "vieux", + "vieveen", + "vijfvoud", + "villa", + "vilt", + "vimmetje", + "vindbaar", + "vips", + "virtueel", + "visdieven", + "visee", + "visie", + "vlaag", + "vleugel", + "vmbo", + "vocht", + "voesenek", + "voicemail", + "voip", + "volg", + "vork", + "vorselaar", + "voyeur", + "vracht", + "vrekkig", + "vreten", + "vrije", + "vrozen", + "vrucht", + "vucht", + "vugt", + "vulkaan", + "vulmiddel", + "vulva", + "vuren", + "waas", + "wacht", + "wadvogel", + "wafel", + "waffel", + "walhalla", + "walnoot", + "walraven", + "wals", + "walvis", + "wandaad", + "wanen", + "wanmolen", + "want", + "warklomp", + "warm", + "wasachtig", + "wasteil", + "watt", + "webhandel", + "weblog", + "webpagina", + "webzine", + "wedereis", + "wedstrijd", + "weeda", + "weert", + "wegmaaien", + "wegscheer", + "wekelijks", + "wekken", + "wekroep", + "wektoon", + "weldaad", + "welwater", + "wendbaar", + "wenkbrauw", + "wens", + "wentelaar", + "wervel", + "wesseling", + "wetboek", + "wetmatig", + "whirlpool", + "wijbrands", + "wijdbeens", + "wijk", + "wijnbes", + "wijting", + "wild", + "wimpelen", + "wingebied", + "winplaats", + "winter", + "winzucht", + "wipstaart", + "wisgerhof", + "withaar", + "witmaker", + "wokkel", + "wolf", + "wonenden", + "woning", + "worden", + "worp", + "wortel", + "wrat", + "wrijf", + "wringen", + "yoghurt", + "ypsilon", + "zaaijer", + "zaak", + "zacharias", + "zakelijk", + "zakkam", + "zakwater", + "zalf", + "zalig", + "zaniken", + "zebracode", + "zeeblauw", + "zeef", + "zeegaand", + "zeeuw", + "zege", + "zegje", + "zeil", + "zesbaans", + "zesenhalf", + "zeskantig", + "zesmaal", + "zetbaas", + "zetpil", + "zeulen", + "ziezo", + "zigzag", + "zijaltaar", + "zijbeuk", + "zijlijn", + "zijmuur", + "zijn", + "zijwaarts", + "zijzelf", + "zilt", + "zimmerman", + "zinledig", + "zinnelijk", + "zionist", + "zitdag", + "zitruimte", + "zitzak", + "zoal", + "zodoende", + "zoekbots", + "zoem", + "zoiets", + "zojuist", + "zondaar", + "zotskap", + "zottebol", + "zucht", + "zuivel", + "zulk", + "zult", + "zuster", + "zuur", + "zweedijk", + "zwendel", + "zwepen", + "zwiep", + "zwijmel", + "zworen" + ]; +} \ No newline at end of file diff --git a/cw_wownero/lib/mnemonics/english.dart b/cw_wownero/lib/mnemonics/english.dart new file mode 100644 index 000000000..fb464d04e --- /dev/null +++ b/cw_wownero/lib/mnemonics/english.dart @@ -0,0 +1,1630 @@ +class EnglishMnemonics { + static const words = [ + "abbey", + "abducts", + "ability", + "ablaze", + "abnormal", + "abort", + "abrasive", + "absorb", + "abyss", + "academy", + "aces", + "aching", + "acidic", + "acoustic", + "acquire", + "across", + "actress", + "acumen", + "adapt", + "addicted", + "adept", + "adhesive", + "adjust", + "adopt", + "adrenalin", + "adult", + "adventure", + "aerial", + "afar", + "affair", + "afield", + "afloat", + "afoot", + "afraid", + "after", + "against", + "agenda", + "aggravate", + "agile", + "aglow", + "agnostic", + "agony", + "agreed", + "ahead", + "aided", + "ailments", + "aimless", + "airport", + "aisle", + "ajar", + "akin", + "alarms", + "album", + "alchemy", + "alerts", + "algebra", + "alkaline", + "alley", + "almost", + "aloof", + "alpine", + "already", + "also", + "altitude", + "alumni", + "always", + "amaze", + "ambush", + "amended", + "amidst", + "ammo", + "amnesty", + "among", + "amply", + "amused", + "anchor", + "android", + "anecdote", + "angled", + "ankle", + "annoyed", + "answers", + "antics", + "anvil", + "anxiety", + "anybody", + "apart", + "apex", + "aphid", + "aplomb", + "apology", + "apply", + "apricot", + "aptitude", + "aquarium", + "arbitrary", + "archer", + "ardent", + "arena", + "argue", + "arises", + "army", + "around", + "arrow", + "arsenic", + "artistic", + "ascend", + "ashtray", + "aside", + "asked", + "asleep", + "aspire", + "assorted", + "asylum", + "athlete", + "atlas", + "atom", + "atrium", + "attire", + "auburn", + "auctions", + "audio", + "august", + "aunt", + "austere", + "autumn", + "avatar", + "avidly", + "avoid", + "awakened", + "awesome", + "awful", + "awkward", + "awning", + "awoken", + "axes", + "axis", + "axle", + "aztec", + "azure", + "baby", + "bacon", + "badge", + "baffles", + "bagpipe", + "bailed", + "bakery", + "balding", + "bamboo", + "banjo", + "baptism", + "basin", + "batch", + "bawled", + "bays", + "because", + "beer", + "befit", + "begun", + "behind", + "being", + "below", + "bemused", + "benches", + "berries", + "bested", + "betting", + "bevel", + "beware", + "beyond", + "bias", + "bicycle", + "bids", + "bifocals", + "biggest", + "bikini", + "bimonthly", + "binocular", + "biology", + "biplane", + "birth", + "biscuit", + "bite", + "biweekly", + "blender", + "blip", + "bluntly", + "boat", + "bobsled", + "bodies", + "bogeys", + "boil", + "boldly", + "bomb", + "border", + "boss", + "both", + "bounced", + "bovine", + "bowling", + "boxes", + "boyfriend", + "broken", + "brunt", + "bubble", + "buckets", + "budget", + "buffet", + "bugs", + "building", + "bulb", + "bumper", + "bunch", + "business", + "butter", + "buying", + "buzzer", + "bygones", + "byline", + "bypass", + "cabin", + "cactus", + "cadets", + "cafe", + "cage", + "cajun", + "cake", + "calamity", + "camp", + "candy", + "casket", + "catch", + "cause", + "cavernous", + "cease", + "cedar", + "ceiling", + "cell", + "cement", + "cent", + "certain", + "chlorine", + "chrome", + "cider", + "cigar", + "cinema", + "circle", + "cistern", + "citadel", + "civilian", + "claim", + "click", + "clue", + "coal", + "cobra", + "cocoa", + "code", + "coexist", + "coffee", + "cogs", + "cohesive", + "coils", + "colony", + "comb", + "cool", + "copy", + "corrode", + "costume", + "cottage", + "cousin", + "cowl", + "criminal", + "cube", + "cucumber", + "cuddled", + "cuffs", + "cuisine", + "cunning", + "cupcake", + "custom", + "cycling", + "cylinder", + "cynical", + "dabbing", + "dads", + "daft", + "dagger", + "daily", + "damp", + "dangerous", + "dapper", + "darted", + "dash", + "dating", + "dauntless", + "dawn", + "daytime", + "dazed", + "debut", + "decay", + "dedicated", + "deepest", + "deftly", + "degrees", + "dehydrate", + "deity", + "dejected", + "delayed", + "demonstrate", + "dented", + "deodorant", + "depth", + "desk", + "devoid", + "dewdrop", + "dexterity", + "dialect", + "dice", + "diet", + "different", + "digit", + "dilute", + "dime", + "dinner", + "diode", + "diplomat", + "directed", + "distance", + "ditch", + "divers", + "dizzy", + "doctor", + "dodge", + "does", + "dogs", + "doing", + "dolphin", + "domestic", + "donuts", + "doorway", + "dormant", + "dosage", + "dotted", + "double", + "dove", + "down", + "dozen", + "dreams", + "drinks", + "drowning", + "drunk", + "drying", + "dual", + "dubbed", + "duckling", + "dude", + "duets", + "duke", + "dullness", + "dummy", + "dunes", + "duplex", + "duration", + "dusted", + "duties", + "dwarf", + "dwelt", + "dwindling", + "dying", + "dynamite", + "dyslexic", + "each", + "eagle", + "earth", + "easy", + "eating", + "eavesdrop", + "eccentric", + "echo", + "eclipse", + "economics", + "ecstatic", + "eden", + "edgy", + "edited", + "educated", + "eels", + "efficient", + "eggs", + "egotistic", + "eight", + "either", + "eject", + "elapse", + "elbow", + "eldest", + "eleven", + "elite", + "elope", + "else", + "eluded", + "emails", + "ember", + "emerge", + "emit", + "emotion", + "empty", + "emulate", + "energy", + "enforce", + "enhanced", + "enigma", + "enjoy", + "enlist", + "enmity", + "enough", + "enraged", + "ensign", + "entrance", + "envy", + "epoxy", + "equip", + "erase", + "erected", + "erosion", + "error", + "eskimos", + "espionage", + "essential", + "estate", + "etched", + "eternal", + "ethics", + "etiquette", + "evaluate", + "evenings", + "evicted", + "evolved", + "examine", + "excess", + "exhale", + "exit", + "exotic", + "exquisite", + "extra", + "exult", + "fabrics", + "factual", + "fading", + "fainted", + "faked", + "fall", + "family", + "fancy", + "farming", + "fatal", + "faulty", + "fawns", + "faxed", + "fazed", + "feast", + "february", + "federal", + "feel", + "feline", + "females", + "fences", + "ferry", + "festival", + "fetches", + "fever", + "fewest", + "fiat", + "fibula", + "fictional", + "fidget", + "fierce", + "fifteen", + "fight", + "films", + "firm", + "fishing", + "fitting", + "five", + "fixate", + "fizzle", + "fleet", + "flippant", + "flying", + "foamy", + "focus", + "foes", + "foggy", + "foiled", + "folding", + "fonts", + "foolish", + "fossil", + "fountain", + "fowls", + "foxes", + "foyer", + "framed", + "friendly", + "frown", + "fruit", + "frying", + "fudge", + "fuel", + "fugitive", + "fully", + "fuming", + "fungal", + "furnished", + "fuselage", + "future", + "fuzzy", + "gables", + "gadget", + "gags", + "gained", + "galaxy", + "gambit", + "gang", + "gasp", + "gather", + "gauze", + "gave", + "gawk", + "gaze", + "gearbox", + "gecko", + "geek", + "gels", + "gemstone", + "general", + "geometry", + "germs", + "gesture", + "getting", + "geyser", + "ghetto", + "ghost", + "giant", + "giddy", + "gifts", + "gigantic", + "gills", + "gimmick", + "ginger", + "girth", + "giving", + "glass", + "gleeful", + "glide", + "gnaw", + "gnome", + "goat", + "goblet", + "godfather", + "goes", + "goggles", + "going", + "goldfish", + "gone", + "goodbye", + "gopher", + "gorilla", + "gossip", + "gotten", + "gourmet", + "governing", + "gown", + "greater", + "grunt", + "guarded", + "guest", + "guide", + "gulp", + "gumball", + "guru", + "gusts", + "gutter", + "guys", + "gymnast", + "gypsy", + "gyrate", + "habitat", + "hacksaw", + "haggled", + "hairy", + "hamburger", + "happens", + "hashing", + "hatchet", + "haunted", + "having", + "hawk", + "haystack", + "hazard", + "hectare", + "hedgehog", + "heels", + "hefty", + "height", + "hemlock", + "hence", + "heron", + "hesitate", + "hexagon", + "hickory", + "hiding", + "highway", + "hijack", + "hiker", + "hills", + "himself", + "hinder", + "hippo", + "hire", + "history", + "hitched", + "hive", + "hoax", + "hobby", + "hockey", + "hoisting", + "hold", + "honked", + "hookup", + "hope", + "hornet", + "hospital", + "hotel", + "hounded", + "hover", + "howls", + "hubcaps", + "huddle", + "huge", + "hull", + "humid", + "hunter", + "hurried", + "husband", + "huts", + "hybrid", + "hydrogen", + "hyper", + "iceberg", + "icing", + "icon", + "identity", + "idiom", + "idled", + "idols", + "igloo", + "ignore", + "iguana", + "illness", + "imagine", + "imbalance", + "imitate", + "impel", + "inactive", + "inbound", + "incur", + "industrial", + "inexact", + "inflamed", + "ingested", + "initiate", + "injury", + "inkling", + "inline", + "inmate", + "innocent", + "inorganic", + "input", + "inquest", + "inroads", + "insult", + "intended", + "inundate", + "invoke", + "inwardly", + "ionic", + "irate", + "iris", + "irony", + "irritate", + "island", + "isolated", + "issued", + "italics", + "itches", + "items", + "itinerary", + "itself", + "ivory", + "jabbed", + "jackets", + "jaded", + "jagged", + "jailed", + "jamming", + "january", + "jargon", + "jaunt", + "javelin", + "jaws", + "jazz", + "jeans", + "jeers", + "jellyfish", + "jeopardy", + "jerseys", + "jester", + "jetting", + "jewels", + "jigsaw", + "jingle", + "jittery", + "jive", + "jobs", + "jockey", + "jogger", + "joining", + "joking", + "jolted", + "jostle", + "journal", + "joyous", + "jubilee", + "judge", + "juggled", + "juicy", + "jukebox", + "july", + "jump", + "junk", + "jury", + "justice", + "juvenile", + "kangaroo", + "karate", + "keep", + "kennel", + "kept", + "kernels", + "kettle", + "keyboard", + "kickoff", + "kidneys", + "king", + "kiosk", + "kisses", + "kitchens", + "kiwi", + "knapsack", + "knee", + "knife", + "knowledge", + "knuckle", + "koala", + "laboratory", + "ladder", + "lagoon", + "lair", + "lakes", + "lamb", + "language", + "laptop", + "large", + "last", + "later", + "launching", + "lava", + "lawsuit", + "layout", + "lazy", + "lectures", + "ledge", + "leech", + "left", + "legion", + "leisure", + "lemon", + "lending", + "leopard", + "lesson", + "lettuce", + "lexicon", + "liar", + "library", + "licks", + "lids", + "lied", + "lifestyle", + "light", + "likewise", + "lilac", + "limits", + "linen", + "lion", + "lipstick", + "liquid", + "listen", + "lively", + "loaded", + "lobster", + "locker", + "lodge", + "lofty", + "logic", + "loincloth", + "long", + "looking", + "lopped", + "lordship", + "losing", + "lottery", + "loudly", + "love", + "lower", + "loyal", + "lucky", + "luggage", + "lukewarm", + "lullaby", + "lumber", + "lunar", + "lurk", + "lush", + "luxury", + "lymph", + "lynx", + "lyrics", + "macro", + "madness", + "magically", + "mailed", + "major", + "makeup", + "malady", + "mammal", + "maps", + "masterful", + "match", + "maul", + "maverick", + "maximum", + "mayor", + "maze", + "meant", + "mechanic", + "medicate", + "meeting", + "megabyte", + "melting", + "memoir", + "menu", + "merger", + "mesh", + "metro", + "mews", + "mice", + "midst", + "mighty", + "mime", + "mirror", + "misery", + "mittens", + "mixture", + "moat", + "mobile", + "mocked", + "mohawk", + "moisture", + "molten", + "moment", + "money", + "moon", + "mops", + "morsel", + "mostly", + "motherly", + "mouth", + "movement", + "mowing", + "much", + "muddy", + "muffin", + "mugged", + "mullet", + "mumble", + "mundane", + "muppet", + "mural", + "musical", + "muzzle", + "myriad", + "mystery", + "myth", + "nabbing", + "nagged", + "nail", + "names", + "nanny", + "napkin", + "narrate", + "nasty", + "natural", + "nautical", + "navy", + "nearby", + "necklace", + "needed", + "negative", + "neither", + "neon", + "nephew", + "nerves", + "nestle", + "network", + "neutral", + "never", + "newt", + "nexus", + "nibs", + "niche", + "niece", + "nifty", + "nightly", + "nimbly", + "nineteen", + "nirvana", + "nitrogen", + "nobody", + "nocturnal", + "nodes", + "noises", + "nomad", + "noodles", + "northern", + "nostril", + "noted", + "nouns", + "novelty", + "nowhere", + "nozzle", + "nuance", + "nucleus", + "nudged", + "nugget", + "nuisance", + "null", + "number", + "nuns", + "nurse", + "nutshell", + "nylon", + "oaks", + "oars", + "oasis", + "oatmeal", + "obedient", + "object", + "obliged", + "obnoxious", + "observant", + "obtains", + "obvious", + "occur", + "ocean", + "october", + "odds", + "odometer", + "offend", + "often", + "oilfield", + "ointment", + "okay", + "older", + "olive", + "olympics", + "omega", + "omission", + "omnibus", + "onboard", + "oncoming", + "oneself", + "ongoing", + "onion", + "online", + "onslaught", + "onto", + "onward", + "oozed", + "opacity", + "opened", + "opposite", + "optical", + "opus", + "orange", + "orbit", + "orchid", + "orders", + "organs", + "origin", + "ornament", + "orphans", + "oscar", + "ostrich", + "otherwise", + "otter", + "ouch", + "ought", + "ounce", + "ourselves", + "oust", + "outbreak", + "oval", + "oven", + "owed", + "owls", + "owner", + "oxidant", + "oxygen", + "oyster", + "ozone", + "pact", + "paddles", + "pager", + "pairing", + "palace", + "pamphlet", + "pancakes", + "paper", + "paradise", + "pastry", + "patio", + "pause", + "pavements", + "pawnshop", + "payment", + "peaches", + "pebbles", + "peculiar", + "pedantic", + "peeled", + "pegs", + "pelican", + "pencil", + "people", + "pepper", + "perfect", + "pests", + "petals", + "phase", + "pheasants", + "phone", + "phrases", + "physics", + "piano", + "picked", + "pierce", + "pigment", + "piloted", + "pimple", + "pinched", + "pioneer", + "pipeline", + "pirate", + "pistons", + "pitched", + "pivot", + "pixels", + "pizza", + "playful", + "pledge", + "pliers", + "plotting", + "plus", + "plywood", + "poaching", + "pockets", + "podcast", + "poetry", + "point", + "poker", + "polar", + "ponies", + "pool", + "popular", + "portents", + "possible", + "potato", + "pouch", + "poverty", + "powder", + "pram", + "present", + "pride", + "problems", + "pruned", + "prying", + "psychic", + "public", + "puck", + "puddle", + "puffin", + "pulp", + "pumpkins", + "punch", + "puppy", + "purged", + "push", + "putty", + "puzzled", + "pylons", + "pyramid", + "python", + "queen", + "quick", + "quote", + "rabbits", + "racetrack", + "radar", + "rafts", + "rage", + "railway", + "raking", + "rally", + "ramped", + "randomly", + "rapid", + "rarest", + "rash", + "rated", + "ravine", + "rays", + "razor", + "react", + "rebel", + "recipe", + "reduce", + "reef", + "refer", + "regular", + "reheat", + "reinvest", + "rejoices", + "rekindle", + "relic", + "remedy", + "renting", + "reorder", + "repent", + "request", + "reruns", + "rest", + "return", + "reunion", + "revamp", + "rewind", + "rhino", + "rhythm", + "ribbon", + "richly", + "ridges", + "rift", + "rigid", + "rims", + "ringing", + "riots", + "ripped", + "rising", + "ritual", + "river", + "roared", + "robot", + "rockets", + "rodent", + "rogue", + "roles", + "romance", + "roomy", + "roped", + "roster", + "rotate", + "rounded", + "rover", + "rowboat", + "royal", + "ruby", + "rudely", + "ruffled", + "rugged", + "ruined", + "ruling", + "rumble", + "runway", + "rural", + "rustled", + "ruthless", + "sabotage", + "sack", + "sadness", + "safety", + "saga", + "sailor", + "sake", + "salads", + "sample", + "sanity", + "sapling", + "sarcasm", + "sash", + "satin", + "saucepan", + "saved", + "sawmill", + "saxophone", + "sayings", + "scamper", + "scenic", + "school", + "science", + "scoop", + "scrub", + "scuba", + "seasons", + "second", + "sedan", + "seeded", + "segments", + "seismic", + "selfish", + "semifinal", + "sensible", + "september", + "sequence", + "serving", + "session", + "setup", + "seventh", + "sewage", + "shackles", + "shelter", + "shipped", + "shocking", + "shrugged", + "shuffled", + "shyness", + "siblings", + "sickness", + "sidekick", + "sieve", + "sifting", + "sighting", + "silk", + "simplest", + "sincerely", + "sipped", + "siren", + "situated", + "sixteen", + "sizes", + "skater", + "skew", + "skirting", + "skulls", + "skydive", + "slackens", + "sleepless", + "slid", + "slower", + "slug", + "smash", + "smelting", + "smidgen", + "smog", + "smuggled", + "snake", + "sneeze", + "sniff", + "snout", + "snug", + "soapy", + "sober", + "soccer", + "soda", + "software", + "soggy", + "soil", + "solved", + "somewhere", + "sonic", + "soothe", + "soprano", + "sorry", + "southern", + "sovereign", + "sowed", + "soya", + "space", + "speedy", + "sphere", + "spiders", + "splendid", + "spout", + "sprig", + "spud", + "spying", + "square", + "stacking", + "stellar", + "stick", + "stockpile", + "strained", + "stunning", + "stylishly", + "subtly", + "succeed", + "suddenly", + "suede", + "suffice", + "sugar", + "suitcase", + "sulking", + "summon", + "sunken", + "superior", + "surfer", + "sushi", + "suture", + "swagger", + "swept", + "swiftly", + "sword", + "swung", + "syllabus", + "symptoms", + "syndrome", + "syringe", + "system", + "taboo", + "tacit", + "tadpoles", + "tagged", + "tail", + "taken", + "talent", + "tamper", + "tanks", + "tapestry", + "tarnished", + "tasked", + "tattoo", + "taunts", + "tavern", + "tawny", + "taxi", + "teardrop", + "technical", + "tedious", + "teeming", + "tell", + "template", + "tender", + "tepid", + "tequila", + "terminal", + "testing", + "tether", + "textbook", + "thaw", + "theatrics", + "thirsty", + "thorn", + "threaten", + "thumbs", + "thwart", + "ticket", + "tidy", + "tiers", + "tiger", + "tilt", + "timber", + "tinted", + "tipsy", + "tirade", + "tissue", + "titans", + "toaster", + "tobacco", + "today", + "toenail", + "toffee", + "together", + "toilet", + "token", + "tolerant", + "tomorrow", + "tonic", + "toolbox", + "topic", + "torch", + "tossed", + "total", + "touchy", + "towel", + "toxic", + "toyed", + "trash", + "trendy", + "tribal", + "trolling", + "truth", + "trying", + "tsunami", + "tubes", + "tucks", + "tudor", + "tuesday", + "tufts", + "tugs", + "tuition", + "tulips", + "tumbling", + "tunnel", + "turnip", + "tusks", + "tutor", + "tuxedo", + "twang", + "tweezers", + "twice", + "twofold", + "tycoon", + "typist", + "tyrant", + "ugly", + "ulcers", + "ultimate", + "umbrella", + "umpire", + "unafraid", + "unbending", + "uncle", + "under", + "uneven", + "unfit", + "ungainly", + "unhappy", + "union", + "unjustly", + "unknown", + "unlikely", + "unmask", + "unnoticed", + "unopened", + "unplugs", + "unquoted", + "unrest", + "unsafe", + "until", + "unusual", + "unveil", + "unwind", + "unzip", + "upbeat", + "upcoming", + "update", + "upgrade", + "uphill", + "upkeep", + "upload", + "upon", + "upper", + "upright", + "upstairs", + "uptight", + "upwards", + "urban", + "urchins", + "urgent", + "usage", + "useful", + "usher", + "using", + "usual", + "utensils", + "utility", + "utmost", + "utopia", + "uttered", + "vacation", + "vague", + "vain", + "value", + "vampire", + "vane", + "vapidly", + "vary", + "vastness", + "vats", + "vaults", + "vector", + "veered", + "vegan", + "vehicle", + "vein", + "velvet", + "venomous", + "verification", + "vessel", + "veteran", + "vexed", + "vials", + "vibrate", + "victim", + "video", + "viewpoint", + "vigilant", + "viking", + "village", + "vinegar", + "violin", + "vipers", + "virtual", + "visited", + "vitals", + "vivid", + "vixen", + "vocal", + "vogue", + "voice", + "volcano", + "vortex", + "voted", + "voucher", + "vowels", + "voyage", + "vulture", + "wade", + "waffle", + "wagtail", + "waist", + "waking", + "wallets", + "wanted", + "warped", + "washing", + "water", + "waveform", + "waxing", + "wayside", + "weavers", + "website", + "wedge", + "weekday", + "weird", + "welders", + "went", + "wept", + "were", + "western", + "wetsuit", + "whale", + "when", + "whipped", + "whole", + "wickets", + "width", + "wield", + "wife", + "wiggle", + "wildly", + "winter", + "wipeout", + "wiring", + "wise", + "withdrawn", + "wives", + "wizard", + "wobbly", + "woes", + "woken", + "wolf", + "womanly", + "wonders", + "woozy", + "worry", + "wounded", + "woven", + "wrap", + "wrist", + "wrong", + "yacht", + "yahoo", + "yanks", + "yard", + "yawning", + "yearbook", + "yellow", + "yesterday", + "yeti", + "yields", + "yodel", + "yoga", + "younger", + "yoyo", + "zapped", + "zeal", + "zebra", + "zero", + "zesty", + "zigzags", + "zinger", + "zippers", + "zodiac", + "zombie", + "zones", + "zoom" + ]; +} diff --git a/cw_wownero/lib/mnemonics/french.dart b/cw_wownero/lib/mnemonics/french.dart new file mode 100644 index 000000000..76d556f6a --- /dev/null +++ b/cw_wownero/lib/mnemonics/french.dart @@ -0,0 +1,1630 @@ +class FrenchMnemonics { + static const words = [ + "abandon", + "abattre", + "aboi", + "abolir", + "aborder", + "abri", + "absence", + "absolu", + "abuser", + "acacia", + "acajou", + "accent", + "accord", + "accrocher", + "accuser", + "acerbe", + "achat", + "acheter", + "acide", + "acier", + "acquis", + "acte", + "action", + "adage", + "adepte", + "adieu", + "admettre", + "admis", + "adorer", + "adresser", + "aduler", + "affaire", + "affirmer", + "afin", + "agacer", + "agent", + "agir", + "agiter", + "agonie", + "agrafe", + "agrume", + "aider", + "aigle", + "aigre", + "aile", + "ailleurs", + "aimant", + "aimer", + "ainsi", + "aise", + "ajouter", + "alarme", + "album", + "alcool", + "alerte", + "algue", + "alibi", + "aller", + "allumer", + "alors", + "amande", + "amener", + "amie", + "amorcer", + "amour", + "ample", + "amuser", + "ananas", + "ancien", + "anglais", + "angoisse", + "animal", + "anneau", + "annoncer", + "apercevoir", + "apparence", + "appel", + "apporter", + "apprendre", + "appuyer", + "arbre", + "arcade", + "arceau", + "arche", + "ardeur", + "argent", + "argile", + "aride", + "arme", + "armure", + "arracher", + "arriver", + "article", + "asile", + "aspect", + "assaut", + "assez", + "assister", + "assurer", + "astre", + "astuce", + "atlas", + "atroce", + "attacher", + "attente", + "attirer", + "aube", + "aucun", + "audace", + "auparavant", + "auquel", + "aurore", + "aussi", + "autant", + "auteur", + "autoroute", + "autre", + "aval", + "avant", + "avec", + "avenir", + "averse", + "aveu", + "avide", + "avion", + "avis", + "avoir", + "avouer", + "avril", + "azote", + "azur", + "badge", + "bagage", + "bague", + "bain", + "baisser", + "balai", + "balcon", + "balise", + "balle", + "bambou", + "banane", + "banc", + "bandage", + "banjo", + "banlieue", + "bannir", + "banque", + "baobab", + "barbe", + "barque", + "barrer", + "bassine", + "bataille", + "bateau", + "battre", + "baver", + "bavoir", + "bazar", + "beau", + "beige", + "berger", + "besoin", + "beurre", + "biais", + "biceps", + "bidule", + "bien", + "bijou", + "bilan", + "billet", + "blanc", + "blason", + "bleu", + "bloc", + "blond", + "bocal", + "boire", + "boiserie", + "boiter", + "bonbon", + "bondir", + "bonheur", + "bordure", + "borgne", + "borner", + "bosse", + "bouche", + "bouder", + "bouger", + "boule", + "bourse", + "bout", + "boxe", + "brader", + "braise", + "branche", + "braquer", + "bras", + "brave", + "brebis", + "brevet", + "brider", + "briller", + "brin", + "brique", + "briser", + "broche", + "broder", + "bronze", + "brosser", + "brouter", + "bruit", + "brute", + "budget", + "buffet", + "bulle", + "bureau", + "buriner", + "buste", + "buter", + "butiner", + "cabas", + "cabinet", + "cabri", + "cacao", + "cacher", + "cadeau", + "cadre", + "cage", + "caisse", + "caler", + "calme", + "camarade", + "camion", + "campagne", + "canal", + "canif", + "capable", + "capot", + "carat", + "caresser", + "carie", + "carpe", + "cartel", + "casier", + "casque", + "casserole", + "cause", + "cavale", + "cave", + "ceci", + "cela", + "celui", + "cendre", + "cent", + "cependant", + "cercle", + "cerise", + "cerner", + "certes", + "cerveau", + "cesser", + "chacun", + "chair", + "chaleur", + "chamois", + "chanson", + "chaque", + "charge", + "chasse", + "chat", + "chaud", + "chef", + "chemin", + "cheveu", + "chez", + "chicane", + "chien", + "chiffre", + "chiner", + "chiot", + "chlore", + "choc", + "choix", + "chose", + "chou", + "chute", + "cibler", + "cidre", + "ciel", + "cigale", + "cinq", + "cintre", + "cirage", + "cirque", + "ciseau", + "citation", + "citer", + "citron", + "civet", + "clairon", + "clan", + "classe", + "clavier", + "clef", + "climat", + "cloche", + "cloner", + "clore", + "clos", + "clou", + "club", + "cobra", + "cocon", + "coiffer", + "coin", + "colline", + "colon", + "combat", + "comme", + "compte", + "conclure", + "conduire", + "confier", + "connu", + "conseil", + "contre", + "convenir", + "copier", + "cordial", + "cornet", + "corps", + "cosmos", + "coton", + "couche", + "coude", + "couler", + "coupure", + "cour", + "couteau", + "couvrir", + "crabe", + "crainte", + "crampe", + "cran", + "creuser", + "crever", + "crier", + "crime", + "crin", + "crise", + "crochet", + "croix", + "cruel", + "cuisine", + "cuite", + "culot", + "culte", + "cumul", + "cure", + "curieux", + "cuve", + "dame", + "danger", + "dans", + "davantage", + "debout", + "dedans", + "dehors", + "delta", + "demain", + "demeurer", + "demi", + "dense", + "dent", + "depuis", + "dernier", + "descendre", + "dessus", + "destin", + "dette", + "deuil", + "deux", + "devant", + "devenir", + "devin", + "devoir", + "dicton", + "dieu", + "difficile", + "digestion", + "digue", + "diluer", + "dimanche", + "dinde", + "diode", + "dire", + "diriger", + "discours", + "disposer", + "distance", + "divan", + "divers", + "docile", + "docteur", + "dodu", + "dogme", + "doigt", + "dominer", + "donation", + "donjon", + "donner", + "dopage", + "dorer", + "dormir", + "doseur", + "douane", + "double", + "douche", + "douleur", + "doute", + "doux", + "douzaine", + "draguer", + "drame", + "drap", + "dresser", + "droit", + "duel", + "dune", + "duper", + "durant", + "durcir", + "durer", + "eaux", + "effacer", + "effet", + "effort", + "effrayant", + "elle", + "embrasser", + "emmener", + "emparer", + "empire", + "employer", + "emporter", + "enclos", + "encore", + "endive", + "endormir", + "endroit", + "enduit", + "enfant", + "enfermer", + "enfin", + "enfler", + "enfoncer", + "enfuir", + "engager", + "engin", + "enjeu", + "enlever", + "ennemi", + "ennui", + "ensemble", + "ensuite", + "entamer", + "entendre", + "entier", + "entourer", + "entre", + "envelopper", + "envie", + "envoyer", + "erreur", + "escalier", + "espace", + "espoir", + "esprit", + "essai", + "essor", + "essuyer", + "estimer", + "exact", + "examiner", + "excuse", + "exemple", + "exiger", + "exil", + "exister", + "exode", + "expliquer", + "exposer", + "exprimer", + "extase", + "fable", + "facette", + "facile", + "fade", + "faible", + "faim", + "faire", + "fait", + "falloir", + "famille", + "faner", + "farce", + "farine", + "fatigue", + "faucon", + "faune", + "faute", + "faux", + "faveur", + "favori", + "faxer", + "feinter", + "femme", + "fendre", + "fente", + "ferme", + "festin", + "feuille", + "feutre", + "fiable", + "fibre", + "ficher", + "fier", + "figer", + "figure", + "filet", + "fille", + "filmer", + "fils", + "filtre", + "final", + "finesse", + "finir", + "fiole", + "firme", + "fixe", + "flacon", + "flair", + "flamme", + "flan", + "flaque", + "fleur", + "flocon", + "flore", + "flot", + "flou", + "fluide", + "fluor", + "flux", + "focus", + "foin", + "foire", + "foison", + "folie", + "fonction", + "fondre", + "fonte", + "force", + "forer", + "forger", + "forme", + "fort", + "fosse", + "fouet", + "fouine", + "foule", + "four", + "foyer", + "frais", + "franc", + "frapper", + "freiner", + "frimer", + "friser", + "frite", + "froid", + "froncer", + "fruit", + "fugue", + "fuir", + "fuite", + "fumer", + "fureur", + "furieux", + "fuser", + "fusil", + "futile", + "futur", + "gagner", + "gain", + "gala", + "galet", + "galop", + "gamme", + "gant", + "garage", + "garde", + "garer", + "gauche", + "gaufre", + "gaule", + "gaver", + "gazon", + "geler", + "genou", + "genre", + "gens", + "gercer", + "germer", + "geste", + "gibier", + "gicler", + "gilet", + "girafe", + "givre", + "glace", + "glisser", + "globe", + "gloire", + "gluant", + "gober", + "golf", + "gommer", + "gorge", + "gosier", + "goutte", + "grain", + "gramme", + "grand", + "gras", + "grave", + "gredin", + "griffure", + "griller", + "gris", + "gronder", + "gros", + "grotte", + "groupe", + "grue", + "guerrier", + "guetter", + "guider", + "guise", + "habiter", + "hache", + "haie", + "haine", + "halte", + "hamac", + "hanche", + "hangar", + "hanter", + "haras", + "hareng", + "harpe", + "hasard", + "hausse", + "haut", + "havre", + "herbe", + "heure", + "hibou", + "hier", + "histoire", + "hiver", + "hochet", + "homme", + "honneur", + "honte", + "horde", + "horizon", + "hormone", + "houle", + "housse", + "hublot", + "huile", + "huit", + "humain", + "humble", + "humide", + "humour", + "hurler", + "idole", + "igloo", + "ignorer", + "illusion", + "image", + "immense", + "immobile", + "imposer", + "impression", + "incapable", + "inconnu", + "index", + "indiquer", + "infime", + "injure", + "inox", + "inspirer", + "instant", + "intention", + "intime", + "inutile", + "inventer", + "inviter", + "iode", + "iris", + "issue", + "ivre", + "jade", + "jadis", + "jamais", + "jambe", + "janvier", + "jardin", + "jauge", + "jaunisse", + "jeter", + "jeton", + "jeudi", + "jeune", + "joie", + "joindre", + "joli", + "joueur", + "journal", + "judo", + "juge", + "juillet", + "juin", + "jument", + "jungle", + "jupe", + "jupon", + "jurer", + "juron", + "jury", + "jusque", + "juste", + "kayak", + "ketchup", + "kilo", + "kiwi", + "koala", + "label", + "lacet", + "lacune", + "laine", + "laisse", + "lait", + "lame", + "lancer", + "lande", + "laque", + "lard", + "largeur", + "larme", + "larve", + "lasso", + "laver", + "lendemain", + "lentement", + "lequel", + "lettre", + "leur", + "lever", + "levure", + "liane", + "libre", + "lien", + "lier", + "lieutenant", + "ligne", + "ligoter", + "liguer", + "limace", + "limer", + "limite", + "lingot", + "lion", + "lire", + "lisser", + "litre", + "livre", + "lobe", + "local", + "logis", + "loin", + "loisir", + "long", + "loque", + "lors", + "lotus", + "louer", + "loup", + "lourd", + "louve", + "loyer", + "lubie", + "lucide", + "lueur", + "luge", + "luire", + "lundi", + "lune", + "lustre", + "lutin", + "lutte", + "luxe", + "machine", + "madame", + "magie", + "magnifique", + "magot", + "maigre", + "main", + "mairie", + "maison", + "malade", + "malheur", + "malin", + "manche", + "manger", + "manier", + "manoir", + "manquer", + "marche", + "mardi", + "marge", + "mariage", + "marquer", + "mars", + "masque", + "masse", + "matin", + "mauvais", + "meilleur", + "melon", + "membre", + "menacer", + "mener", + "mensonge", + "mentir", + "menu", + "merci", + "merlu", + "mesure", + "mettre", + "meuble", + "meunier", + "meute", + "miche", + "micro", + "midi", + "miel", + "miette", + "mieux", + "milieu", + "mille", + "mimer", + "mince", + "mineur", + "ministre", + "minute", + "mirage", + "miroir", + "miser", + "mite", + "mixte", + "mobile", + "mode", + "module", + "moins", + "mois", + "moment", + "momie", + "monde", + "monsieur", + "monter", + "moquer", + "moral", + "morceau", + "mordre", + "morose", + "morse", + "mortier", + "morue", + "motif", + "motte", + "moudre", + "moule", + "mourir", + "mousse", + "mouton", + "mouvement", + "moyen", + "muer", + "muette", + "mugir", + "muguet", + "mulot", + "multiple", + "munir", + "muret", + "muse", + "musique", + "muter", + "nacre", + "nager", + "nain", + "naissance", + "narine", + "narrer", + "naseau", + "nasse", + "nation", + "nature", + "naval", + "navet", + "naviguer", + "navrer", + "neige", + "nerf", + "nerveux", + "neuf", + "neutre", + "neuve", + "neveu", + "niche", + "nier", + "niveau", + "noble", + "noce", + "nocif", + "noir", + "nomade", + "nombre", + "nommer", + "nord", + "norme", + "notaire", + "notice", + "notre", + "nouer", + "nougat", + "nourrir", + "nous", + "nouveau", + "novice", + "noyade", + "noyer", + "nuage", + "nuance", + "nuire", + "nuit", + "nulle", + "nuque", + "oasis", + "objet", + "obliger", + "obscur", + "observer", + "obtenir", + "obus", + "occasion", + "occuper", + "ocre", + "octet", + "odeur", + "odorat", + "offense", + "officier", + "offrir", + "ogive", + "oiseau", + "olive", + "ombre", + "onctueux", + "onduler", + "ongle", + "onze", + "opter", + "option", + "orageux", + "oral", + "orange", + "orbite", + "ordinaire", + "ordre", + "oreille", + "organe", + "orgie", + "orgueil", + "orient", + "origan", + "orner", + "orteil", + "ortie", + "oser", + "osselet", + "otage", + "otarie", + "ouate", + "oublier", + "ouest", + "ours", + "outil", + "outre", + "ouvert", + "ouvrir", + "ovale", + "ozone", + "pacte", + "page", + "paille", + "pain", + "paire", + "paix", + "palace", + "palissade", + "palmier", + "palpiter", + "panda", + "panneau", + "papa", + "papier", + "paquet", + "parc", + "pardi", + "parfois", + "parler", + "parmi", + "parole", + "partir", + "parvenir", + "passer", + "pastel", + "patin", + "patron", + "paume", + "pause", + "pauvre", + "paver", + "pavot", + "payer", + "pays", + "peau", + "peigne", + "peinture", + "pelage", + "pelote", + "pencher", + "pendre", + "penser", + "pente", + "percer", + "perdu", + "perle", + "permettre", + "personne", + "perte", + "peser", + "pesticide", + "petit", + "peuple", + "peur", + "phase", + "photo", + "phrase", + "piano", + "pied", + "pierre", + "pieu", + "pile", + "pilier", + "pilote", + "pilule", + "piment", + "pincer", + "pinson", + "pinte", + "pion", + "piquer", + "pirate", + "pire", + "piste", + "piton", + "pitre", + "pivot", + "pizza", + "placer", + "plage", + "plaire", + "plan", + "plaque", + "plat", + "plein", + "pleurer", + "pliage", + "plier", + "plonger", + "plot", + "pluie", + "plume", + "plus", + "pneu", + "poche", + "podium", + "poids", + "poil", + "point", + "poire", + "poison", + "poitrine", + "poivre", + "police", + "pollen", + "pomme", + "pompier", + "poncer", + "pondre", + "pont", + "portion", + "poser", + "position", + "possible", + "poste", + "potage", + "potin", + "pouce", + "poudre", + "poulet", + "poumon", + "poupe", + "pour", + "pousser", + "poutre", + "pouvoir", + "prairie", + "premier", + "prendre", + "presque", + "preuve", + "prier", + "primeur", + "prince", + "prison", + "priver", + "prix", + "prochain", + "produire", + "profond", + "proie", + "projet", + "promener", + "prononcer", + "propre", + "prose", + "prouver", + "prune", + "public", + "puce", + "pudeur", + "puiser", + "pull", + "pulpe", + "puma", + "punir", + "purge", + "putois", + "quand", + "quartier", + "quasi", + "quatre", + "quel", + "question", + "queue", + "quiche", + "quille", + "quinze", + "quitter", + "quoi", + "rabais", + "raboter", + "race", + "racheter", + "racine", + "racler", + "raconter", + "radar", + "radio", + "rafale", + "rage", + "ragot", + "raideur", + "raie", + "rail", + "raison", + "ramasser", + "ramener", + "rampe", + "rance", + "rang", + "rapace", + "rapide", + "rapport", + "rarement", + "rasage", + "raser", + "rasoir", + "rassurer", + "rater", + "ratio", + "rature", + "ravage", + "ravir", + "rayer", + "rayon", + "rebond", + "recevoir", + "recherche", + "record", + "reculer", + "redevenir", + "refuser", + "regard", + "regretter", + "rein", + "rejeter", + "rejoindre", + "relation", + "relever", + "religion", + "remarquer", + "remettre", + "remise", + "remonter", + "remplir", + "remuer", + "rencontre", + "rendre", + "renier", + "renoncer", + "rentrer", + "renverser", + "repas", + "repli", + "reposer", + "reproche", + "requin", + "respect", + "ressembler", + "reste", + "retard", + "retenir", + "retirer", + "retour", + "retrouver", + "revenir", + "revoir", + "revue", + "rhume", + "ricaner", + "riche", + "rideau", + "ridicule", + "rien", + "rigide", + "rincer", + "rire", + "risquer", + "rituel", + "rivage", + "rive", + "robe", + "robot", + "robuste", + "rocade", + "roche", + "rodeur", + "rogner", + "roman", + "rompre", + "ronce", + "rondeur", + "ronger", + "roque", + "rose", + "rosir", + "rotation", + "rotule", + "roue", + "rouge", + "rouler", + "route", + "ruban", + "rubis", + "ruche", + "rude", + "ruelle", + "ruer", + "rugby", + "rugir", + "ruine", + "rumeur", + "rural", + "ruse", + "rustre", + "sable", + "sabot", + "sabre", + "sacre", + "sage", + "saint", + "saisir", + "salade", + "salive", + "salle", + "salon", + "salto", + "salut", + "salve", + "samba", + "sandale", + "sanguin", + "sapin", + "sarcasme", + "satisfaire", + "sauce", + "sauf", + "sauge", + "saule", + "sauna", + "sauter", + "sauver", + "savoir", + "science", + "scoop", + "score", + "second", + "secret", + "secte", + "seigneur", + "sein", + "seize", + "selle", + "selon", + "semaine", + "sembler", + "semer", + "semis", + "sensuel", + "sentir", + "sept", + "serpe", + "serrer", + "sertir", + "service", + "seuil", + "seulement", + "short", + "sien", + "sigle", + "signal", + "silence", + "silo", + "simple", + "singe", + "sinon", + "sinus", + "sioux", + "sirop", + "site", + "situation", + "skier", + "snob", + "sobre", + "social", + "socle", + "sodium", + "soigner", + "soir", + "soixante", + "soja", + "solaire", + "soldat", + "soleil", + "solide", + "solo", + "solvant", + "sombre", + "somme", + "somnoler", + "sondage", + "songeur", + "sonner", + "sorte", + "sosie", + "sottise", + "souci", + "soudain", + "souffrir", + "souhaiter", + "soulever", + "soumettre", + "soupe", + "sourd", + "soustraire", + "soutenir", + "souvent", + "soyeux", + "spectacle", + "sport", + "stade", + "stagiaire", + "stand", + "star", + "statue", + "stock", + "stop", + "store", + "style", + "suave", + "subir", + "sucre", + "suer", + "suffire", + "suie", + "suite", + "suivre", + "sujet", + "sulfite", + "supposer", + "surf", + "surprendre", + "surtout", + "surveiller", + "tabac", + "table", + "tabou", + "tache", + "tacler", + "tacot", + "tact", + "taie", + "taille", + "taire", + "talon", + "talus", + "tandis", + "tango", + "tanin", + "tant", + "taper", + "tapis", + "tard", + "tarif", + "tarot", + "tarte", + "tasse", + "taureau", + "taux", + "taverne", + "taxer", + "taxi", + "tellement", + "temple", + "tendre", + "tenir", + "tenter", + "tenu", + "terme", + "ternir", + "terre", + "test", + "texte", + "thym", + "tibia", + "tiers", + "tige", + "tipi", + "tique", + "tirer", + "tissu", + "titre", + "toast", + "toge", + "toile", + "toiser", + "toiture", + "tomber", + "tome", + "tonne", + "tonte", + "toque", + "torse", + "tortue", + "totem", + "toucher", + "toujours", + "tour", + "tousser", + "tout", + "toux", + "trace", + "train", + "trame", + "tranquille", + "travail", + "trembler", + "trente", + "tribu", + "trier", + "trio", + "tripe", + "triste", + "troc", + "trois", + "tromper", + "tronc", + "trop", + "trotter", + "trouer", + "truc", + "truite", + "tuba", + "tuer", + "tuile", + "turbo", + "tutu", + "tuyau", + "type", + "union", + "unique", + "unir", + "unisson", + "untel", + "urne", + "usage", + "user", + "usiner", + "usure", + "utile", + "vache", + "vague", + "vaincre", + "valeur", + "valoir", + "valser", + "valve", + "vampire", + "vaseux", + "vaste", + "veau", + "veille", + "veine", + "velours", + "velu", + "vendre", + "venir", + "vent", + "venue", + "verbe", + "verdict", + "version", + "vertige", + "verve", + "veste", + "veto", + "vexer", + "vice", + "victime", + "vide", + "vieil", + "vieux", + "vigie", + "vigne", + "ville", + "vingt", + "violent", + "virer", + "virus", + "visage", + "viser", + "visite", + "visuel", + "vitamine", + "vitrine", + "vivant", + "vivre", + "vocal", + "vodka", + "vogue", + "voici", + "voile", + "voir", + "voisin", + "voiture", + "volaille", + "volcan", + "voler", + "volt", + "votant", + "votre", + "vouer", + "vouloir", + "vous", + "voyage", + "voyou", + "vrac", + "vrai", + "yacht", + "yeti", + "yeux", + "yoga", + "zeste", + "zinc", + "zone", + "zoom" + ]; +} \ No newline at end of file diff --git a/cw_wownero/lib/mnemonics/german.dart b/cw_wownero/lib/mnemonics/german.dart new file mode 100644 index 000000000..1491c9b0e --- /dev/null +++ b/cw_wownero/lib/mnemonics/german.dart @@ -0,0 +1,1630 @@ +class GermanMnemonics { + static const words = [ + "Abakus", + "Abart", + "abbilden", + "Abbruch", + "Abdrift", + "Abendrot", + "Abfahrt", + "abfeuern", + "Abflug", + "abfragen", + "Abglanz", + "abhärten", + "abheben", + "Abhilfe", + "Abitur", + "Abkehr", + "Ablauf", + "ablecken", + "Ablösung", + "Abnehmer", + "abnutzen", + "Abonnent", + "Abrasion", + "Abrede", + "abrüsten", + "Absicht", + "Absprung", + "Abstand", + "absuchen", + "Abteil", + "Abundanz", + "abwarten", + "Abwurf", + "Abzug", + "Achse", + "Achtung", + "Acker", + "Aderlass", + "Adler", + "Admiral", + "Adresse", + "Affe", + "Affront", + "Afrika", + "Aggregat", + "Agilität", + "ähneln", + "Ahnung", + "Ahorn", + "Akazie", + "Akkord", + "Akrobat", + "Aktfoto", + "Aktivist", + "Albatros", + "Alchimie", + "Alemanne", + "Alibi", + "Alkohol", + "Allee", + "Allüre", + "Almosen", + "Almweide", + "Aloe", + "Alpaka", + "Alpental", + "Alphabet", + "Alpinist", + "Alraune", + "Altbier", + "Alter", + "Altflöte", + "Altruist", + "Alublech", + "Aludose", + "Amateur", + "Amazonas", + "Ameise", + "Amnesie", + "Amok", + "Ampel", + "Amphibie", + "Ampulle", + "Amsel", + "Amulett", + "Anakonda", + "Analogie", + "Ananas", + "Anarchie", + "Anatomie", + "Anbau", + "Anbeginn", + "anbieten", + "Anblick", + "ändern", + "andocken", + "Andrang", + "anecken", + "Anflug", + "Anfrage", + "Anführer", + "Angebot", + "Angler", + "Anhalter", + "Anhöhe", + "Animator", + "Anis", + "Anker", + "ankleben", + "Ankunft", + "Anlage", + "anlocken", + "Anmut", + "Annahme", + "Anomalie", + "Anonymus", + "Anorak", + "anpeilen", + "Anrecht", + "Anruf", + "Ansage", + "Anschein", + "Ansicht", + "Ansporn", + "Anteil", + "Antlitz", + "Antrag", + "Antwort", + "Anwohner", + "Aorta", + "Apfel", + "Appetit", + "Applaus", + "Aquarium", + "Arbeit", + "Arche", + "Argument", + "Arktis", + "Armband", + "Aroma", + "Asche", + "Askese", + "Asphalt", + "Asteroid", + "Ästhetik", + "Astronom", + "Atelier", + "Athlet", + "Atlantik", + "Atmung", + "Audienz", + "aufatmen", + "Auffahrt", + "aufholen", + "aufregen", + "Aufsatz", + "Auftritt", + "Aufwand", + "Augapfel", + "Auktion", + "Ausbruch", + "Ausflug", + "Ausgabe", + "Aushilfe", + "Ausland", + "Ausnahme", + "Aussage", + "Autobahn", + "Avocado", + "Axthieb", + "Bach", + "backen", + "Badesee", + "Bahnhof", + "Balance", + "Balkon", + "Ballett", + "Balsam", + "Banane", + "Bandage", + "Bankett", + "Barbar", + "Barde", + "Barett", + "Bargeld", + "Barkasse", + "Barriere", + "Bart", + "Bass", + "Bastler", + "Batterie", + "Bauch", + "Bauer", + "Bauholz", + "Baujahr", + "Baum", + "Baustahl", + "Bauteil", + "Bauweise", + "Bazar", + "beachten", + "Beatmung", + "beben", + "Becher", + "Becken", + "bedanken", + "beeilen", + "beenden", + "Beere", + "befinden", + "Befreier", + "Begabung", + "Begierde", + "begrüßen", + "Beiboot", + "Beichte", + "Beifall", + "Beigabe", + "Beil", + "Beispiel", + "Beitrag", + "beizen", + "bekommen", + "beladen", + "Beleg", + "bellen", + "belohnen", + "Bemalung", + "Bengel", + "Benutzer", + "Benzin", + "beraten", + "Bereich", + "Bergluft", + "Bericht", + "Bescheid", + "Besitz", + "besorgen", + "Bestand", + "Besuch", + "betanken", + "beten", + "betören", + "Bett", + "Beule", + "Beute", + "Bewegung", + "bewirken", + "Bewohner", + "bezahlen", + "Bezug", + "biegen", + "Biene", + "Bierzelt", + "bieten", + "Bikini", + "Bildung", + "Billard", + "binden", + "Biobauer", + "Biologe", + "Bionik", + "Biotop", + "Birke", + "Bison", + "Bitte", + "Biwak", + "Bizeps", + "blasen", + "Blatt", + "Blauwal", + "Blende", + "Blick", + "Blitz", + "Blockade", + "Blödelei", + "Blondine", + "Blues", + "Blume", + "Blut", + "Bodensee", + "Bogen", + "Boje", + "Bollwerk", + "Bonbon", + "Bonus", + "Boot", + "Bordarzt", + "Börse", + "Böschung", + "Boudoir", + "Boxkampf", + "Boykott", + "Brahms", + "Brandung", + "Brauerei", + "Brecher", + "Breitaxt", + "Bremse", + "brennen", + "Brett", + "Brief", + "Brigade", + "Brillanz", + "bringen", + "brodeln", + "Brosche", + "Brötchen", + "Brücke", + "Brunnen", + "Brüste", + "Brutofen", + "Buch", + "Büffel", + "Bugwelle", + "Bühne", + "Buletten", + "Bullauge", + "Bumerang", + "bummeln", + "Buntglas", + "Bürde", + "Burgherr", + "Bursche", + "Busen", + "Buslinie", + "Bussard", + "Butangas", + "Butter", + "Cabrio", + "campen", + "Captain", + "Cartoon", + "Cello", + "Chalet", + "Charisma", + "Chefarzt", + "Chiffon", + "Chipsatz", + "Chirurg", + "Chor", + "Chronik", + "Chuzpe", + "Clubhaus", + "Cockpit", + "Codewort", + "Cognac", + "Coladose", + "Computer", + "Coupon", + "Cousin", + "Cracking", + "Crash", + "Curry", + "Dach", + "Dackel", + "daddeln", + "daliegen", + "Dame", + "Dammbau", + "Dämon", + "Dampflok", + "Dank", + "Darm", + "Datei", + "Datsche", + "Datteln", + "Datum", + "Dauer", + "Daunen", + "Deckel", + "Decoder", + "Defekt", + "Degen", + "Dehnung", + "Deiche", + "Dekade", + "Dekor", + "Delfin", + "Demut", + "denken", + "Deponie", + "Design", + "Desktop", + "Dessert", + "Detail", + "Detektiv", + "Dezibel", + "Diadem", + "Diagnose", + "Dialekt", + "Diamant", + "Dichter", + "Dickicht", + "Diesel", + "Diktat", + "Diplom", + "Direktor", + "Dirne", + "Diskurs", + "Distanz", + "Docht", + "Dohle", + "Dolch", + "Domäne", + "Donner", + "Dorade", + "Dorf", + "Dörrobst", + "Dorsch", + "Dossier", + "Dozent", + "Drachen", + "Draht", + "Drama", + "Drang", + "Drehbuch", + "Dreieck", + "Dressur", + "Drittel", + "Drossel", + "Druck", + "Duell", + "Duft", + "Düne", + "Dünung", + "dürfen", + "Duschbad", + "Düsenjet", + "Dynamik", + "Ebbe", + "Echolot", + "Echse", + "Eckball", + "Edding", + "Edelweiß", + "Eden", + "Edition", + "Efeu", + "Effekte", + "Egoismus", + "Ehre", + "Eiablage", + "Eiche", + "Eidechse", + "Eidotter", + "Eierkopf", + "Eigelb", + "Eiland", + "Eilbote", + "Eimer", + "einatmen", + "Einband", + "Eindruck", + "Einfall", + "Eingang", + "Einkauf", + "einladen", + "Einöde", + "Einrad", + "Eintopf", + "Einwurf", + "Einzug", + "Eisbär", + "Eisen", + "Eishöhle", + "Eismeer", + "Eiweiß", + "Ekstase", + "Elan", + "Elch", + "Elefant", + "Eleganz", + "Element", + "Elfe", + "Elite", + "Elixier", + "Ellbogen", + "Eloquenz", + "Emigrant", + "Emission", + "Emotion", + "Empathie", + "Empfang", + "Endzeit", + "Energie", + "Engpass", + "Enkel", + "Enklave", + "Ente", + "entheben", + "Entität", + "entladen", + "Entwurf", + "Episode", + "Epoche", + "erachten", + "Erbauer", + "erblühen", + "Erdbeere", + "Erde", + "Erdgas", + "Erdkunde", + "Erdnuss", + "Erdöl", + "Erdteil", + "Ereignis", + "Eremit", + "erfahren", + "Erfolg", + "erfreuen", + "erfüllen", + "Ergebnis", + "erhitzen", + "erkalten", + "erkennen", + "erleben", + "Erlösung", + "ernähren", + "erneuern", + "Ernte", + "Eroberer", + "eröffnen", + "Erosion", + "Erotik", + "Erpel", + "erraten", + "Erreger", + "erröten", + "Ersatz", + "Erstflug", + "Ertrag", + "Eruption", + "erwarten", + "erwidern", + "Erzbau", + "Erzeuger", + "erziehen", + "Esel", + "Eskimo", + "Eskorte", + "Espe", + "Espresso", + "essen", + "Etage", + "Etappe", + "Etat", + "Ethik", + "Etikett", + "Etüde", + "Eule", + "Euphorie", + "Europa", + "Everest", + "Examen", + "Exil", + "Exodus", + "Extrakt", + "Fabel", + "Fabrik", + "Fachmann", + "Fackel", + "Faden", + "Fagott", + "Fahne", + "Faible", + "Fairness", + "Fakt", + "Fakultät", + "Falke", + "Fallobst", + "Fälscher", + "Faltboot", + "Familie", + "Fanclub", + "Fanfare", + "Fangarm", + "Fantasie", + "Farbe", + "Farmhaus", + "Farn", + "Fasan", + "Faser", + "Fassung", + "fasten", + "Faulheit", + "Fauna", + "Faust", + "Favorit", + "Faxgerät", + "Fazit", + "fechten", + "Federboa", + "Fehler", + "Feier", + "Feige", + "feilen", + "Feinripp", + "Feldbett", + "Felge", + "Fellpony", + "Felswand", + "Ferien", + "Ferkel", + "Fernweh", + "Ferse", + "Fest", + "Fettnapf", + "Feuer", + "Fiasko", + "Fichte", + "Fiktion", + "Film", + "Filter", + "Filz", + "Finanzen", + "Findling", + "Finger", + "Fink", + "Finnwal", + "Fisch", + "Fitness", + "Fixpunkt", + "Fixstern", + "Fjord", + "Flachbau", + "Flagge", + "Flamenco", + "Flanke", + "Flasche", + "Flaute", + "Fleck", + "Flegel", + "flehen", + "Fleisch", + "fliegen", + "Flinte", + "Flirt", + "Flocke", + "Floh", + "Floskel", + "Floß", + "Flöte", + "Flugzeug", + "Flunder", + "Flusstal", + "Flutung", + "Fockmast", + "Fohlen", + "Föhnlage", + "Fokus", + "folgen", + "Foliant", + "Folklore", + "Fontäne", + "Förde", + "Forelle", + "Format", + "Forscher", + "Fortgang", + "Forum", + "Fotograf", + "Frachter", + "Fragment", + "Fraktion", + "fräsen", + "Frauenpo", + "Freak", + "Fregatte", + "Freiheit", + "Freude", + "Frieden", + "Frohsinn", + "Frosch", + "Frucht", + "Frühjahr", + "Fuchs", + "Fügung", + "fühlen", + "Füller", + "Fundbüro", + "Funkboje", + "Funzel", + "Furnier", + "Fürsorge", + "Fusel", + "Fußbad", + "Futteral", + "Gabelung", + "gackern", + "Gage", + "gähnen", + "Galaxie", + "Galeere", + "Galopp", + "Gameboy", + "Gamsbart", + "Gandhi", + "Gang", + "Garage", + "Gardine", + "Garküche", + "Garten", + "Gasthaus", + "Gattung", + "gaukeln", + "Gazelle", + "Gebäck", + "Gebirge", + "Gebräu", + "Geburt", + "Gedanke", + "Gedeck", + "Gedicht", + "Gefahr", + "Gefieder", + "Geflügel", + "Gefühl", + "Gegend", + "Gehirn", + "Gehöft", + "Gehweg", + "Geige", + "Geist", + "Gelage", + "Geld", + "Gelenk", + "Gelübde", + "Gemälde", + "Gemeinde", + "Gemüse", + "genesen", + "Genuss", + "Gepäck", + "Geranie", + "Gericht", + "Germane", + "Geruch", + "Gesang", + "Geschenk", + "Gesetz", + "Gesindel", + "Gesöff", + "Gespan", + "Gestade", + "Gesuch", + "Getier", + "Getränk", + "Getümmel", + "Gewand", + "Geweih", + "Gewitter", + "Gewölbe", + "Geysir", + "Giftzahn", + "Gipfel", + "Giraffe", + "Gitarre", + "glänzen", + "Glasauge", + "Glatze", + "Gleis", + "Globus", + "Glück", + "glühen", + "Glutofen", + "Goldzahn", + "Gondel", + "gönnen", + "Gottheit", + "graben", + "Grafik", + "Grashalm", + "Graugans", + "greifen", + "Grenze", + "grillen", + "Groschen", + "Grotte", + "Grube", + "Grünalge", + "Gruppe", + "gruseln", + "Gulasch", + "Gummibär", + "Gurgel", + "Gürtel", + "Güterzug", + "Haarband", + "Habicht", + "hacken", + "hadern", + "Hafen", + "Hagel", + "Hähnchen", + "Haifisch", + "Haken", + "Halbaffe", + "Halsader", + "halten", + "Halunke", + "Handbuch", + "Hanf", + "Harfe", + "Harnisch", + "härten", + "Harz", + "Hasenohr", + "Haube", + "hauchen", + "Haupt", + "Haut", + "Havarie", + "Hebamme", + "hecheln", + "Heck", + "Hedonist", + "Heiler", + "Heimat", + "Heizung", + "Hektik", + "Held", + "helfen", + "Helium", + "Hemd", + "hemmen", + "Hengst", + "Herd", + "Hering", + "Herkunft", + "Hermelin", + "Herrchen", + "Herzdame", + "Heulboje", + "Hexe", + "Hilfe", + "Himbeere", + "Himmel", + "Hingabe", + "hinhören", + "Hinweis", + "Hirsch", + "Hirte", + "Hitzkopf", + "Hobel", + "Hochform", + "Hocker", + "hoffen", + "Hofhund", + "Hofnarr", + "Höhenzug", + "Hohlraum", + "Hölle", + "Holzboot", + "Honig", + "Honorar", + "horchen", + "Hörprobe", + "Höschen", + "Hotel", + "Hubraum", + "Hufeisen", + "Hügel", + "huldigen", + "Hülle", + "Humbug", + "Hummer", + "Humor", + "Hund", + "Hunger", + "Hupe", + "Hürde", + "Hurrikan", + "Hydrant", + "Hypnose", + "Ibis", + "Idee", + "Idiot", + "Igel", + "Illusion", + "Imitat", + "impfen", + "Import", + "Inferno", + "Ingwer", + "Inhalte", + "Inland", + "Insekt", + "Ironie", + "Irrfahrt", + "Irrtum", + "Isolator", + "Istwert", + "Jacke", + "Jade", + "Jagdhund", + "Jäger", + "Jaguar", + "Jahr", + "Jähzorn", + "Jazzfest", + "Jetpilot", + "jobben", + "Jochbein", + "jodeln", + "Jodsalz", + "Jolle", + "Journal", + "Jubel", + "Junge", + "Junimond", + "Jupiter", + "Jutesack", + "Juwel", + "Kabarett", + "Kabine", + "Kabuff", + "Käfer", + "Kaffee", + "Kahlkopf", + "Kaimauer", + "Kajüte", + "Kaktus", + "Kaliber", + "Kaltluft", + "Kamel", + "kämmen", + "Kampagne", + "Kanal", + "Känguru", + "Kanister", + "Kanone", + "Kante", + "Kanu", + "kapern", + "Kapitän", + "Kapuze", + "Karneval", + "Karotte", + "Käsebrot", + "Kasper", + "Kastanie", + "Katalog", + "Kathode", + "Katze", + "kaufen", + "Kaugummi", + "Kauz", + "Kehle", + "Keilerei", + "Keksdose", + "Kellner", + "Keramik", + "Kerze", + "Kessel", + "Kette", + "keuchen", + "kichern", + "Kielboot", + "Kindheit", + "Kinnbart", + "Kinosaal", + "Kiosk", + "Kissen", + "Klammer", + "Klang", + "Klapprad", + "Klartext", + "kleben", + "Klee", + "Kleinod", + "Klima", + "Klingel", + "Klippe", + "Klischee", + "Kloster", + "Klugheit", + "Klüngel", + "kneten", + "Knie", + "Knöchel", + "knüpfen", + "Kobold", + "Kochbuch", + "Kohlrabi", + "Koje", + "Kokosöl", + "Kolibri", + "Kolumne", + "Kombüse", + "Komiker", + "kommen", + "Konto", + "Konzept", + "Kopfkino", + "Kordhose", + "Korken", + "Korsett", + "Kosename", + "Krabbe", + "Krach", + "Kraft", + "Krähe", + "Kralle", + "Krapfen", + "Krater", + "kraulen", + "Kreuz", + "Krokodil", + "Kröte", + "Kugel", + "Kuhhirt", + "Kühnheit", + "Künstler", + "Kurort", + "Kurve", + "Kurzfilm", + "kuscheln", + "küssen", + "Kutter", + "Labor", + "lachen", + "Lackaffe", + "Ladeluke", + "Lagune", + "Laib", + "Lakritze", + "Lammfell", + "Land", + "Langmut", + "Lappalie", + "Last", + "Laterne", + "Latzhose", + "Laubsäge", + "laufen", + "Laune", + "Lausbub", + "Lavasee", + "Leben", + "Leder", + "Leerlauf", + "Lehm", + "Lehrer", + "leihen", + "Lektüre", + "Lenker", + "Lerche", + "Leseecke", + "Leuchter", + "Lexikon", + "Libelle", + "Libido", + "Licht", + "Liebe", + "liefern", + "Liftboy", + "Limonade", + "Lineal", + "Linoleum", + "List", + "Liveband", + "Lobrede", + "locken", + "Löffel", + "Logbuch", + "Logik", + "Lohn", + "Loipe", + "Lokal", + "Lorbeer", + "Lösung", + "löten", + "Lottofee", + "Löwe", + "Luchs", + "Luder", + "Luftpost", + "Luke", + "Lümmel", + "Lunge", + "lutschen", + "Luxus", + "Macht", + "Magazin", + "Magier", + "Magnet", + "mähen", + "Mahlzeit", + "Mahnmal", + "Maibaum", + "Maisbrei", + "Makel", + "malen", + "Mammut", + "Maniküre", + "Mantel", + "Marathon", + "Marder", + "Marine", + "Marke", + "Marmor", + "Märzluft", + "Maske", + "Maßanzug", + "Maßkrug", + "Mastkorb", + "Material", + "Matratze", + "Mauerbau", + "Maulkorb", + "Mäuschen", + "Mäzen", + "Medium", + "Meinung", + "melden", + "Melodie", + "Mensch", + "Merkmal", + "Messe", + "Metall", + "Meteor", + "Methode", + "Metzger", + "Mieze", + "Milchkuh", + "Mimose", + "Minirock", + "Minute", + "mischen", + "Missetat", + "mitgehen", + "Mittag", + "Mixtape", + "Möbel", + "Modul", + "mögen", + "Möhre", + "Molch", + "Moment", + "Monat", + "Mondflug", + "Monitor", + "Monokini", + "Monster", + "Monument", + "Moorhuhn", + "Moos", + "Möpse", + "Moral", + "Mörtel", + "Motiv", + "Motorrad", + "Möwe", + "Mühe", + "Mulatte", + "Müller", + "Mumie", + "Mund", + "Münze", + "Muschel", + "Muster", + "Mythos", + "Nabel", + "Nachtzug", + "Nackedei", + "Nagel", + "Nähe", + "Nähnadel", + "Namen", + "Narbe", + "Narwal", + "Nasenbär", + "Natur", + "Nebel", + "necken", + "Neffe", + "Neigung", + "Nektar", + "Nenner", + "Neptun", + "Nerz", + "Nessel", + "Nestbau", + "Netz", + "Neubau", + "Neuerung", + "Neugier", + "nicken", + "Niere", + "Nilpferd", + "nisten", + "Nocke", + "Nomade", + "Nordmeer", + "Notdurft", + "Notstand", + "Notwehr", + "Nudismus", + "Nuss", + "Nutzhanf", + "Oase", + "Obdach", + "Oberarzt", + "Objekt", + "Oboe", + "Obsthain", + "Ochse", + "Odyssee", + "Ofenholz", + "öffnen", + "Ohnmacht", + "Ohrfeige", + "Ohrwurm", + "Ökologie", + "Oktave", + "Ölberg", + "Olive", + "Ölkrise", + "Omelett", + "Onkel", + "Oper", + "Optiker", + "Orange", + "Orchidee", + "ordnen", + "Orgasmus", + "Orkan", + "Ortskern", + "Ortung", + "Ostasien", + "Ozean", + "Paarlauf", + "Packeis", + "paddeln", + "Paket", + "Palast", + "Pandabär", + "Panik", + "Panorama", + "Panther", + "Papagei", + "Papier", + "Paprika", + "Paradies", + "Parka", + "Parodie", + "Partner", + "Passant", + "Patent", + "Patzer", + "Pause", + "Pavian", + "Pedal", + "Pegel", + "peilen", + "Perle", + "Person", + "Pfad", + "Pfau", + "Pferd", + "Pfleger", + "Physik", + "Pier", + "Pilotwal", + "Pinzette", + "Piste", + "Plakat", + "Plankton", + "Platin", + "Plombe", + "plündern", + "Pobacke", + "Pokal", + "polieren", + "Popmusik", + "Porträt", + "Posaune", + "Postamt", + "Pottwal", + "Pracht", + "Pranke", + "Preis", + "Primat", + "Prinzip", + "Protest", + "Proviant", + "Prüfung", + "Pubertät", + "Pudding", + "Pullover", + "Pulsader", + "Punkt", + "Pute", + "Putsch", + "Puzzle", + "Python", + "quaken", + "Qualle", + "Quark", + "Quellsee", + "Querkopf", + "Quitte", + "Quote", + "Rabauke", + "Rache", + "Radclub", + "Radhose", + "Radio", + "Radtour", + "Rahmen", + "Rampe", + "Randlage", + "Ranzen", + "Rapsöl", + "Raserei", + "rasten", + "Rasur", + "Rätsel", + "Raubtier", + "Raumzeit", + "Rausch", + "Reaktor", + "Realität", + "Rebell", + "Rede", + "Reetdach", + "Regatta", + "Regen", + "Rehkitz", + "Reifen", + "Reim", + "Reise", + "Reizung", + "Rekord", + "Relevanz", + "Rennboot", + "Respekt", + "Restmüll", + "retten", + "Reue", + "Revolte", + "Rhetorik", + "Rhythmus", + "Richtung", + "Riegel", + "Rindvieh", + "Rippchen", + "Ritter", + "Robbe", + "Roboter", + "Rockband", + "Rohdaten", + "Roller", + "Roman", + "röntgen", + "Rose", + "Rosskur", + "Rost", + "Rotahorn", + "Rotglut", + "Rotznase", + "Rubrik", + "Rückweg", + "Rufmord", + "Ruhe", + "Ruine", + "Rumpf", + "Runde", + "Rüstung", + "rütteln", + "Saaltür", + "Saatguts", + "Säbel", + "Sachbuch", + "Sack", + "Saft", + "sagen", + "Sahneeis", + "Salat", + "Salbe", + "Salz", + "Sammlung", + "Samt", + "Sandbank", + "Sanftmut", + "Sardine", + "Satire", + "Sattel", + "Satzbau", + "Sauerei", + "Saum", + "Säure", + "Schall", + "Scheitel", + "Schiff", + "Schlager", + "Schmied", + "Schnee", + "Scholle", + "Schrank", + "Schulbus", + "Schwan", + "Seeadler", + "Seefahrt", + "Seehund", + "Seeufer", + "segeln", + "Sehnerv", + "Seide", + "Seilzug", + "Senf", + "Sessel", + "Seufzer", + "Sexgott", + "Sichtung", + "Signal", + "Silber", + "singen", + "Sinn", + "Sirup", + "Sitzbank", + "Skandal", + "Skikurs", + "Skipper", + "Skizze", + "Smaragd", + "Socke", + "Sohn", + "Sommer", + "Songtext", + "Sorte", + "Spagat", + "Spannung", + "Spargel", + "Specht", + "Speiseöl", + "Spiegel", + "Sport", + "spülen", + "Stadtbus", + "Stall", + "Stärke", + "Stativ", + "staunen", + "Stern", + "Stiftung", + "Stollen", + "Strömung", + "Sturm", + "Substanz", + "Südalpen", + "Sumpf", + "surfen", + "Tabak", + "Tafel", + "Tagebau", + "takeln", + "Taktung", + "Talsohle", + "Tand", + "Tanzbär", + "Tapir", + "Tarantel", + "Tarnname", + "Tasse", + "Tatnacht", + "Tatsache", + "Tatze", + "Taube", + "tauchen", + "Taufpate", + "Taumel", + "Teelicht", + "Teich", + "teilen", + "Tempo", + "Tenor", + "Terrasse", + "Testflug", + "Theater", + "Thermik", + "ticken", + "Tiefflug", + "Tierart", + "Tigerhai", + "Tinte", + "Tischler", + "toben", + "Toleranz", + "Tölpel", + "Tonband", + "Topf", + "Topmodel", + "Torbogen", + "Torlinie", + "Torte", + "Tourist", + "Tragesel", + "trampeln", + "Trapez", + "Traum", + "treffen", + "Trennung", + "Treue", + "Trick", + "trimmen", + "Trödel", + "Trost", + "Trumpf", + "tüfteln", + "Turban", + "Turm", + "Übermut", + "Ufer", + "Uhrwerk", + "umarmen", + "Umbau", + "Umfeld", + "Umgang", + "Umsturz", + "Unart", + "Unfug", + "Unimog", + "Unruhe", + "Unwucht", + "Uranerz", + "Urlaub", + "Urmensch", + "Utopie", + "Vakuum", + "Valuta", + "Vandale", + "Vase", + "Vektor", + "Ventil", + "Verb", + "Verdeck", + "Verfall", + "Vergaser", + "verhexen", + "Verlag", + "Vers", + "Vesper", + "Vieh", + "Viereck", + "Vinyl", + "Virus", + "Vitrine", + "Vollblut", + "Vorbote", + "Vorrat", + "Vorsicht", + "Vulkan", + "Wachstum", + "Wade", + "Wagemut", + "Wahlen", + "Wahrheit", + "Wald", + "Walhai", + "Wallach", + "Walnuss", + "Walzer", + "wandeln", + "Wanze", + "wärmen", + "Warnruf", + "Wäsche", + "Wasser", + "Weberei", + "wechseln", + "Wegegeld", + "wehren", + "Weiher", + "Weinglas", + "Weißbier", + "Weitwurf", + "Welle", + "Weltall", + "Werkbank", + "Werwolf", + "Wetter", + "wiehern", + "Wildgans", + "Wind", + "Wohl", + "Wohnort", + "Wolf", + "Wollust", + "Wortlaut", + "Wrack", + "Wunder", + "Wurfaxt", + "Wurst", + "Yacht", + "Yeti", + "Zacke", + "Zahl", + "zähmen", + "Zahnfee", + "Zäpfchen", + "Zaster", + "Zaumzeug", + "Zebra", + "zeigen", + "Zeitlupe", + "Zellkern", + "Zeltdach", + "Zensor", + "Zerfall", + "Zeug", + "Ziege", + "Zielfoto", + "Zimteis", + "Zobel", + "Zollhund", + "Zombie", + "Zöpfe", + "Zucht", + "Zufahrt", + "Zugfahrt", + "Zugvogel", + "Zündung", + "Zweck", + "Zyklop" + ]; +} \ No newline at end of file diff --git a/cw_wownero/lib/mnemonics/italian.dart b/cw_wownero/lib/mnemonics/italian.dart new file mode 100644 index 000000000..275f85bf4 --- /dev/null +++ b/cw_wownero/lib/mnemonics/italian.dart @@ -0,0 +1,1630 @@ +class ItalianMnemonics { + static const words = [ + "abbinare", + "abbonato", + "abisso", + "abitare", + "abominio", + "accadere", + "accesso", + "acciaio", + "accordo", + "accumulo", + "acido", + "acqua", + "acrobata", + "acustico", + "adattare", + "addetto", + "addio", + "addome", + "adeguato", + "aderire", + "adorare", + "adottare", + "adozione", + "adulto", + "aereo", + "aerobica", + "affare", + "affetto", + "affidare", + "affogato", + "affronto", + "africano", + "afrodite", + "agenzia", + "aggancio", + "aggeggio", + "aggiunta", + "agio", + "agire", + "agitare", + "aglio", + "agnello", + "agosto", + "aiutare", + "albero", + "albo", + "alce", + "alchimia", + "alcool", + "alfabeto", + "algebra", + "alimento", + "allarme", + "alleanza", + "allievo", + "alloggio", + "alluce", + "alpi", + "alterare", + "altro", + "aluminio", + "amante", + "amarezza", + "ambiente", + "ambrosia", + "america", + "amico", + "ammalare", + "ammirare", + "amnesia", + "amnistia", + "amore", + "ampliare", + "amputare", + "analisi", + "anamnesi", + "ananas", + "anarchia", + "anatra", + "anca", + "ancorato", + "andare", + "androide", + "aneddoto", + "anello", + "angelo", + "angolino", + "anguilla", + "anidride", + "anima", + "annegare", + "anno", + "annuncio", + "anomalia", + "antenna", + "anticipo", + "aperto", + "apostolo", + "appalto", + "appello", + "appiglio", + "applauso", + "appoggio", + "appurare", + "aprile", + "aquila", + "arabo", + "arachidi", + "aragosta", + "arancia", + "arbitrio", + "archivio", + "arco", + "argento", + "argilla", + "aria", + "ariete", + "arma", + "armonia", + "aroma", + "arrivare", + "arrosto", + "arsenale", + "arte", + "artiglio", + "asfalto", + "asfissia", + "asino", + "asparagi", + "aspirina", + "assalire", + "assegno", + "assolto", + "assurdo", + "asta", + "astratto", + "atlante", + "atletica", + "atomo", + "atropina", + "attacco", + "attesa", + "attico", + "atto", + "attrarre", + "auguri", + "aula", + "aumento", + "aurora", + "auspicio", + "autista", + "auto", + "autunno", + "avanzare", + "avarizia", + "avere", + "aviatore", + "avido", + "avorio", + "avvenire", + "avviso", + "avvocato", + "azienda", + "azione", + "azzardo", + "azzurro", + "babbuino", + "bacio", + "badante", + "baffi", + "bagaglio", + "bagliore", + "bagno", + "balcone", + "balena", + "ballare", + "balordo", + "balsamo", + "bambola", + "bancomat", + "banda", + "barato", + "barba", + "barista", + "barriera", + "basette", + "basilico", + "bassista", + "bastare", + "battello", + "bavaglio", + "beccare", + "beduino", + "bellezza", + "bene", + "benzina", + "berretto", + "bestia", + "bevitore", + "bianco", + "bibbia", + "biberon", + "bibita", + "bici", + "bidone", + "bilancia", + "biliardo", + "binario", + "binocolo", + "biologia", + "biondina", + "biopsia", + "biossido", + "birbante", + "birra", + "biscotto", + "bisogno", + "bistecca", + "bivio", + "blindare", + "bloccare", + "bocca", + "bollire", + "bombola", + "bonifico", + "borghese", + "borsa", + "bottino", + "botulino", + "braccio", + "bradipo", + "branco", + "bravo", + "bresaola", + "bretelle", + "brevetto", + "briciola", + "brigante", + "brillare", + "brindare", + "brivido", + "broccoli", + "brontolo", + "bruciare", + "brufolo", + "bucare", + "buddista", + "budino", + "bufera", + "buffo", + "bugiardo", + "buio", + "buono", + "burrone", + "bussola", + "bustina", + "buttare", + "cabernet", + "cabina", + "cacao", + "cacciare", + "cactus", + "cadavere", + "caffe", + "calamari", + "calcio", + "caldaia", + "calmare", + "calunnia", + "calvario", + "calzone", + "cambiare", + "camera", + "camion", + "cammello", + "campana", + "canarino", + "cancello", + "candore", + "cane", + "canguro", + "cannone", + "canoa", + "cantare", + "canzone", + "caos", + "capanna", + "capello", + "capire", + "capo", + "capperi", + "capra", + "capsula", + "caraffa", + "carbone", + "carciofo", + "cardigan", + "carenza", + "caricare", + "carota", + "carrello", + "carta", + "casa", + "cascare", + "caserma", + "cashmere", + "casino", + "cassetta", + "castello", + "catalogo", + "catena", + "catorcio", + "cattivo", + "causa", + "cauzione", + "cavallo", + "caverna", + "caviglia", + "cavo", + "cazzotto", + "celibato", + "cemento", + "cenare", + "centrale", + "ceramica", + "cercare", + "ceretta", + "cerniera", + "certezza", + "cervello", + "cessione", + "cestino", + "cetriolo", + "chiave", + "chiedere", + "chilo", + "chimera", + "chiodo", + "chirurgo", + "chitarra", + "chiudere", + "ciabatta", + "ciao", + "cibo", + "ciccia", + "cicerone", + "ciclone", + "cicogna", + "cielo", + "cifra", + "cigno", + "ciliegia", + "cimitero", + "cinema", + "cinque", + "cintura", + "ciondolo", + "ciotola", + "cipolla", + "cippato", + "circuito", + "cisterna", + "citofono", + "ciuccio", + "civetta", + "civico", + "clausola", + "cliente", + "clima", + "clinica", + "cobra", + "coccole", + "cocktail", + "cocomero", + "codice", + "coesione", + "cogliere", + "cognome", + "colla", + "colomba", + "colpire", + "coltello", + "comando", + "comitato", + "commedia", + "comodino", + "compagna", + "comune", + "concerto", + "condotto", + "conforto", + "congiura", + "coniglio", + "consegna", + "conto", + "convegno", + "coperta", + "copia", + "coprire", + "corazza", + "corda", + "corleone", + "cornice", + "corona", + "corpo", + "corrente", + "corsa", + "cortesia", + "corvo", + "coso", + "costume", + "cotone", + "cottura", + "cozza", + "crampo", + "cratere", + "cravatta", + "creare", + "credere", + "crema", + "crescere", + "crimine", + "criterio", + "croce", + "crollare", + "cronaca", + "crostata", + "croupier", + "cubetto", + "cucciolo", + "cucina", + "cultura", + "cuoco", + "cuore", + "cupido", + "cupola", + "cura", + "curva", + "cuscino", + "custode", + "danzare", + "data", + "decennio", + "decidere", + "decollo", + "dedicare", + "dedurre", + "definire", + "delegare", + "delfino", + "delitto", + "demone", + "dentista", + "denuncia", + "deposito", + "derivare", + "deserto", + "designer", + "destino", + "detonare", + "dettagli", + "diagnosi", + "dialogo", + "diamante", + "diario", + "diavolo", + "dicembre", + "difesa", + "digerire", + "digitare", + "diluvio", + "dinamica", + "dipinto", + "diploma", + "diramare", + "dire", + "dirigere", + "dirupo", + "discesa", + "disdetta", + "disegno", + "disporre", + "dissenso", + "distacco", + "dito", + "ditta", + "diva", + "divenire", + "dividere", + "divorare", + "docente", + "dolcetto", + "dolore", + "domatore", + "domenica", + "dominare", + "donatore", + "donna", + "dorato", + "dormire", + "dorso", + "dosaggio", + "dottore", + "dovere", + "download", + "dragone", + "dramma", + "dubbio", + "dubitare", + "duetto", + "durata", + "ebbrezza", + "eccesso", + "eccitare", + "eclissi", + "economia", + "edera", + "edificio", + "editore", + "edizione", + "educare", + "effetto", + "egitto", + "egiziano", + "elastico", + "elefante", + "eleggere", + "elemento", + "elenco", + "elezione", + "elmetto", + "elogio", + "embrione", + "emergere", + "emettere", + "eminenza", + "emisfero", + "emozione", + "empatia", + "energia", + "enfasi", + "enigma", + "entrare", + "enzima", + "epidemia", + "epilogo", + "episodio", + "epoca", + "equivoco", + "erba", + "erede", + "eroe", + "erotico", + "errore", + "eruzione", + "esaltare", + "esame", + "esaudire", + "eseguire", + "esempio", + "esigere", + "esistere", + "esito", + "esperto", + "espresso", + "essere", + "estasi", + "esterno", + "estrarre", + "eterno", + "etica", + "euforico", + "europa", + "evacuare", + "evasione", + "evento", + "evidenza", + "evitare", + "evolvere", + "fabbrica", + "facciata", + "fagiano", + "fagotto", + "falco", + "fame", + "famiglia", + "fanale", + "fango", + "fantasia", + "farfalla", + "farmacia", + "faro", + "fase", + "fastidio", + "faticare", + "fatto", + "favola", + "febbre", + "femmina", + "femore", + "fenomeno", + "fermata", + "feromoni", + "ferrari", + "fessura", + "festa", + "fiaba", + "fiamma", + "fianco", + "fiat", + "fibbia", + "fidare", + "fieno", + "figa", + "figlio", + "figura", + "filetto", + "filmato", + "filosofo", + "filtrare", + "finanza", + "finestra", + "fingere", + "finire", + "finta", + "finzione", + "fiocco", + "fioraio", + "firewall", + "firmare", + "fisico", + "fissare", + "fittizio", + "fiume", + "flacone", + "flagello", + "flirtare", + "flusso", + "focaccia", + "foglio", + "fognario", + "follia", + "fonderia", + "fontana", + "forbici", + "forcella", + "foresta", + "forgiare", + "formare", + "fornace", + "foro", + "fortuna", + "forzare", + "fosforo", + "fotoni", + "fracasso", + "fragola", + "frantumi", + "fratello", + "frazione", + "freccia", + "freddo", + "frenare", + "fresco", + "friggere", + "frittata", + "frivolo", + "frizione", + "fronte", + "frullato", + "frumento", + "frusta", + "frutto", + "fucile", + "fuggire", + "fulmine", + "fumare", + "funzione", + "fuoco", + "furbizia", + "furgone", + "furia", + "furore", + "fusibile", + "fuso", + "futuro", + "gabbiano", + "galassia", + "gallina", + "gamba", + "gancio", + "garanzia", + "garofano", + "gasolio", + "gatto", + "gazebo", + "gazzetta", + "gelato", + "gemelli", + "generare", + "genitori", + "gennaio", + "geologia", + "germania", + "gestire", + "gettare", + "ghepardo", + "ghiaccio", + "giaccone", + "giaguaro", + "giallo", + "giappone", + "giardino", + "gigante", + "gioco", + "gioiello", + "giorno", + "giovane", + "giraffa", + "giudizio", + "giurare", + "giusto", + "globo", + "gloria", + "glucosio", + "gnocca", + "gocciola", + "godere", + "gomito", + "gomma", + "gonfiare", + "gorilla", + "governo", + "gradire", + "graffiti", + "granchio", + "grappolo", + "grasso", + "grattare", + "gridare", + "grissino", + "grondaia", + "grugnito", + "gruppo", + "guadagno", + "guaio", + "guancia", + "guardare", + "gufo", + "guidare", + "guscio", + "gusto", + "icona", + "idea", + "identico", + "idolo", + "idoneo", + "idrante", + "idrogeno", + "igiene", + "ignoto", + "imbarco", + "immagine", + "immobile", + "imparare", + "impedire", + "impianto", + "importo", + "impresa", + "impulso", + "incanto", + "incendio", + "incidere", + "incontro", + "incrocia", + "incubo", + "indagare", + "indice", + "indotto", + "infanzia", + "inferno", + "infinito", + "infranto", + "ingerire", + "inglese", + "ingoiare", + "ingresso", + "iniziare", + "innesco", + "insalata", + "inserire", + "insicuro", + "insonnia", + "insulto", + "interno", + "introiti", + "invasori", + "inverno", + "invito", + "invocare", + "ipnosi", + "ipocrita", + "ipotesi", + "ironia", + "irrigare", + "iscritto", + "isola", + "ispirare", + "isterico", + "istinto", + "istruire", + "italiano", + "jazz", + "labbra", + "labrador", + "ladro", + "lago", + "lamento", + "lampone", + "lancetta", + "lanterna", + "lapide", + "larva", + "lasagne", + "lasciare", + "lastra", + "latte", + "laurea", + "lavagna", + "lavorare", + "leccare", + "legare", + "leggere", + "lenzuolo", + "leone", + "lepre", + "letargo", + "lettera", + "levare", + "levitare", + "lezione", + "liberare", + "libidine", + "libro", + "licenza", + "lievito", + "limite", + "lince", + "lingua", + "liquore", + "lire", + "listino", + "litigare", + "litro", + "locale", + "lottare", + "lucciola", + "lucidare", + "luglio", + "luna", + "macchina", + "madama", + "madre", + "maestro", + "maggio", + "magico", + "maglione", + "magnolia", + "mago", + "maialino", + "maionese", + "malattia", + "male", + "malloppo", + "mancare", + "mandorla", + "mangiare", + "manico", + "manopola", + "mansarda", + "mantello", + "manubrio", + "manzo", + "mappa", + "mare", + "margine", + "marinaio", + "marmotta", + "marocco", + "martello", + "marzo", + "maschera", + "matrice", + "maturare", + "mazzetta", + "meandri", + "medaglia", + "medico", + "medusa", + "megafono", + "melone", + "membrana", + "menta", + "mercato", + "meritare", + "merluzzo", + "mese", + "mestiere", + "metafora", + "meteo", + "metodo", + "mettere", + "miele", + "miglio", + "miliardo", + "mimetica", + "minatore", + "minuto", + "miracolo", + "mirtillo", + "missile", + "mistero", + "misura", + "mito", + "mobile", + "moda", + "moderare", + "moglie", + "molecola", + "molle", + "momento", + "moneta", + "mongolia", + "monologo", + "montagna", + "morale", + "morbillo", + "mordere", + "mosaico", + "mosca", + "mostro", + "motivare", + "moto", + "mulino", + "mulo", + "muovere", + "muraglia", + "muscolo", + "museo", + "musica", + "mutande", + "nascere", + "nastro", + "natale", + "natura", + "nave", + "navigare", + "negare", + "negozio", + "nemico", + "nero", + "nervo", + "nessuno", + "nettare", + "neutroni", + "neve", + "nevicare", + "nicotina", + "nido", + "nipote", + "nocciola", + "noleggio", + "nome", + "nonno", + "norvegia", + "notare", + "notizia", + "nove", + "nucleo", + "nuda", + "nuotare", + "nutrire", + "obbligo", + "occhio", + "occupare", + "oceano", + "odissea", + "odore", + "offerta", + "officina", + "offrire", + "oggetto", + "oggi", + "olfatto", + "olio", + "oliva", + "ombelico", + "ombrello", + "omuncolo", + "ondata", + "onore", + "opera", + "opinione", + "opuscolo", + "opzione", + "orario", + "orbita", + "orchidea", + "ordine", + "orecchio", + "orgasmo", + "orgoglio", + "origine", + "orologio", + "oroscopo", + "orso", + "oscurare", + "ospedale", + "ospite", + "ossigeno", + "ostacolo", + "ostriche", + "ottenere", + "ottimo", + "ottobre", + "ovest", + "pacco", + "pace", + "pacifico", + "padella", + "pagare", + "pagina", + "pagnotta", + "palazzo", + "palestra", + "palpebre", + "pancetta", + "panfilo", + "panino", + "pannello", + "panorama", + "papa", + "paperino", + "paradiso", + "parcella", + "parente", + "parlare", + "parodia", + "parrucca", + "partire", + "passare", + "pasta", + "patata", + "patente", + "patogeno", + "patriota", + "pausa", + "pazienza", + "peccare", + "pecora", + "pedalare", + "pelare", + "pena", + "pendenza", + "penisola", + "pennello", + "pensare", + "pentirsi", + "percorso", + "perdono", + "perfetto", + "perizoma", + "perla", + "permesso", + "persona", + "pesare", + "pesce", + "peso", + "petardo", + "petrolio", + "pezzo", + "piacere", + "pianeta", + "piastra", + "piatto", + "piazza", + "piccolo", + "piede", + "piegare", + "pietra", + "pigiama", + "pigliare", + "pigrizia", + "pilastro", + "pilota", + "pinguino", + "pioggia", + "piombo", + "pionieri", + "piovra", + "pipa", + "pirata", + "pirolisi", + "piscina", + "pisolino", + "pista", + "pitone", + "piumino", + "pizza", + "plastica", + "platino", + "poesia", + "poiana", + "polaroid", + "polenta", + "polimero", + "pollo", + "polmone", + "polpetta", + "poltrona", + "pomodoro", + "pompa", + "popolo", + "porco", + "porta", + "porzione", + "possesso", + "postino", + "potassio", + "potere", + "poverino", + "pranzo", + "prato", + "prefisso", + "prelievo", + "premio", + "prendere", + "prestare", + "pretesa", + "prezzo", + "primario", + "privacy", + "problema", + "processo", + "prodotto", + "profeta", + "progetto", + "promessa", + "pronto", + "proposta", + "proroga", + "prossimo", + "proteina", + "prova", + "prudenza", + "pubblico", + "pudore", + "pugilato", + "pulire", + "pulsante", + "puntare", + "pupazzo", + "puzzle", + "quaderno", + "qualcuno", + "quarzo", + "quercia", + "quintale", + "rabbia", + "racconto", + "radice", + "raffica", + "ragazza", + "ragione", + "rammento", + "ramo", + "rana", + "randagio", + "rapace", + "rapinare", + "rapporto", + "rasatura", + "ravioli", + "reagire", + "realista", + "reattore", + "reazione", + "recitare", + "recluso", + "record", + "recupero", + "redigere", + "regalare", + "regina", + "regola", + "relatore", + "reliquia", + "remare", + "rendere", + "reparto", + "resina", + "resto", + "rete", + "retorica", + "rettile", + "revocare", + "riaprire", + "ribadire", + "ribelle", + "ricambio", + "ricetta", + "richiamo", + "ricordo", + "ridurre", + "riempire", + "riferire", + "riflesso", + "righello", + "rilancio", + "rilevare", + "rilievo", + "rimanere", + "rimborso", + "rinforzo", + "rinuncia", + "riparo", + "ripetere", + "riposare", + "ripulire", + "risalita", + "riscatto", + "riserva", + "riso", + "rispetto", + "ritaglio", + "ritmo", + "ritorno", + "ritratto", + "rituale", + "riunione", + "riuscire", + "riva", + "robotica", + "rondine", + "rosa", + "rospo", + "rosso", + "rotonda", + "rotta", + "roulotte", + "rubare", + "rubrica", + "ruffiano", + "rumore", + "ruota", + "ruscello", + "sabbia", + "sacco", + "saggio", + "sale", + "salire", + "salmone", + "salto", + "salutare", + "salvia", + "sangue", + "sanzioni", + "sapere", + "sapienza", + "sarcasmo", + "sardine", + "sartoria", + "sbalzo", + "sbarcare", + "sberla", + "sborsare", + "scadenza", + "scafo", + "scala", + "scambio", + "scappare", + "scarpa", + "scatola", + "scelta", + "scena", + "sceriffo", + "scheggia", + "schiuma", + "sciarpa", + "scienza", + "scimmia", + "sciopero", + "scivolo", + "sclerare", + "scolpire", + "sconto", + "scopa", + "scordare", + "scossa", + "scrivere", + "scrupolo", + "scuderia", + "scultore", + "scuola", + "scusare", + "sdraiare", + "secolo", + "sedativo", + "sedere", + "sedia", + "segare", + "segreto", + "seguire", + "semaforo", + "seme", + "senape", + "seno", + "sentiero", + "separare", + "sepolcro", + "sequenza", + "serata", + "serpente", + "servizio", + "sesso", + "seta", + "settore", + "sfamare", + "sfera", + "sfidare", + "sfiorare", + "sfogare", + "sgabello", + "sicuro", + "siepe", + "sigaro", + "silenzio", + "silicone", + "simbiosi", + "simpatia", + "simulare", + "sinapsi", + "sindrome", + "sinergia", + "sinonimo", + "sintonia", + "sirena", + "siringa", + "sistema", + "sito", + "smalto", + "smentire", + "smontare", + "soccorso", + "socio", + "soffitto", + "software", + "soggetto", + "sogliola", + "sognare", + "soldi", + "sole", + "sollievo", + "solo", + "sommario", + "sondare", + "sonno", + "sorpresa", + "sorriso", + "sospiro", + "sostegno", + "sovrano", + "spaccare", + "spada", + "spagnolo", + "spalla", + "sparire", + "spavento", + "spazio", + "specchio", + "spedire", + "spegnere", + "spendere", + "speranza", + "spessore", + "spezzare", + "spiaggia", + "spiccare", + "spiegare", + "spiffero", + "spingere", + "sponda", + "sporcare", + "spostare", + "spremuta", + "spugna", + "spumante", + "spuntare", + "squadra", + "squillo", + "staccare", + "stadio", + "stagione", + "stallone", + "stampa", + "stancare", + "starnuto", + "statura", + "stella", + "stendere", + "sterzo", + "stilista", + "stimolo", + "stinco", + "stiva", + "stoffa", + "storia", + "strada", + "stregone", + "striscia", + "studiare", + "stufa", + "stupendo", + "subire", + "successo", + "sudare", + "suono", + "superare", + "supporto", + "surfista", + "sussurro", + "svelto", + "svenire", + "sviluppo", + "svolta", + "svuotare", + "tabacco", + "tabella", + "tabu", + "tacchino", + "tacere", + "taglio", + "talento", + "tangente", + "tappeto", + "tartufo", + "tassello", + "tastiera", + "tavolo", + "tazza", + "teatro", + "tedesco", + "telaio", + "telefono", + "tema", + "temere", + "tempo", + "tendenza", + "tenebre", + "tensione", + "tentare", + "teologia", + "teorema", + "termica", + "terrazzo", + "teschio", + "tesi", + "tesoro", + "tessera", + "testa", + "thriller", + "tifoso", + "tigre", + "timbrare", + "timido", + "tinta", + "tirare", + "tisana", + "titano", + "titolo", + "toccare", + "togliere", + "topolino", + "torcia", + "torrente", + "tovaglia", + "traffico", + "tragitto", + "training", + "tramonto", + "transito", + "trapezio", + "trasloco", + "trattore", + "trazione", + "treccia", + "tregua", + "treno", + "triciclo", + "tridente", + "trilogia", + "tromba", + "troncare", + "trota", + "trovare", + "trucco", + "tubo", + "tulipano", + "tumulto", + "tunisia", + "tuono", + "turista", + "tuta", + "tutelare", + "tutore", + "ubriaco", + "uccello", + "udienza", + "udito", + "uffa", + "umanoide", + "umore", + "unghia", + "unguento", + "unicorno", + "unione", + "universo", + "uomo", + "uragano", + "uranio", + "urlare", + "uscire", + "utente", + "utilizzo", + "vacanza", + "vacca", + "vaglio", + "vagonata", + "valle", + "valore", + "valutare", + "valvola", + "vampiro", + "vaniglia", + "vanto", + "vapore", + "variante", + "vasca", + "vaselina", + "vassoio", + "vedere", + "vegetale", + "veglia", + "veicolo", + "vela", + "veleno", + "velivolo", + "velluto", + "vendere", + "venerare", + "venire", + "vento", + "veranda", + "verbo", + "verdura", + "vergine", + "verifica", + "vernice", + "vero", + "verruca", + "versare", + "vertebra", + "vescica", + "vespaio", + "vestito", + "vesuvio", + "veterano", + "vetro", + "vetta", + "viadotto", + "viaggio", + "vibrare", + "vicenda", + "vichingo", + "vietare", + "vigilare", + "vigneto", + "villa", + "vincere", + "violino", + "vipera", + "virgola", + "virtuoso", + "visita", + "vita", + "vitello", + "vittima", + "vivavoce", + "vivere", + "viziato", + "voglia", + "volare", + "volpe", + "volto", + "volume", + "vongole", + "voragine", + "vortice", + "votare", + "vulcano", + "vuotare", + "zabaione", + "zaffiro", + "zainetto", + "zampa", + "zanzara", + "zattera", + "zavorra", + "zenzero", + "zero", + "zingaro", + "zittire", + "zoccolo", + "zolfo", + "zombie", + "zucchero" + ]; +} \ No newline at end of file diff --git a/cw_wownero/lib/mnemonics/japanese.dart b/cw_wownero/lib/mnemonics/japanese.dart new file mode 100644 index 000000000..5d17fdb14 --- /dev/null +++ b/cw_wownero/lib/mnemonics/japanese.dart @@ -0,0 +1,1630 @@ +class JapaneseMnemonics { + static const words = [ + "あいこくしん", + "あいさつ", + "あいだ", + "あおぞら", + "あかちゃん", + "あきる", + "あけがた", + "あける", + "あこがれる", + "あさい", + "あさひ", + "あしあと", + "あじわう", + "あずかる", + "あずき", + "あそぶ", + "あたえる", + "あたためる", + "あたりまえ", + "あたる", + "あつい", + "あつかう", + "あっしゅく", + "あつまり", + "あつめる", + "あてな", + "あてはまる", + "あひる", + "あぶら", + "あぶる", + "あふれる", + "あまい", + "あまど", + "あまやかす", + "あまり", + "あみもの", + "あめりか", + "あやまる", + "あゆむ", + "あらいぐま", + "あらし", + "あらすじ", + "あらためる", + "あらゆる", + "あらわす", + "ありがとう", + "あわせる", + "あわてる", + "あんい", + "あんがい", + "あんこ", + "あんぜん", + "あんてい", + "あんない", + "あんまり", + "いいだす", + "いおん", + "いがい", + "いがく", + "いきおい", + "いきなり", + "いきもの", + "いきる", + "いくじ", + "いくぶん", + "いけばな", + "いけん", + "いこう", + "いこく", + "いこつ", + "いさましい", + "いさん", + "いしき", + "いじゅう", + "いじょう", + "いじわる", + "いずみ", + "いずれ", + "いせい", + "いせえび", + "いせかい", + "いせき", + "いぜん", + "いそうろう", + "いそがしい", + "いだい", + "いだく", + "いたずら", + "いたみ", + "いたりあ", + "いちおう", + "いちじ", + "いちど", + "いちば", + "いちぶ", + "いちりゅう", + "いつか", + "いっしゅん", + "いっせい", + "いっそう", + "いったん", + "いっち", + "いってい", + "いっぽう", + "いてざ", + "いてん", + "いどう", + "いとこ", + "いない", + "いなか", + "いねむり", + "いのち", + "いのる", + "いはつ", + "いばる", + "いはん", + "いびき", + "いひん", + "いふく", + "いへん", + "いほう", + "いみん", + "いもうと", + "いもたれ", + "いもり", + "いやがる", + "いやす", + "いよかん", + "いよく", + "いらい", + "いらすと", + "いりぐち", + "いりょう", + "いれい", + "いれもの", + "いれる", + "いろえんぴつ", + "いわい", + "いわう", + "いわかん", + "いわば", + "いわゆる", + "いんげんまめ", + "いんさつ", + "いんしょう", + "いんよう", + "うえき", + "うえる", + "うおざ", + "うがい", + "うかぶ", + "うかべる", + "うきわ", + "うくらいな", + "うくれれ", + "うけたまわる", + "うけつけ", + "うけとる", + "うけもつ", + "うける", + "うごかす", + "うごく", + "うこん", + "うさぎ", + "うしなう", + "うしろがみ", + "うすい", + "うすぎ", + "うすぐらい", + "うすめる", + "うせつ", + "うちあわせ", + "うちがわ", + "うちき", + "うちゅう", + "うっかり", + "うつくしい", + "うったえる", + "うつる", + "うどん", + "うなぎ", + "うなじ", + "うなずく", + "うなる", + "うねる", + "うのう", + "うぶげ", + "うぶごえ", + "うまれる", + "うめる", + "うもう", + "うやまう", + "うよく", + "うらがえす", + "うらぐち", + "うらない", + "うりあげ", + "うりきれ", + "うるさい", + "うれしい", + "うれゆき", + "うれる", + "うろこ", + "うわき", + "うわさ", + "うんこう", + "うんちん", + "うんてん", + "うんどう", + "えいえん", + "えいが", + "えいきょう", + "えいご", + "えいせい", + "えいぶん", + "えいよう", + "えいわ", + "えおり", + "えがお", + "えがく", + "えきたい", + "えくせる", + "えしゃく", + "えすて", + "えつらん", + "えのぐ", + "えほうまき", + "えほん", + "えまき", + "えもじ", + "えもの", + "えらい", + "えらぶ", + "えりあ", + "えんえん", + "えんかい", + "えんぎ", + "えんげき", + "えんしゅう", + "えんぜつ", + "えんそく", + "えんちょう", + "えんとつ", + "おいかける", + "おいこす", + "おいしい", + "おいつく", + "おうえん", + "おうさま", + "おうじ", + "おうせつ", + "おうたい", + "おうふく", + "おうべい", + "おうよう", + "おえる", + "おおい", + "おおう", + "おおどおり", + "おおや", + "おおよそ", + "おかえり", + "おかず", + "おがむ", + "おかわり", + "おぎなう", + "おきる", + "おくさま", + "おくじょう", + "おくりがな", + "おくる", + "おくれる", + "おこす", + "おこなう", + "おこる", + "おさえる", + "おさない", + "おさめる", + "おしいれ", + "おしえる", + "おじぎ", + "おじさん", + "おしゃれ", + "おそらく", + "おそわる", + "おたがい", + "おたく", + "おだやか", + "おちつく", + "おっと", + "おつり", + "おでかけ", + "おとしもの", + "おとなしい", + "おどり", + "おどろかす", + "おばさん", + "おまいり", + "おめでとう", + "おもいで", + "おもう", + "おもたい", + "おもちゃ", + "おやつ", + "おやゆび", + "およぼす", + "おらんだ", + "おろす", + "おんがく", + "おんけい", + "おんしゃ", + "おんせん", + "おんだん", + "おんちゅう", + "おんどけい", + "かあつ", + "かいが", + "がいき", + "がいけん", + "がいこう", + "かいさつ", + "かいしゃ", + "かいすいよく", + "かいぜん", + "かいぞうど", + "かいつう", + "かいてん", + "かいとう", + "かいふく", + "がいへき", + "かいほう", + "かいよう", + "がいらい", + "かいわ", + "かえる", + "かおり", + "かかえる", + "かがく", + "かがし", + "かがみ", + "かくご", + "かくとく", + "かざる", + "がぞう", + "かたい", + "かたち", + "がちょう", + "がっきゅう", + "がっこう", + "がっさん", + "がっしょう", + "かなざわし", + "かのう", + "がはく", + "かぶか", + "かほう", + "かほご", + "かまう", + "かまぼこ", + "かめれおん", + "かゆい", + "かようび", + "からい", + "かるい", + "かろう", + "かわく", + "かわら", + "がんか", + "かんけい", + "かんこう", + "かんしゃ", + "かんそう", + "かんたん", + "かんち", + "がんばる", + "きあい", + "きあつ", + "きいろ", + "ぎいん", + "きうい", + "きうん", + "きえる", + "きおう", + "きおく", + "きおち", + "きおん", + "きかい", + "きかく", + "きかんしゃ", + "ききて", + "きくばり", + "きくらげ", + "きけんせい", + "きこう", + "きこえる", + "きこく", + "きさい", + "きさく", + "きさま", + "きさらぎ", + "ぎじかがく", + "ぎしき", + "ぎじたいけん", + "ぎじにってい", + "ぎじゅつしゃ", + "きすう", + "きせい", + "きせき", + "きせつ", + "きそう", + "きぞく", + "きぞん", + "きたえる", + "きちょう", + "きつえん", + "ぎっちり", + "きつつき", + "きつね", + "きてい", + "きどう", + "きどく", + "きない", + "きなが", + "きなこ", + "きぬごし", + "きねん", + "きのう", + "きのした", + "きはく", + "きびしい", + "きひん", + "きふく", + "きぶん", + "きぼう", + "きほん", + "きまる", + "きみつ", + "きむずかしい", + "きめる", + "きもだめし", + "きもち", + "きもの", + "きゃく", + "きやく", + "ぎゅうにく", + "きよう", + "きょうりゅう", + "きらい", + "きらく", + "きりん", + "きれい", + "きれつ", + "きろく", + "ぎろん", + "きわめる", + "ぎんいろ", + "きんかくじ", + "きんじょ", + "きんようび", + "ぐあい", + "くいず", + "くうかん", + "くうき", + "くうぐん", + "くうこう", + "ぐうせい", + "くうそう", + "ぐうたら", + "くうふく", + "くうぼ", + "くかん", + "くきょう", + "くげん", + "ぐこう", + "くさい", + "くさき", + "くさばな", + "くさる", + "くしゃみ", + "くしょう", + "くすのき", + "くすりゆび", + "くせげ", + "くせん", + "ぐたいてき", + "くださる", + "くたびれる", + "くちこみ", + "くちさき", + "くつした", + "ぐっすり", + "くつろぐ", + "くとうてん", + "くどく", + "くなん", + "くねくね", + "くのう", + "くふう", + "くみあわせ", + "くみたてる", + "くめる", + "くやくしょ", + "くらす", + "くらべる", + "くるま", + "くれる", + "くろう", + "くわしい", + "ぐんかん", + "ぐんしょく", + "ぐんたい", + "ぐんて", + "けあな", + "けいかく", + "けいけん", + "けいこ", + "けいさつ", + "げいじゅつ", + "けいたい", + "げいのうじん", + "けいれき", + "けいろ", + "けおとす", + "けおりもの", + "げきか", + "げきげん", + "げきだん", + "げきちん", + "げきとつ", + "げきは", + "げきやく", + "げこう", + "げこくじょう", + "げざい", + "けさき", + "げざん", + "けしき", + "けしごむ", + "けしょう", + "げすと", + "けたば", + "けちゃっぷ", + "けちらす", + "けつあつ", + "けつい", + "けつえき", + "けっこん", + "けつじょ", + "けっせき", + "けってい", + "けつまつ", + "げつようび", + "げつれい", + "けつろん", + "げどく", + "けとばす", + "けとる", + "けなげ", + "けなす", + "けなみ", + "けぬき", + "げねつ", + "けねん", + "けはい", + "げひん", + "けぶかい", + "げぼく", + "けまり", + "けみかる", + "けむし", + "けむり", + "けもの", + "けらい", + "けろけろ", + "けわしい", + "けんい", + "けんえつ", + "けんお", + "けんか", + "げんき", + "けんげん", + "けんこう", + "けんさく", + "けんしゅう", + "けんすう", + "げんそう", + "けんちく", + "けんてい", + "けんとう", + "けんない", + "けんにん", + "げんぶつ", + "けんま", + "けんみん", + "けんめい", + "けんらん", + "けんり", + "こあくま", + "こいぬ", + "こいびと", + "ごうい", + "こうえん", + "こうおん", + "こうかん", + "ごうきゅう", + "ごうけい", + "こうこう", + "こうさい", + "こうじ", + "こうすい", + "ごうせい", + "こうそく", + "こうたい", + "こうちゃ", + "こうつう", + "こうてい", + "こうどう", + "こうない", + "こうはい", + "ごうほう", + "ごうまん", + "こうもく", + "こうりつ", + "こえる", + "こおり", + "ごかい", + "ごがつ", + "ごかん", + "こくご", + "こくさい", + "こくとう", + "こくない", + "こくはく", + "こぐま", + "こけい", + "こける", + "ここのか", + "こころ", + "こさめ", + "こしつ", + "こすう", + "こせい", + "こせき", + "こぜん", + "こそだて", + "こたい", + "こたえる", + "こたつ", + "こちょう", + "こっか", + "こつこつ", + "こつばん", + "こつぶ", + "こてい", + "こてん", + "ことがら", + "ことし", + "ことば", + "ことり", + "こなごな", + "こねこね", + "このまま", + "このみ", + "このよ", + "ごはん", + "こひつじ", + "こふう", + "こふん", + "こぼれる", + "ごまあぶら", + "こまかい", + "ごますり", + "こまつな", + "こまる", + "こむぎこ", + "こもじ", + "こもち", + "こもの", + "こもん", + "こやく", + "こやま", + "こゆう", + "こゆび", + "こよい", + "こよう", + "こりる", + "これくしょん", + "ころっけ", + "こわもて", + "こわれる", + "こんいん", + "こんかい", + "こんき", + "こんしゅう", + "こんすい", + "こんだて", + "こんとん", + "こんなん", + "こんびに", + "こんぽん", + "こんまけ", + "こんや", + "こんれい", + "こんわく", + "ざいえき", + "さいかい", + "さいきん", + "ざいげん", + "ざいこ", + "さいしょ", + "さいせい", + "ざいたく", + "ざいちゅう", + "さいてき", + "ざいりょう", + "さうな", + "さかいし", + "さがす", + "さかな", + "さかみち", + "さがる", + "さぎょう", + "さくし", + "さくひん", + "さくら", + "さこく", + "さこつ", + "さずかる", + "ざせき", + "さたん", + "さつえい", + "ざつおん", + "ざっか", + "ざつがく", + "さっきょく", + "ざっし", + "さつじん", + "ざっそう", + "さつたば", + "さつまいも", + "さてい", + "さといも", + "さとう", + "さとおや", + "さとし", + "さとる", + "さのう", + "さばく", + "さびしい", + "さべつ", + "さほう", + "さほど", + "さます", + "さみしい", + "さみだれ", + "さむけ", + "さめる", + "さやえんどう", + "さゆう", + "さよう", + "さよく", + "さらだ", + "ざるそば", + "さわやか", + "さわる", + "さんいん", + "さんか", + "さんきゃく", + "さんこう", + "さんさい", + "ざんしょ", + "さんすう", + "さんせい", + "さんそ", + "さんち", + "さんま", + "さんみ", + "さんらん", + "しあい", + "しあげ", + "しあさって", + "しあわせ", + "しいく", + "しいん", + "しうち", + "しえい", + "しおけ", + "しかい", + "しかく", + "じかん", + "しごと", + "しすう", + "じだい", + "したうけ", + "したぎ", + "したて", + "したみ", + "しちょう", + "しちりん", + "しっかり", + "しつじ", + "しつもん", + "してい", + "してき", + "してつ", + "じてん", + "じどう", + "しなぎれ", + "しなもの", + "しなん", + "しねま", + "しねん", + "しのぐ", + "しのぶ", + "しはい", + "しばかり", + "しはつ", + "しはらい", + "しはん", + "しひょう", + "しふく", + "じぶん", + "しへい", + "しほう", + "しほん", + "しまう", + "しまる", + "しみん", + "しむける", + "じむしょ", + "しめい", + "しめる", + "しもん", + "しゃいん", + "しゃうん", + "しゃおん", + "じゃがいも", + "しやくしょ", + "しゃくほう", + "しゃけん", + "しゃこ", + "しゃざい", + "しゃしん", + "しゃせん", + "しゃそう", + "しゃたい", + "しゃちょう", + "しゃっきん", + "じゃま", + "しゃりん", + "しゃれい", + "じゆう", + "じゅうしょ", + "しゅくはく", + "じゅしん", + "しゅっせき", + "しゅみ", + "しゅらば", + "じゅんばん", + "しょうかい", + "しょくたく", + "しょっけん", + "しょどう", + "しょもつ", + "しらせる", + "しらべる", + "しんか", + "しんこう", + "じんじゃ", + "しんせいじ", + "しんちく", + "しんりん", + "すあげ", + "すあし", + "すあな", + "ずあん", + "すいえい", + "すいか", + "すいとう", + "ずいぶん", + "すいようび", + "すうがく", + "すうじつ", + "すうせん", + "すおどり", + "すきま", + "すくう", + "すくない", + "すける", + "すごい", + "すこし", + "ずさん", + "すずしい", + "すすむ", + "すすめる", + "すっかり", + "ずっしり", + "ずっと", + "すてき", + "すてる", + "すねる", + "すのこ", + "すはだ", + "すばらしい", + "ずひょう", + "ずぶぬれ", + "すぶり", + "すふれ", + "すべて", + "すべる", + "ずほう", + "すぼん", + "すまい", + "すめし", + "すもう", + "すやき", + "すらすら", + "するめ", + "すれちがう", + "すろっと", + "すわる", + "すんぜん", + "すんぽう", + "せあぶら", + "せいかつ", + "せいげん", + "せいじ", + "せいよう", + "せおう", + "せかいかん", + "せきにん", + "せきむ", + "せきゆ", + "せきらんうん", + "せけん", + "せこう", + "せすじ", + "せたい", + "せたけ", + "せっかく", + "せっきゃく", + "ぜっく", + "せっけん", + "せっこつ", + "せっさたくま", + "せつぞく", + "せつだん", + "せつでん", + "せっぱん", + "せつび", + "せつぶん", + "せつめい", + "せつりつ", + "せなか", + "せのび", + "せはば", + "せびろ", + "せぼね", + "せまい", + "せまる", + "せめる", + "せもたれ", + "せりふ", + "ぜんあく", + "せんい", + "せんえい", + "せんか", + "せんきょ", + "せんく", + "せんげん", + "ぜんご", + "せんさい", + "せんしゅ", + "せんすい", + "せんせい", + "せんぞ", + "せんたく", + "せんちょう", + "せんてい", + "せんとう", + "せんぬき", + "せんねん", + "せんぱい", + "ぜんぶ", + "ぜんぽう", + "せんむ", + "せんめんじょ", + "せんもん", + "せんやく", + "せんゆう", + "せんよう", + "ぜんら", + "ぜんりゃく", + "せんれい", + "せんろ", + "そあく", + "そいとげる", + "そいね", + "そうがんきょう", + "そうき", + "そうご", + "そうしん", + "そうだん", + "そうなん", + "そうび", + "そうめん", + "そうり", + "そえもの", + "そえん", + "そがい", + "そげき", + "そこう", + "そこそこ", + "そざい", + "そしな", + "そせい", + "そせん", + "そそぐ", + "そだてる", + "そつう", + "そつえん", + "そっかん", + "そつぎょう", + "そっけつ", + "そっこう", + "そっせん", + "そっと", + "そとがわ", + "そとづら", + "そなえる", + "そなた", + "そふぼ", + "そぼく", + "そぼろ", + "そまつ", + "そまる", + "そむく", + "そむりえ", + "そめる", + "そもそも", + "そよかぜ", + "そらまめ", + "そろう", + "そんかい", + "そんけい", + "そんざい", + "そんしつ", + "そんぞく", + "そんちょう", + "ぞんび", + "ぞんぶん", + "そんみん", + "たあい", + "たいいん", + "たいうん", + "たいえき", + "たいおう", + "だいがく", + "たいき", + "たいぐう", + "たいけん", + "たいこ", + "たいざい", + "だいじょうぶ", + "だいすき", + "たいせつ", + "たいそう", + "だいたい", + "たいちょう", + "たいてい", + "だいどころ", + "たいない", + "たいねつ", + "たいのう", + "たいはん", + "だいひょう", + "たいふう", + "たいへん", + "たいほ", + "たいまつばな", + "たいみんぐ", + "たいむ", + "たいめん", + "たいやき", + "たいよう", + "たいら", + "たいりょく", + "たいる", + "たいわん", + "たうえ", + "たえる", + "たおす", + "たおる", + "たおれる", + "たかい", + "たかね", + "たきび", + "たくさん", + "たこく", + "たこやき", + "たさい", + "たしざん", + "だじゃれ", + "たすける", + "たずさわる", + "たそがれ", + "たたかう", + "たたく", + "ただしい", + "たたみ", + "たちばな", + "だっかい", + "だっきゃく", + "だっこ", + "だっしゅつ", + "だったい", + "たてる", + "たとえる", + "たなばた", + "たにん", + "たぬき", + "たのしみ", + "たはつ", + "たぶん", + "たべる", + "たぼう", + "たまご", + "たまる", + "だむる", + "ためいき", + "ためす", + "ためる", + "たもつ", + "たやすい", + "たよる", + "たらす", + "たりきほんがん", + "たりょう", + "たりる", + "たると", + "たれる", + "たれんと", + "たろっと", + "たわむれる", + "だんあつ", + "たんい", + "たんおん", + "たんか", + "たんき", + "たんけん", + "たんご", + "たんさん", + "たんじょうび", + "だんせい", + "たんそく", + "たんたい", + "だんち", + "たんてい", + "たんとう", + "だんな", + "たんにん", + "だんねつ", + "たんのう", + "たんぴん", + "だんぼう", + "たんまつ", + "たんめい", + "だんれつ", + "だんろ", + "だんわ", + "ちあい", + "ちあん", + "ちいき", + "ちいさい", + "ちえん", + "ちかい", + "ちから", + "ちきゅう", + "ちきん", + "ちけいず", + "ちけん", + "ちこく", + "ちさい", + "ちしき", + "ちしりょう", + "ちせい", + "ちそう", + "ちたい", + "ちたん", + "ちちおや", + "ちつじょ", + "ちてき", + "ちてん", + "ちぬき", + "ちぬり", + "ちのう", + "ちひょう", + "ちへいせん", + "ちほう", + "ちまた", + "ちみつ", + "ちみどろ", + "ちめいど", + "ちゃんこなべ", + "ちゅうい", + "ちゆりょく", + "ちょうし", + "ちょさくけん", + "ちらし", + "ちらみ", + "ちりがみ", + "ちりょう", + "ちるど", + "ちわわ", + "ちんたい", + "ちんもく", + "ついか", + "ついたち", + "つうか", + "つうじょう", + "つうはん", + "つうわ", + "つかう", + "つかれる", + "つくね", + "つくる", + "つけね", + "つける", + "つごう", + "つたえる", + "つづく", + "つつじ", + "つつむ", + "つとめる", + "つながる", + "つなみ", + "つねづね", + "つのる", + "つぶす", + "つまらない", + "つまる", + "つみき", + "つめたい", + "つもり", + "つもる", + "つよい", + "つるぼ", + "つるみく", + "つわもの", + "つわり", + "てあし", + "てあて", + "てあみ", + "ていおん", + "ていか", + "ていき", + "ていけい", + "ていこく", + "ていさつ", + "ていし", + "ていせい", + "ていたい", + "ていど", + "ていねい", + "ていひょう", + "ていへん", + "ていぼう", + "てうち", + "ておくれ", + "てきとう", + "てくび", + "でこぼこ", + "てさぎょう", + "てさげ", + "てすり", + "てそう", + "てちがい", + "てちょう", + "てつがく", + "てつづき", + "でっぱ", + "てつぼう", + "てつや", + "でぬかえ", + "てぬき", + "てぬぐい", + "てのひら", + "てはい", + "てぶくろ", + "てふだ", + "てほどき", + "てほん", + "てまえ", + "てまきずし", + "てみじか", + "てみやげ", + "てらす", + "てれび", + "てわけ", + "てわたし", + "でんあつ", + "てんいん", + "てんかい", + "てんき", + "てんぐ", + "てんけん", + "てんごく", + "てんさい", + "てんし", + "てんすう", + "でんち", + "てんてき", + "てんとう", + "てんない", + "てんぷら", + "てんぼうだい", + "てんめつ", + "てんらんかい", + "でんりょく", + "でんわ", + "どあい", + "といれ", + "どうかん", + "とうきゅう", + "どうぐ", + "とうし", + "とうむぎ", + "とおい", + "とおか", + "とおく", + "とおす", + "とおる", + "とかい", + "とかす", + "ときおり", + "ときどき", + "とくい", + "とくしゅう", + "とくてん", + "とくに", + "とくべつ", + "とけい", + "とける", + "とこや", + "とさか", + "としょかん", + "とそう", + "とたん", + "とちゅう", + "とっきゅう", + "とっくん", + "とつぜん", + "とつにゅう", + "とどける", + "ととのえる", + "とない", + "となえる", + "となり", + "とのさま", + "とばす", + "どぶがわ", + "とほう", + "とまる", + "とめる", + "ともだち", + "ともる", + "どようび", + "とらえる", + "とんかつ", + "どんぶり", + "ないかく", + "ないこう", + "ないしょ", + "ないす", + "ないせん", + "ないそう", + "なおす", + "ながい", + "なくす", + "なげる", + "なこうど", + "なさけ", + "なたでここ", + "なっとう", + "なつやすみ", + "ななおし", + "なにごと", + "なにもの", + "なにわ", + "なのか", + "なふだ", + "なまいき", + "なまえ", + "なまみ", + "なみだ", + "なめらか", + "なめる", + "なやむ", + "ならう", + "ならび", + "ならぶ", + "なれる", + "なわとび", + "なわばり", + "にあう", + "にいがた", + "にうけ", + "におい", + "にかい", + "にがて", + "にきび", + "にくしみ", + "にくまん", + "にげる", + "にさんかたんそ", + "にしき", + "にせもの", + "にちじょう", + "にちようび", + "にっか", + "にっき", + "にっけい", + "にっこう", + "にっさん", + "にっしょく", + "にっすう", + "にっせき", + "にってい", + "になう", + "にほん", + "にまめ", + "にもつ", + "にやり", + "にゅういん", + "にりんしゃ", + "にわとり", + "にんい", + "にんか", + "にんき", + "にんげん", + "にんしき", + "にんずう", + "にんそう", + "にんたい", + "にんち", + "にんてい", + "にんにく", + "にんぷ", + "にんまり", + "にんむ", + "にんめい", + "にんよう", + "ぬいくぎ", + "ぬかす", + "ぬぐいとる", + "ぬぐう", + "ぬくもり", + "ぬすむ", + "ぬまえび", + "ぬめり", + "ぬらす", + "ぬんちゃく", + "ねあげ", + "ねいき", + "ねいる", + "ねいろ", + "ねぐせ", + "ねくたい", + "ねくら", + "ねこぜ", + "ねこむ", + "ねさげ", + "ねすごす", + "ねそべる", + "ねだん", + "ねつい", + "ねっしん", + "ねつぞう", + "ねったいぎょ", + "ねぶそく", + "ねふだ", + "ねぼう", + "ねほりはほり", + "ねまき", + "ねまわし", + "ねみみ", + "ねむい", + "ねむたい", + "ねもと", + "ねらう", + "ねわざ", + "ねんいり", + "ねんおし", + "ねんかん", + "ねんきん", + "ねんぐ", + "ねんざ", + "ねんし", + "ねんちゃく", + "ねんど", + "ねんぴ", + "ねんぶつ", + "ねんまつ", + "ねんりょう", + "ねんれい", + "のいず", + "のおづま", + "のがす", + "のきなみ", + "のこぎり", + "のこす", + "のこる", + "のせる", + "のぞく", + "のぞむ", + "のたまう", + "のちほど", + "のっく", + "のばす", + "のはら", + "のべる", + "のぼる", + "のみもの", + "のやま", + "のらいぬ", + "のらねこ", + "のりもの", + "のりゆき", + "のれん", + "のんき", + "ばあい", + "はあく", + "ばあさん", + "ばいか", + "ばいく", + "はいけん", + "はいご", + "はいしん", + "はいすい", + "はいせん", + "はいそう", + "はいち", + "ばいばい", + "はいれつ", + "はえる", + "はおる", + "はかい", + "ばかり", + "はかる", + "はくしゅ", + "はけん", + "はこぶ", + "はさみ", + "はさん", + "はしご", + "ばしょ", + "はしる", + "はせる", + "ぱそこん", + "はそん", + "はたん", + "はちみつ", + "はつおん", + "はっかく", + "はづき", + "はっきり", + "はっくつ", + "はっけん", + "はっこう", + "はっさん", + "はっしん", + "はったつ", + "はっちゅう", + "はってん", + "はっぴょう", + "はっぽう", + "はなす", + "はなび", + "はにかむ", + "はぶらし", + "はみがき", + "はむかう", + "はめつ", + "はやい", + "はやし", + "はらう", + "はろうぃん", + "はわい", + "はんい", + "はんえい", + "はんおん", + "はんかく", + "はんきょう", + "ばんぐみ", + "はんこ", + "はんしゃ", + "はんすう", + "はんだん", + "ぱんち", + "ぱんつ", + "はんてい", + "はんとし", + "はんのう", + "はんぱ", + "はんぶん", + "はんぺん", + "はんぼうき", + "はんめい", + "はんらん", + "はんろん", + "ひいき", + "ひうん", + "ひえる", + "ひかく", + "ひかり", + "ひかる", + "ひかん", + "ひくい", + "ひけつ", + "ひこうき", + "ひこく", + "ひさい", + "ひさしぶり", + "ひさん", + "びじゅつかん", + "ひしょ" + ]; +} \ No newline at end of file diff --git a/cw_wownero/lib/mnemonics/portuguese.dart b/cw_wownero/lib/mnemonics/portuguese.dart new file mode 100644 index 000000000..bdd63d3b2 --- /dev/null +++ b/cw_wownero/lib/mnemonics/portuguese.dart @@ -0,0 +1,1630 @@ +class PortugueseMnemonics { + static const words = [ + "abaular", + "abdominal", + "abeto", + "abissinio", + "abjeto", + "ablucao", + "abnegar", + "abotoar", + "abrutalhar", + "absurdo", + "abutre", + "acautelar", + "accessorios", + "acetona", + "achocolatado", + "acirrar", + "acne", + "acovardar", + "acrostico", + "actinomicete", + "acustico", + "adaptavel", + "adeus", + "adivinho", + "adjunto", + "admoestar", + "adnominal", + "adotivo", + "adquirir", + "adriatico", + "adsorcao", + "adutora", + "advogar", + "aerossol", + "afazeres", + "afetuoso", + "afixo", + "afluir", + "afortunar", + "afrouxar", + "aftosa", + "afunilar", + "agentes", + "agito", + "aglutinar", + "aiatola", + "aimore", + "aino", + "aipo", + "airoso", + "ajeitar", + "ajoelhar", + "ajudante", + "ajuste", + "alazao", + "albumina", + "alcunha", + "alegria", + "alexandre", + "alforriar", + "alguns", + "alhures", + "alivio", + "almoxarife", + "alotropico", + "alpiste", + "alquimista", + "alsaciano", + "altura", + "aluviao", + "alvura", + "amazonico", + "ambulatorio", + "ametodico", + "amizades", + "amniotico", + "amovivel", + "amurada", + "anatomico", + "ancorar", + "anexo", + "anfora", + "aniversario", + "anjo", + "anotar", + "ansioso", + "anturio", + "anuviar", + "anverso", + "anzol", + "aonde", + "apaziguar", + "apito", + "aplicavel", + "apoteotico", + "aprimorar", + "aprumo", + "apto", + "apuros", + "aquoso", + "arauto", + "arbusto", + "arduo", + "aresta", + "arfar", + "arguto", + "aritmetico", + "arlequim", + "armisticio", + "aromatizar", + "arpoar", + "arquivo", + "arrumar", + "arsenio", + "arturiano", + "aruaque", + "arvores", + "asbesto", + "ascorbico", + "aspirina", + "asqueroso", + "assustar", + "astuto", + "atazanar", + "ativo", + "atletismo", + "atmosferico", + "atormentar", + "atroz", + "aturdir", + "audivel", + "auferir", + "augusto", + "aula", + "aumento", + "aurora", + "autuar", + "avatar", + "avexar", + "avizinhar", + "avolumar", + "avulso", + "axiomatico", + "azerbaijano", + "azimute", + "azoto", + "azulejo", + "bacteriologista", + "badulaque", + "baforada", + "baixote", + "bajular", + "balzaquiana", + "bambuzal", + "banzo", + "baoba", + "baqueta", + "barulho", + "bastonete", + "batuta", + "bauxita", + "bavaro", + "bazuca", + "bcrepuscular", + "beato", + "beduino", + "begonia", + "behaviorista", + "beisebol", + "belzebu", + "bemol", + "benzido", + "beocio", + "bequer", + "berro", + "besuntar", + "betume", + "bexiga", + "bezerro", + "biatlon", + "biboca", + "bicuspide", + "bidirecional", + "bienio", + "bifurcar", + "bigorna", + "bijuteria", + "bimotor", + "binormal", + "bioxido", + "bipolarizacao", + "biquini", + "birutice", + "bisturi", + "bituca", + "biunivoco", + "bivalve", + "bizarro", + "blasfemo", + "blenorreia", + "blindar", + "bloqueio", + "blusao", + "boazuda", + "bofete", + "bojudo", + "bolso", + "bombordo", + "bonzo", + "botina", + "boquiaberto", + "bostoniano", + "botulismo", + "bourbon", + "bovino", + "boximane", + "bravura", + "brevidade", + "britar", + "broxar", + "bruno", + "bruxuleio", + "bubonico", + "bucolico", + "buda", + "budista", + "bueiro", + "buffer", + "bugre", + "bujao", + "bumerangue", + "burundines", + "busto", + "butique", + "buzios", + "caatinga", + "cabuqui", + "cacunda", + "cafuzo", + "cajueiro", + "camurca", + "canudo", + "caquizeiro", + "carvoeiro", + "casulo", + "catuaba", + "cauterizar", + "cebolinha", + "cedula", + "ceifeiro", + "celulose", + "cerzir", + "cesto", + "cetro", + "ceus", + "cevar", + "chavena", + "cheroqui", + "chita", + "chovido", + "chuvoso", + "ciatico", + "cibernetico", + "cicuta", + "cidreira", + "cientistas", + "cifrar", + "cigarro", + "cilio", + "cimo", + "cinzento", + "cioso", + "cipriota", + "cirurgico", + "cisto", + "citrico", + "ciumento", + "civismo", + "clavicula", + "clero", + "clitoris", + "cluster", + "coaxial", + "cobrir", + "cocota", + "codorniz", + "coexistir", + "cogumelo", + "coito", + "colusao", + "compaixao", + "comutativo", + "contentamento", + "convulsivo", + "coordenativa", + "coquetel", + "correto", + "corvo", + "costureiro", + "cotovia", + "covil", + "cozinheiro", + "cretino", + "cristo", + "crivo", + "crotalo", + "cruzes", + "cubo", + "cucuia", + "cueiro", + "cuidar", + "cujo", + "cultural", + "cunilingua", + "cupula", + "curvo", + "custoso", + "cutucar", + "czarismo", + "dablio", + "dacota", + "dados", + "daguerreotipo", + "daiquiri", + "daltonismo", + "damista", + "dantesco", + "daquilo", + "darwinista", + "dasein", + "dativo", + "deao", + "debutantes", + "decurso", + "deduzir", + "defunto", + "degustar", + "dejeto", + "deltoide", + "demover", + "denunciar", + "deputado", + "deque", + "dervixe", + "desvirtuar", + "deturpar", + "deuteronomio", + "devoto", + "dextrose", + "dezoito", + "diatribe", + "dicotomico", + "didatico", + "dietista", + "difuso", + "digressao", + "diluvio", + "diminuto", + "dinheiro", + "dinossauro", + "dioxido", + "diplomatico", + "dique", + "dirimivel", + "disturbio", + "diurno", + "divulgar", + "dizivel", + "doar", + "dobro", + "docura", + "dodoi", + "doer", + "dogue", + "doloso", + "domo", + "donzela", + "doping", + "dorsal", + "dossie", + "dote", + "doutro", + "doze", + "dravidico", + "dreno", + "driver", + "dropes", + "druso", + "dubnio", + "ducto", + "dueto", + "dulija", + "dundum", + "duodeno", + "duquesa", + "durou", + "duvidoso", + "duzia", + "ebano", + "ebrio", + "eburneo", + "echarpe", + "eclusa", + "ecossistema", + "ectoplasma", + "ecumenismo", + "eczema", + "eden", + "editorial", + "edredom", + "edulcorar", + "efetuar", + "efigie", + "efluvio", + "egiptologo", + "egresso", + "egua", + "einsteiniano", + "eira", + "eivar", + "eixos", + "ejetar", + "elastomero", + "eldorado", + "elixir", + "elmo", + "eloquente", + "elucidativo", + "emaranhar", + "embutir", + "emerito", + "emfa", + "emitir", + "emotivo", + "empuxo", + "emulsao", + "enamorar", + "encurvar", + "enduro", + "enevoar", + "enfurnar", + "enguico", + "enho", + "enigmista", + "enlutar", + "enormidade", + "enpreendimento", + "enquanto", + "enriquecer", + "enrugar", + "entusiastico", + "enunciar", + "envolvimento", + "enxuto", + "enzimatico", + "eolico", + "epiteto", + "epoxi", + "epura", + "equivoco", + "erario", + "erbio", + "ereto", + "erguido", + "erisipela", + "ermo", + "erotizar", + "erros", + "erupcao", + "ervilha", + "esburacar", + "escutar", + "esfuziante", + "esguio", + "esloveno", + "esmurrar", + "esoterismo", + "esperanca", + "espirito", + "espurio", + "essencialmente", + "esturricar", + "esvoacar", + "etario", + "eterno", + "etiquetar", + "etnologo", + "etos", + "etrusco", + "euclidiano", + "euforico", + "eugenico", + "eunuco", + "europio", + "eustaquio", + "eutanasia", + "evasivo", + "eventualidade", + "evitavel", + "evoluir", + "exaustor", + "excursionista", + "exercito", + "exfoliado", + "exito", + "exotico", + "expurgo", + "exsudar", + "extrusora", + "exumar", + "fabuloso", + "facultativo", + "fado", + "fagulha", + "faixas", + "fajuto", + "faltoso", + "famoso", + "fanzine", + "fapesp", + "faquir", + "fartura", + "fastio", + "faturista", + "fausto", + "favorito", + "faxineira", + "fazer", + "fealdade", + "febril", + "fecundo", + "fedorento", + "feerico", + "feixe", + "felicidade", + "felpudo", + "feltro", + "femur", + "fenotipo", + "fervura", + "festivo", + "feto", + "feudo", + "fevereiro", + "fezinha", + "fiasco", + "fibra", + "ficticio", + "fiduciario", + "fiesp", + "fifa", + "figurino", + "fijiano", + "filtro", + "finura", + "fiorde", + "fiquei", + "firula", + "fissurar", + "fitoteca", + "fivela", + "fixo", + "flavio", + "flexor", + "flibusteiro", + "flotilha", + "fluxograma", + "fobos", + "foco", + "fofura", + "foguista", + "foie", + "foliculo", + "fominha", + "fonte", + "forum", + "fosso", + "fotossintese", + "foxtrote", + "fraudulento", + "frevo", + "frivolo", + "frouxo", + "frutose", + "fuba", + "fucsia", + "fugitivo", + "fuinha", + "fujao", + "fulustreco", + "fumo", + "funileiro", + "furunculo", + "fustigar", + "futurologo", + "fuxico", + "fuzue", + "gabriel", + "gado", + "gaelico", + "gafieira", + "gaguejo", + "gaivota", + "gajo", + "galvanoplastico", + "gamo", + "ganso", + "garrucha", + "gastronomo", + "gatuno", + "gaussiano", + "gaviao", + "gaxeta", + "gazeteiro", + "gear", + "geiser", + "geminiano", + "generoso", + "genuino", + "geossinclinal", + "gerundio", + "gestual", + "getulista", + "gibi", + "gigolo", + "gilete", + "ginseng", + "giroscopio", + "glaucio", + "glacial", + "gleba", + "glifo", + "glote", + "glutonia", + "gnostico", + "goela", + "gogo", + "goitaca", + "golpista", + "gomo", + "gonzo", + "gorro", + "gostou", + "goticula", + "gourmet", + "governo", + "gozo", + "graxo", + "grevista", + "grito", + "grotesco", + "gruta", + "guaxinim", + "gude", + "gueto", + "guizo", + "guloso", + "gume", + "guru", + "gustativo", + "grelhado", + "gutural", + "habitue", + "haitiano", + "halterofilista", + "hamburguer", + "hanseniase", + "happening", + "harpista", + "hastear", + "haveres", + "hebreu", + "hectometro", + "hedonista", + "hegira", + "helena", + "helminto", + "hemorroidas", + "henrique", + "heptassilabo", + "hertziano", + "hesitar", + "heterossexual", + "heuristico", + "hexagono", + "hiato", + "hibrido", + "hidrostatico", + "hieroglifo", + "hifenizar", + "higienizar", + "hilario", + "himen", + "hino", + "hippie", + "hirsuto", + "historiografia", + "hitlerista", + "hodometro", + "hoje", + "holograma", + "homus", + "honroso", + "hoquei", + "horto", + "hostilizar", + "hotentote", + "huguenote", + "humilde", + "huno", + "hurra", + "hutu", + "iaia", + "ialorixa", + "iambico", + "iansa", + "iaque", + "iara", + "iatista", + "iberico", + "ibis", + "icar", + "iceberg", + "icosagono", + "idade", + "ideologo", + "idiotice", + "idoso", + "iemenita", + "iene", + "igarape", + "iglu", + "ignorar", + "igreja", + "iguaria", + "iidiche", + "ilativo", + "iletrado", + "ilharga", + "ilimitado", + "ilogismo", + "ilustrissimo", + "imaturo", + "imbuzeiro", + "imerso", + "imitavel", + "imovel", + "imputar", + "imutavel", + "inaveriguavel", + "incutir", + "induzir", + "inextricavel", + "infusao", + "ingua", + "inhame", + "iniquo", + "injusto", + "inning", + "inoxidavel", + "inquisitorial", + "insustentavel", + "intumescimento", + "inutilizavel", + "invulneravel", + "inzoneiro", + "iodo", + "iogurte", + "ioio", + "ionosfera", + "ioruba", + "iota", + "ipsilon", + "irascivel", + "iris", + "irlandes", + "irmaos", + "iroques", + "irrupcao", + "isca", + "isento", + "islandes", + "isotopo", + "isqueiro", + "israelita", + "isso", + "isto", + "iterbio", + "itinerario", + "itrio", + "iuane", + "iugoslavo", + "jabuticabeira", + "jacutinga", + "jade", + "jagunco", + "jainista", + "jaleco", + "jambo", + "jantarada", + "japones", + "jaqueta", + "jarro", + "jasmim", + "jato", + "jaula", + "javel", + "jazz", + "jegue", + "jeitoso", + "jejum", + "jenipapo", + "jeova", + "jequitiba", + "jersei", + "jesus", + "jetom", + "jiboia", + "jihad", + "jilo", + "jingle", + "jipe", + "jocoso", + "joelho", + "joguete", + "joio", + "jojoba", + "jorro", + "jota", + "joule", + "joviano", + "jubiloso", + "judoca", + "jugular", + "juizo", + "jujuba", + "juliano", + "jumento", + "junto", + "jururu", + "justo", + "juta", + "juventude", + "labutar", + "laguna", + "laico", + "lajota", + "lanterninha", + "lapso", + "laquear", + "lastro", + "lauto", + "lavrar", + "laxativo", + "lazer", + "leasing", + "lebre", + "lecionar", + "ledo", + "leguminoso", + "leitura", + "lele", + "lemure", + "lento", + "leonardo", + "leopardo", + "lepton", + "leque", + "leste", + "letreiro", + "leucocito", + "levitico", + "lexicologo", + "lhama", + "lhufas", + "liame", + "licoroso", + "lidocaina", + "liliputiano", + "limusine", + "linotipo", + "lipoproteina", + "liquidos", + "lirismo", + "lisura", + "liturgico", + "livros", + "lixo", + "lobulo", + "locutor", + "lodo", + "logro", + "lojista", + "lombriga", + "lontra", + "loop", + "loquaz", + "lorota", + "losango", + "lotus", + "louvor", + "luar", + "lubrificavel", + "lucros", + "lugubre", + "luis", + "luminoso", + "luneta", + "lustroso", + "luto", + "luvas", + "luxuriante", + "luzeiro", + "maduro", + "maestro", + "mafioso", + "magro", + "maiuscula", + "majoritario", + "malvisto", + "mamute", + "manutencao", + "mapoteca", + "maquinista", + "marzipa", + "masturbar", + "matuto", + "mausoleu", + "mavioso", + "maxixe", + "mazurca", + "meandro", + "mecha", + "medusa", + "mefistofelico", + "megera", + "meirinho", + "melro", + "memorizar", + "menu", + "mequetrefe", + "mertiolate", + "mestria", + "metroviario", + "mexilhao", + "mezanino", + "miau", + "microssegundo", + "midia", + "migratorio", + "mimosa", + "minuto", + "miosotis", + "mirtilo", + "misturar", + "mitzvah", + "miudos", + "mixuruca", + "mnemonico", + "moagem", + "mobilizar", + "modulo", + "moer", + "mofo", + "mogno", + "moita", + "molusco", + "monumento", + "moqueca", + "morubixaba", + "mostruario", + "motriz", + "mouse", + "movivel", + "mozarela", + "muarra", + "muculmano", + "mudo", + "mugir", + "muitos", + "mumunha", + "munir", + "muon", + "muquira", + "murros", + "musselina", + "nacoes", + "nado", + "naftalina", + "nago", + "naipe", + "naja", + "nalgum", + "namoro", + "nanquim", + "napolitano", + "naquilo", + "nascimento", + "nautilo", + "navios", + "nazista", + "nebuloso", + "nectarina", + "nefrologo", + "negus", + "nelore", + "nenufar", + "nepotismo", + "nervura", + "neste", + "netuno", + "neutron", + "nevoeiro", + "newtoniano", + "nexo", + "nhenhenhem", + "nhoque", + "nigeriano", + "niilista", + "ninho", + "niobio", + "niponico", + "niquelar", + "nirvana", + "nisto", + "nitroglicerina", + "nivoso", + "nobreza", + "nocivo", + "noel", + "nogueira", + "noivo", + "nojo", + "nominativo", + "nonuplo", + "noruegues", + "nostalgico", + "noturno", + "nouveau", + "nuanca", + "nublar", + "nucleotideo", + "nudista", + "nulo", + "numismatico", + "nunquinha", + "nupcias", + "nutritivo", + "nuvens", + "oasis", + "obcecar", + "obeso", + "obituario", + "objetos", + "oblongo", + "obnoxio", + "obrigatorio", + "obstruir", + "obtuso", + "obus", + "obvio", + "ocaso", + "occipital", + "oceanografo", + "ocioso", + "oclusivo", + "ocorrer", + "ocre", + "octogono", + "odalisca", + "odisseia", + "odorifico", + "oersted", + "oeste", + "ofertar", + "ofidio", + "oftalmologo", + "ogiva", + "ogum", + "oigale", + "oitavo", + "oitocentos", + "ojeriza", + "olaria", + "oleoso", + "olfato", + "olhos", + "oliveira", + "olmo", + "olor", + "olvidavel", + "ombudsman", + "omeleteira", + "omitir", + "omoplata", + "onanismo", + "ondular", + "oneroso", + "onomatopeico", + "ontologico", + "onus", + "onze", + "opalescente", + "opcional", + "operistico", + "opio", + "oposto", + "oprobrio", + "optometrista", + "opusculo", + "oratorio", + "orbital", + "orcar", + "orfao", + "orixa", + "orla", + "ornitologo", + "orquidea", + "ortorrombico", + "orvalho", + "osculo", + "osmotico", + "ossudo", + "ostrogodo", + "otario", + "otite", + "ouro", + "ousar", + "outubro", + "ouvir", + "ovario", + "overnight", + "oviparo", + "ovni", + "ovoviviparo", + "ovulo", + "oxala", + "oxente", + "oxiuro", + "oxossi", + "ozonizar", + "paciente", + "pactuar", + "padronizar", + "paete", + "pagodeiro", + "paixao", + "pajem", + "paludismo", + "pampas", + "panturrilha", + "papudo", + "paquistanes", + "pastoso", + "patua", + "paulo", + "pauzinhos", + "pavoroso", + "paxa", + "pazes", + "peao", + "pecuniario", + "pedunculo", + "pegaso", + "peixinho", + "pejorativo", + "pelvis", + "penuria", + "pequno", + "petunia", + "pezada", + "piauiense", + "pictorico", + "pierro", + "pigmeu", + "pijama", + "pilulas", + "pimpolho", + "pintura", + "piorar", + "pipocar", + "piqueteiro", + "pirulito", + "pistoleiro", + "pituitaria", + "pivotar", + "pixote", + "pizzaria", + "plistoceno", + "plotar", + "pluviometrico", + "pneumonico", + "poco", + "podridao", + "poetisa", + "pogrom", + "pois", + "polvorosa", + "pomposo", + "ponderado", + "pontudo", + "populoso", + "poquer", + "porvir", + "posudo", + "potro", + "pouso", + "povoar", + "prazo", + "prezar", + "privilegios", + "proximo", + "prussiano", + "pseudopode", + "psoriase", + "pterossauros", + "ptialina", + "ptolemaico", + "pudor", + "pueril", + "pufe", + "pugilista", + "puir", + "pujante", + "pulverizar", + "pumba", + "punk", + "purulento", + "pustula", + "putsch", + "puxe", + "quatrocentos", + "quetzal", + "quixotesco", + "quotizavel", + "rabujice", + "racista", + "radonio", + "rafia", + "ragu", + "rajado", + "ralo", + "rampeiro", + "ranzinza", + "raptor", + "raquitismo", + "raro", + "rasurar", + "ratoeira", + "ravioli", + "razoavel", + "reavivar", + "rebuscar", + "recusavel", + "reduzivel", + "reexposicao", + "refutavel", + "regurgitar", + "reivindicavel", + "rejuvenescimento", + "relva", + "remuneravel", + "renunciar", + "reorientar", + "repuxo", + "requisito", + "resumo", + "returno", + "reutilizar", + "revolvido", + "rezonear", + "riacho", + "ribossomo", + "ricota", + "ridiculo", + "rifle", + "rigoroso", + "rijo", + "rimel", + "rins", + "rios", + "riqueza", + "respeito", + "rissole", + "ritualistico", + "rivalizar", + "rixa", + "robusto", + "rococo", + "rodoviario", + "roer", + "rogo", + "rojao", + "rolo", + "rompimento", + "ronronar", + "roqueiro", + "rorqual", + "rosto", + "rotundo", + "rouxinol", + "roxo", + "royal", + "ruas", + "rucula", + "rudimentos", + "ruela", + "rufo", + "rugoso", + "ruivo", + "rule", + "rumoroso", + "runico", + "ruptura", + "rural", + "rustico", + "rutilar", + "saariano", + "sabujo", + "sacudir", + "sadomasoquista", + "safra", + "sagui", + "sais", + "samurai", + "santuario", + "sapo", + "saquear", + "sartriano", + "saturno", + "saude", + "sauva", + "saveiro", + "saxofonista", + "sazonal", + "scherzo", + "script", + "seara", + "seborreia", + "secura", + "seduzir", + "sefardim", + "seguro", + "seja", + "selvas", + "sempre", + "senzala", + "sepultura", + "sequoia", + "sestercio", + "setuplo", + "seus", + "seviciar", + "sezonismo", + "shalom", + "siames", + "sibilante", + "sicrano", + "sidra", + "sifilitico", + "signos", + "silvo", + "simultaneo", + "sinusite", + "sionista", + "sirio", + "sisudo", + "situar", + "sivan", + "slide", + "slogan", + "soar", + "sobrio", + "socratico", + "sodomizar", + "soerguer", + "software", + "sogro", + "soja", + "solver", + "somente", + "sonso", + "sopro", + "soquete", + "sorveteiro", + "sossego", + "soturno", + "sousafone", + "sovinice", + "sozinho", + "suavizar", + "subverter", + "sucursal", + "sudoriparo", + "sufragio", + "sugestoes", + "suite", + "sujo", + "sultao", + "sumula", + "suntuoso", + "suor", + "supurar", + "suruba", + "susto", + "suturar", + "suvenir", + "tabuleta", + "taco", + "tadjique", + "tafeta", + "tagarelice", + "taitiano", + "talvez", + "tampouco", + "tanzaniano", + "taoista", + "tapume", + "taquion", + "tarugo", + "tascar", + "tatuar", + "tautologico", + "tavola", + "taxionomista", + "tchecoslovaco", + "teatrologo", + "tectonismo", + "tedioso", + "teflon", + "tegumento", + "teixo", + "telurio", + "temporas", + "tenue", + "teosofico", + "tepido", + "tequila", + "terrorista", + "testosterona", + "tetrico", + "teutonico", + "teve", + "texugo", + "tiara", + "tibia", + "tiete", + "tifoide", + "tigresa", + "tijolo", + "tilintar", + "timpano", + "tintureiro", + "tiquete", + "tiroteio", + "tisico", + "titulos", + "tive", + "toar", + "toboga", + "tofu", + "togoles", + "toicinho", + "tolueno", + "tomografo", + "tontura", + "toponimo", + "toquio", + "torvelinho", + "tostar", + "toto", + "touro", + "toxina", + "trazer", + "trezentos", + "trivialidade", + "trovoar", + "truta", + "tuaregue", + "tubular", + "tucano", + "tudo", + "tufo", + "tuiste", + "tulipa", + "tumultuoso", + "tunisino", + "tupiniquim", + "turvo", + "tutu", + "ucraniano", + "udenista", + "ufanista", + "ufologo", + "ugaritico", + "uiste", + "uivo", + "ulceroso", + "ulema", + "ultravioleta", + "umbilical", + "umero", + "umido", + "umlaut", + "unanimidade", + "unesco", + "ungulado", + "unheiro", + "univoco", + "untuoso", + "urano", + "urbano", + "urdir", + "uretra", + "urgente", + "urinol", + "urna", + "urologo", + "urro", + "ursulina", + "urtiga", + "urupe", + "usavel", + "usbeque", + "usei", + "usineiro", + "usurpar", + "utero", + "utilizar", + "utopico", + "uvular", + "uxoricidio", + "vacuo", + "vadio", + "vaguear", + "vaivem", + "valvula", + "vampiro", + "vantajoso", + "vaporoso", + "vaquinha", + "varziano", + "vasto", + "vaticinio", + "vaudeville", + "vazio", + "veado", + "vedico", + "veemente", + "vegetativo", + "veio", + "veja", + "veludo", + "venusiano", + "verdade", + "verve", + "vestuario", + "vetusto", + "vexatorio", + "vezes", + "viavel", + "vibratorio", + "victor", + "vicunha", + "vidros", + "vietnamita", + "vigoroso", + "vilipendiar", + "vime", + "vintem", + "violoncelo", + "viquingue", + "virus", + "visualizar", + "vituperio", + "viuvo", + "vivo", + "vizir", + "voar", + "vociferar", + "vodu", + "vogar", + "voile", + "volver", + "vomito", + "vontade", + "vortice", + "vosso", + "voto", + "vovozinha", + "voyeuse", + "vozes", + "vulva", + "vupt", + "western", + "xadrez", + "xale", + "xampu", + "xango", + "xarope", + "xaual", + "xavante", + "xaxim", + "xenonio", + "xepa", + "xerox", + "xicara", + "xifopago", + "xiita", + "xilogravura", + "xinxim", + "xistoso", + "xixi", + "xodo", + "xogum", + "xucro", + "zabumba", + "zagueiro", + "zambiano", + "zanzar", + "zarpar", + "zebu", + "zefiro", + "zeloso", + "zenite", + "zumbi" + ]; +} \ No newline at end of file diff --git a/cw_wownero/lib/mnemonics/russian.dart b/cw_wownero/lib/mnemonics/russian.dart new file mode 100644 index 000000000..f10af0ff6 --- /dev/null +++ b/cw_wownero/lib/mnemonics/russian.dart @@ -0,0 +1,1630 @@ +class RussianMnemonics { + static const words = [ + "абажур", + "абзац", + "абонент", + "абрикос", + "абсурд", + "авангард", + "август", + "авиация", + "авоська", + "автор", + "агат", + "агент", + "агитатор", + "агнец", + "агония", + "агрегат", + "адвокат", + "адмирал", + "адрес", + "ажиотаж", + "азарт", + "азбука", + "азот", + "аист", + "айсберг", + "академия", + "аквариум", + "аккорд", + "акробат", + "аксиома", + "актер", + "акула", + "акция", + "алгоритм", + "алебарда", + "аллея", + "алмаз", + "алтарь", + "алфавит", + "алхимик", + "алый", + "альбом", + "алюминий", + "амбар", + "аметист", + "амнезия", + "ампула", + "амфора", + "анализ", + "ангел", + "анекдот", + "анимация", + "анкета", + "аномалия", + "ансамбль", + "антенна", + "апатия", + "апельсин", + "апофеоз", + "аппарат", + "апрель", + "аптека", + "арабский", + "арбуз", + "аргумент", + "арест", + "ария", + "арка", + "армия", + "аромат", + "арсенал", + "артист", + "архив", + "аршин", + "асбест", + "аскетизм", + "аспект", + "ассорти", + "астроном", + "асфальт", + "атака", + "ателье", + "атлас", + "атом", + "атрибут", + "аудитор", + "аукцион", + "аура", + "афера", + "афиша", + "ахинея", + "ацетон", + "аэропорт", + "бабушка", + "багаж", + "бадья", + "база", + "баклажан", + "балкон", + "бампер", + "банк", + "барон", + "бассейн", + "батарея", + "бахрома", + "башня", + "баян", + "бегство", + "бедро", + "бездна", + "бекон", + "белый", + "бензин", + "берег", + "беседа", + "бетонный", + "биатлон", + "библия", + "бивень", + "бигуди", + "бидон", + "бизнес", + "бикини", + "билет", + "бинокль", + "биология", + "биржа", + "бисер", + "битва", + "бицепс", + "благо", + "бледный", + "близкий", + "блок", + "блуждать", + "блюдо", + "бляха", + "бобер", + "богатый", + "бодрый", + "боевой", + "бокал", + "большой", + "борьба", + "босой", + "ботинок", + "боцман", + "бочка", + "боярин", + "брать", + "бревно", + "бригада", + "бросать", + "брызги", + "брюки", + "бублик", + "бугор", + "будущее", + "буква", + "бульвар", + "бумага", + "бунт", + "бурный", + "бусы", + "бутылка", + "буфет", + "бухта", + "бушлат", + "бывалый", + "быль", + "быстрый", + "быть", + "бюджет", + "бюро", + "бюст", + "вагон", + "важный", + "ваза", + "вакцина", + "валюта", + "вампир", + "ванная", + "вариант", + "вассал", + "вата", + "вафля", + "вахта", + "вдова", + "вдыхать", + "ведущий", + "веер", + "вежливый", + "везти", + "веко", + "великий", + "вена", + "верить", + "веселый", + "ветер", + "вечер", + "вешать", + "вещь", + "веяние", + "взаимный", + "взбучка", + "взвод", + "взгляд", + "вздыхать", + "взлетать", + "взмах", + "взнос", + "взор", + "взрыв", + "взывать", + "взятка", + "вибрация", + "визит", + "вилка", + "вино", + "вирус", + "висеть", + "витрина", + "вихрь", + "вишневый", + "включать", + "вкус", + "власть", + "влечь", + "влияние", + "влюблять", + "внешний", + "внимание", + "внук", + "внятный", + "вода", + "воевать", + "вождь", + "воздух", + "войти", + "вокзал", + "волос", + "вопрос", + "ворота", + "восток", + "впадать", + "впускать", + "врач", + "время", + "вручать", + "всадник", + "всеобщий", + "вспышка", + "встреча", + "вторник", + "вулкан", + "вурдалак", + "входить", + "въезд", + "выбор", + "вывод", + "выгодный", + "выделять", + "выезжать", + "выживать", + "вызывать", + "выигрыш", + "вылезать", + "выносить", + "выпивать", + "высокий", + "выходить", + "вычет", + "вышка", + "выяснять", + "вязать", + "вялый", + "гавань", + "гадать", + "газета", + "гаишник", + "галстук", + "гамма", + "гарантия", + "гастроли", + "гвардия", + "гвоздь", + "гектар", + "гель", + "генерал", + "геолог", + "герой", + "гешефт", + "гибель", + "гигант", + "гильза", + "гимн", + "гипотеза", + "гитара", + "глаз", + "глина", + "глоток", + "глубокий", + "глыба", + "глядеть", + "гнать", + "гнев", + "гнить", + "гном", + "гнуть", + "говорить", + "годовой", + "голова", + "гонка", + "город", + "гость", + "готовый", + "граница", + "грех", + "гриб", + "громкий", + "группа", + "грызть", + "грязный", + "губа", + "гудеть", + "гулять", + "гуманный", + "густой", + "гуща", + "давать", + "далекий", + "дама", + "данные", + "дарить", + "дать", + "дача", + "дверь", + "движение", + "двор", + "дебют", + "девушка", + "дедушка", + "дежурный", + "дезертир", + "действие", + "декабрь", + "дело", + "демократ", + "день", + "депутат", + "держать", + "десяток", + "детский", + "дефицит", + "дешевый", + "деятель", + "джаз", + "джинсы", + "джунгли", + "диалог", + "диван", + "диета", + "дизайн", + "дикий", + "динамика", + "диплом", + "директор", + "диск", + "дитя", + "дичь", + "длинный", + "дневник", + "добрый", + "доверие", + "договор", + "дождь", + "доза", + "документ", + "должен", + "домашний", + "допрос", + "дорога", + "доход", + "доцент", + "дочь", + "дощатый", + "драка", + "древний", + "дрожать", + "друг", + "дрянь", + "дубовый", + "дуга", + "дудка", + "дукат", + "дуло", + "думать", + "дупло", + "дурак", + "дуть", + "духи", + "душа", + "дуэт", + "дымить", + "дыня", + "дыра", + "дыханье", + "дышать", + "дьявол", + "дюжина", + "дюйм", + "дюна", + "дядя", + "дятел", + "егерь", + "единый", + "едкий", + "ежевика", + "ежик", + "езда", + "елка", + "емкость", + "ерунда", + "ехать", + "жадный", + "жажда", + "жалеть", + "жанр", + "жара", + "жать", + "жгучий", + "ждать", + "жевать", + "желание", + "жемчуг", + "женщина", + "жертва", + "жесткий", + "жечь", + "живой", + "жидкость", + "жизнь", + "жилье", + "жирный", + "житель", + "журнал", + "жюри", + "забывать", + "завод", + "загадка", + "задача", + "зажечь", + "зайти", + "закон", + "замечать", + "занимать", + "западный", + "зарплата", + "засыпать", + "затрата", + "захват", + "зацепка", + "зачет", + "защита", + "заявка", + "звать", + "звезда", + "звонить", + "звук", + "здание", + "здешний", + "здоровье", + "зебра", + "зевать", + "зеленый", + "земля", + "зенит", + "зеркало", + "зефир", + "зигзаг", + "зима", + "зиять", + "злак", + "злой", + "змея", + "знать", + "зной", + "зодчий", + "золотой", + "зомби", + "зона", + "зоопарк", + "зоркий", + "зрачок", + "зрение", + "зритель", + "зубной", + "зыбкий", + "зять", + "игла", + "иголка", + "играть", + "идея", + "идиот", + "идол", + "идти", + "иерархия", + "избрать", + "известие", + "изгонять", + "издание", + "излагать", + "изменять", + "износ", + "изоляция", + "изрядный", + "изучать", + "изымать", + "изящный", + "икона", + "икра", + "иллюзия", + "имбирь", + "иметь", + "имидж", + "иммунный", + "империя", + "инвестор", + "индивид", + "инерция", + "инженер", + "иномарка", + "институт", + "интерес", + "инфекция", + "инцидент", + "ипподром", + "ирис", + "ирония", + "искать", + "история", + "исходить", + "исчезать", + "итог", + "июль", + "июнь", + "кабинет", + "кавалер", + "кадр", + "казарма", + "кайф", + "кактус", + "калитка", + "камень", + "канал", + "капитан", + "картина", + "касса", + "катер", + "кафе", + "качество", + "каша", + "каюта", + "квартира", + "квинтет", + "квота", + "кедр", + "кекс", + "кенгуру", + "кепка", + "керосин", + "кетчуп", + "кефир", + "кибитка", + "кивнуть", + "кидать", + "километр", + "кино", + "киоск", + "кипеть", + "кирпич", + "кисть", + "китаец", + "класс", + "клетка", + "клиент", + "клоун", + "клуб", + "клык", + "ключ", + "клятва", + "книга", + "кнопка", + "кнут", + "князь", + "кобура", + "ковер", + "коготь", + "кодекс", + "кожа", + "козел", + "койка", + "коктейль", + "колено", + "компания", + "конец", + "копейка", + "короткий", + "костюм", + "котел", + "кофе", + "кошка", + "красный", + "кресло", + "кричать", + "кровь", + "крупный", + "крыша", + "крючок", + "кубок", + "кувшин", + "кудрявый", + "кузов", + "кукла", + "культура", + "кумир", + "купить", + "курс", + "кусок", + "кухня", + "куча", + "кушать", + "кювет", + "лабиринт", + "лавка", + "лагерь", + "ладонь", + "лазерный", + "лайнер", + "лакей", + "лампа", + "ландшафт", + "лапа", + "ларек", + "ласковый", + "лауреат", + "лачуга", + "лаять", + "лгать", + "лебедь", + "левый", + "легкий", + "ледяной", + "лежать", + "лекция", + "лента", + "лепесток", + "лесной", + "лето", + "лечь", + "леший", + "лживый", + "либерал", + "ливень", + "лига", + "лидер", + "ликовать", + "лиловый", + "лимон", + "линия", + "липа", + "лирика", + "лист", + "литр", + "лифт", + "лихой", + "лицо", + "личный", + "лишний", + "лобовой", + "ловить", + "логика", + "лодка", + "ложка", + "лозунг", + "локоть", + "ломать", + "лоно", + "лопата", + "лорд", + "лось", + "лоток", + "лохматый", + "лошадь", + "лужа", + "лукавый", + "луна", + "лупить", + "лучший", + "лыжный", + "лысый", + "львиный", + "льгота", + "льдина", + "любить", + "людской", + "люстра", + "лютый", + "лягушка", + "магазин", + "мадам", + "мазать", + "майор", + "максимум", + "мальчик", + "манера", + "март", + "масса", + "мать", + "мафия", + "махать", + "мачта", + "машина", + "маэстро", + "маяк", + "мгла", + "мебель", + "медведь", + "мелкий", + "мемуары", + "менять", + "мера", + "место", + "метод", + "механизм", + "мечтать", + "мешать", + "миграция", + "мизинец", + "микрофон", + "миллион", + "минута", + "мировой", + "миссия", + "митинг", + "мишень", + "младший", + "мнение", + "мнимый", + "могила", + "модель", + "мозг", + "мойка", + "мокрый", + "молодой", + "момент", + "монах", + "море", + "мост", + "мотор", + "мохнатый", + "мочь", + "мошенник", + "мощный", + "мрачный", + "мстить", + "мудрый", + "мужчина", + "музыка", + "мука", + "мумия", + "мундир", + "муравей", + "мусор", + "мутный", + "муфта", + "муха", + "мучить", + "мушкетер", + "мыло", + "мысль", + "мыть", + "мычать", + "мышь", + "мэтр", + "мюзикл", + "мягкий", + "мякиш", + "мясо", + "мятый", + "мячик", + "набор", + "навык", + "нагрузка", + "надежда", + "наемный", + "нажать", + "называть", + "наивный", + "накрыть", + "налог", + "намерен", + "наносить", + "написать", + "народ", + "натура", + "наука", + "нация", + "начать", + "небо", + "невеста", + "негодяй", + "неделя", + "нежный", + "незнание", + "нелепый", + "немалый", + "неправда", + "нервный", + "нести", + "нефть", + "нехватка", + "нечистый", + "неясный", + "нива", + "нижний", + "низкий", + "никель", + "нирвана", + "нить", + "ничья", + "ниша", + "нищий", + "новый", + "нога", + "ножницы", + "ноздря", + "ноль", + "номер", + "норма", + "нота", + "ночь", + "ноша", + "ноябрь", + "нрав", + "нужный", + "нутро", + "нынешний", + "нырнуть", + "ныть", + "нюанс", + "нюхать", + "няня", + "оазис", + "обаяние", + "обвинять", + "обгонять", + "обещать", + "обжигать", + "обзор", + "обида", + "область", + "обмен", + "обнимать", + "оборона", + "образ", + "обучение", + "обходить", + "обширный", + "общий", + "объект", + "обычный", + "обязать", + "овальный", + "овес", + "овощи", + "овраг", + "овца", + "овчарка", + "огненный", + "огонь", + "огромный", + "огурец", + "одежда", + "одинокий", + "одобрить", + "ожидать", + "ожог", + "озарение", + "озеро", + "означать", + "оказать", + "океан", + "оклад", + "окно", + "округ", + "октябрь", + "окурок", + "олень", + "опасный", + "операция", + "описать", + "оплата", + "опора", + "оппонент", + "опрос", + "оптимизм", + "опускать", + "опыт", + "орать", + "орбита", + "орган", + "орден", + "орел", + "оригинал", + "оркестр", + "орнамент", + "оружие", + "осадок", + "освещать", + "осень", + "осина", + "осколок", + "осмотр", + "основной", + "особый", + "осуждать", + "отбор", + "отвечать", + "отдать", + "отец", + "отзыв", + "открытие", + "отмечать", + "относить", + "отпуск", + "отрасль", + "отставка", + "оттенок", + "отходить", + "отчет", + "отъезд", + "офицер", + "охапка", + "охота", + "охрана", + "оценка", + "очаг", + "очередь", + "очищать", + "очки", + "ошейник", + "ошибка", + "ощущение", + "павильон", + "падать", + "паек", + "пакет", + "палец", + "память", + "панель", + "папка", + "партия", + "паспорт", + "патрон", + "пауза", + "пафос", + "пахнуть", + "пациент", + "пачка", + "пашня", + "певец", + "педагог", + "пейзаж", + "пельмень", + "пенсия", + "пепел", + "период", + "песня", + "петля", + "пехота", + "печать", + "пешеход", + "пещера", + "пианист", + "пиво", + "пиджак", + "пиковый", + "пилот", + "пионер", + "пирог", + "писать", + "пить", + "пицца", + "пишущий", + "пища", + "план", + "плечо", + "плита", + "плохой", + "плыть", + "плюс", + "пляж", + "победа", + "повод", + "погода", + "подумать", + "поехать", + "пожимать", + "позиция", + "поиск", + "покой", + "получать", + "помнить", + "пони", + "поощрять", + "попадать", + "порядок", + "пост", + "поток", + "похожий", + "поцелуй", + "почва", + "пощечина", + "поэт", + "пояснить", + "право", + "предмет", + "проблема", + "пруд", + "прыгать", + "прямой", + "психолог", + "птица", + "публика", + "пугать", + "пудра", + "пузырь", + "пуля", + "пункт", + "пурга", + "пустой", + "путь", + "пухлый", + "пучок", + "пушистый", + "пчела", + "пшеница", + "пыль", + "пытка", + "пыхтеть", + "пышный", + "пьеса", + "пьяный", + "пятно", + "работа", + "равный", + "радость", + "развитие", + "район", + "ракета", + "рамка", + "ранний", + "рапорт", + "рассказ", + "раунд", + "рация", + "рвать", + "реальный", + "ребенок", + "реветь", + "регион", + "редакция", + "реестр", + "режим", + "резкий", + "рейтинг", + "река", + "религия", + "ремонт", + "рента", + "реплика", + "ресурс", + "реформа", + "рецепт", + "речь", + "решение", + "ржавый", + "рисунок", + "ритм", + "рифма", + "робкий", + "ровный", + "рогатый", + "родитель", + "рождение", + "розовый", + "роковой", + "роль", + "роман", + "ронять", + "рост", + "рота", + "роща", + "рояль", + "рубль", + "ругать", + "руда", + "ружье", + "руины", + "рука", + "руль", + "румяный", + "русский", + "ручка", + "рыба", + "рывок", + "рыдать", + "рыжий", + "рынок", + "рысь", + "рыть", + "рыхлый", + "рыцарь", + "рычаг", + "рюкзак", + "рюмка", + "рябой", + "рядовой", + "сабля", + "садовый", + "сажать", + "салон", + "самолет", + "сани", + "сапог", + "сарай", + "сатира", + "сауна", + "сахар", + "сбегать", + "сбивать", + "сбор", + "сбыт", + "свадьба", + "свет", + "свидание", + "свобода", + "связь", + "сгорать", + "сдвигать", + "сеанс", + "северный", + "сегмент", + "седой", + "сезон", + "сейф", + "секунда", + "сельский", + "семья", + "сентябрь", + "сердце", + "сеть", + "сечение", + "сеять", + "сигнал", + "сидеть", + "сизый", + "сила", + "символ", + "синий", + "сирота", + "система", + "ситуация", + "сиять", + "сказать", + "скважина", + "скелет", + "скидка", + "склад", + "скорый", + "скрывать", + "скучный", + "слава", + "слеза", + "слияние", + "слово", + "случай", + "слышать", + "слюна", + "смех", + "смирение", + "смотреть", + "смутный", + "смысл", + "смятение", + "снаряд", + "снег", + "снижение", + "сносить", + "снять", + "событие", + "совет", + "согласие", + "сожалеть", + "сойти", + "сокол", + "солнце", + "сомнение", + "сонный", + "сообщать", + "соперник", + "сорт", + "состав", + "сотня", + "соус", + "социолог", + "сочинять", + "союз", + "спать", + "спешить", + "спина", + "сплошной", + "способ", + "спутник", + "средство", + "срок", + "срывать", + "стать", + "ствол", + "стена", + "стихи", + "сторона", + "страна", + "студент", + "стыд", + "субъект", + "сувенир", + "сугроб", + "судьба", + "суета", + "суждение", + "сукно", + "сулить", + "сумма", + "сунуть", + "супруг", + "суровый", + "сустав", + "суть", + "сухой", + "суша", + "существо", + "сфера", + "схема", + "сцена", + "счастье", + "счет", + "считать", + "сшивать", + "съезд", + "сынок", + "сыпать", + "сырье", + "сытый", + "сыщик", + "сюжет", + "сюрприз", + "таблица", + "таежный", + "таинство", + "тайна", + "такси", + "талант", + "таможня", + "танец", + "тарелка", + "таскать", + "тахта", + "тачка", + "таять", + "тварь", + "твердый", + "творить", + "театр", + "тезис", + "текст", + "тело", + "тема", + "тень", + "теория", + "теплый", + "терять", + "тесный", + "тетя", + "техника", + "течение", + "тигр", + "типичный", + "тираж", + "титул", + "тихий", + "тишина", + "ткань", + "товарищ", + "толпа", + "тонкий", + "топливо", + "торговля", + "тоска", + "точка", + "тощий", + "традиция", + "тревога", + "трибуна", + "трогать", + "труд", + "трюк", + "тряпка", + "туалет", + "тугой", + "туловище", + "туман", + "тундра", + "тупой", + "турнир", + "тусклый", + "туфля", + "туча", + "туша", + "тыкать", + "тысяча", + "тьма", + "тюльпан", + "тюрьма", + "тяга", + "тяжелый", + "тянуть", + "убеждать", + "убирать", + "убогий", + "убыток", + "уважение", + "уверять", + "увлекать", + "угнать", + "угол", + "угроза", + "удар", + "удивлять", + "удобный", + "уезд", + "ужас", + "ужин", + "узел", + "узкий", + "узнавать", + "узор", + "уйма", + "уклон", + "укол", + "уксус", + "улетать", + "улица", + "улучшать", + "улыбка", + "уметь", + "умиление", + "умный", + "умолять", + "умысел", + "унижать", + "уносить", + "уныние", + "упасть", + "уплата", + "упор", + "упрекать", + "упускать", + "уран", + "урна", + "уровень", + "усадьба", + "усердие", + "усилие", + "ускорять", + "условие", + "усмешка", + "уснуть", + "успеть", + "усыпать", + "утешать", + "утка", + "уточнять", + "утро", + "утюг", + "уходить", + "уцелеть", + "участие", + "ученый", + "учитель", + "ушко", + "ущерб", + "уютный", + "уяснять", + "фабрика", + "фаворит", + "фаза", + "файл", + "факт", + "фамилия", + "фантазия", + "фара", + "фасад", + "февраль", + "фельдшер", + "феномен", + "ферма", + "фигура", + "физика", + "фильм", + "финал", + "фирма", + "фишка", + "флаг", + "флейта", + "флот", + "фокус", + "фольклор", + "фонд", + "форма", + "фото", + "фраза", + "фреска", + "фронт", + "фрукт", + "функция", + "фуражка", + "футбол", + "фыркать", + "халат", + "хамство", + "хаос", + "характер", + "хата", + "хватать", + "хвост", + "хижина", + "хилый", + "химия", + "хирург", + "хитрый", + "хищник", + "хлам", + "хлеб", + "хлопать", + "хмурый", + "ходить", + "хозяин", + "хоккей", + "холодный", + "хороший", + "хотеть", + "хохотать", + "храм", + "хрен", + "хриплый", + "хроника", + "хрупкий", + "художник", + "хулиган", + "хутор", + "царь", + "цвет", + "цель", + "цемент", + "центр", + "цепь", + "церковь", + "цикл", + "цилиндр", + "циничный", + "цирк", + "цистерна", + "цитата", + "цифра", + "цыпленок", + "чадо", + "чайник", + "часть", + "чашка", + "человек", + "чемодан", + "чепуха", + "черный", + "честь", + "четкий", + "чехол", + "чиновник", + "число", + "читать", + "членство", + "чреватый", + "чтение", + "чувство", + "чугунный", + "чудо", + "чужой", + "чукча", + "чулок", + "чума", + "чуткий", + "чучело", + "чушь", + "шаблон", + "шагать", + "шайка", + "шакал", + "шалаш", + "шампунь", + "шанс", + "шапка", + "шарик", + "шасси", + "шатер", + "шахта", + "шашлык", + "швейный", + "швырять", + "шевелить", + "шедевр", + "шейка", + "шелковый", + "шептать", + "шерсть", + "шестерка", + "шикарный", + "шинель", + "шипеть", + "широкий", + "шить", + "шишка", + "шкаф", + "школа", + "шкура", + "шланг", + "шлем", + "шлюпка", + "шляпа", + "шнур", + "шоколад", + "шорох", + "шоссе", + "шофер", + "шпага", + "шпион", + "шприц", + "шрам", + "шрифт", + "штаб", + "штора", + "штраф", + "штука", + "штык", + "шуба", + "шуметь", + "шуршать", + "шутка", + "щадить", + "щедрый", + "щека", + "щель", + "щенок", + "щепка", + "щетка", + "щука", + "эволюция", + "эгоизм", + "экзамен", + "экипаж", + "экономия", + "экран", + "эксперт", + "элемент", + "элита", + "эмблема", + "эмигрант", + "эмоция", + "энергия", + "эпизод", + "эпоха", + "эскиз", + "эссе", + "эстрада", + "этап", + "этика", + "этюд", + "эфир", + "эффект", + "эшелон", + "юбилей", + "юбка", + "южный", + "юмор", + "юноша", + "юрист", + "яблоко", + "явление", + "ягода", + "ядерный", + "ядовитый", + "ядро", + "язва", + "язык", + "яйцо", + "якорь", + "январь", + "японец", + "яркий", + "ярмарка", + "ярость", + "ярус", + "ясный", + "яхта", + "ячейка", + "ящик" + ]; +} \ No newline at end of file diff --git a/cw_wownero/lib/mnemonics/spanish.dart b/cw_wownero/lib/mnemonics/spanish.dart new file mode 100644 index 000000000..531eafd35 --- /dev/null +++ b/cw_wownero/lib/mnemonics/spanish.dart @@ -0,0 +1,1630 @@ +class SpanishMnemonics { + static const words = [ + "ábaco", + "abdomen", + "abeja", + "abierto", + "abogado", + "abono", + "aborto", + "abrazo", + "abrir", + "abuelo", + "abuso", + "acabar", + "academia", + "acceso", + "acción", + "aceite", + "acelga", + "acento", + "aceptar", + "ácido", + "aclarar", + "acné", + "acoger", + "acoso", + "activo", + "acto", + "actriz", + "actuar", + "acudir", + "acuerdo", + "acusar", + "adicto", + "admitir", + "adoptar", + "adorno", + "aduana", + "adulto", + "aéreo", + "afectar", + "afición", + "afinar", + "afirmar", + "ágil", + "agitar", + "agonía", + "agosto", + "agotar", + "agregar", + "agrio", + "agua", + "agudo", + "águila", + "aguja", + "ahogo", + "ahorro", + "aire", + "aislar", + "ajedrez", + "ajeno", + "ajuste", + "alacrán", + "alambre", + "alarma", + "alba", + "álbum", + "alcalde", + "aldea", + "alegre", + "alejar", + "alerta", + "aleta", + "alfiler", + "alga", + "algodón", + "aliado", + "aliento", + "alivio", + "alma", + "almeja", + "almíbar", + "altar", + "alteza", + "altivo", + "alto", + "altura", + "alumno", + "alzar", + "amable", + "amante", + "amapola", + "amargo", + "amasar", + "ámbar", + "ámbito", + "ameno", + "amigo", + "amistad", + "amor", + "amparo", + "amplio", + "ancho", + "anciano", + "ancla", + "andar", + "andén", + "anemia", + "ángulo", + "anillo", + "ánimo", + "anís", + "anotar", + "antena", + "antiguo", + "antojo", + "anual", + "anular", + "anuncio", + "añadir", + "añejo", + "año", + "apagar", + "aparato", + "apetito", + "apio", + "aplicar", + "apodo", + "aporte", + "apoyo", + "aprender", + "aprobar", + "apuesta", + "apuro", + "arado", + "araña", + "arar", + "árbitro", + "árbol", + "arbusto", + "archivo", + "arco", + "arder", + "ardilla", + "arduo", + "área", + "árido", + "aries", + "armonía", + "arnés", + "aroma", + "arpa", + "arpón", + "arreglo", + "arroz", + "arruga", + "arte", + "artista", + "asa", + "asado", + "asalto", + "ascenso", + "asegurar", + "aseo", + "asesor", + "asiento", + "asilo", + "asistir", + "asno", + "asombro", + "áspero", + "astilla", + "astro", + "astuto", + "asumir", + "asunto", + "atajo", + "ataque", + "atar", + "atento", + "ateo", + "ático", + "atleta", + "átomo", + "atraer", + "atroz", + "atún", + "audaz", + "audio", + "auge", + "aula", + "aumento", + "ausente", + "autor", + "aval", + "avance", + "avaro", + "ave", + "avellana", + "avena", + "avestruz", + "avión", + "aviso", + "ayer", + "ayuda", + "ayuno", + "azafrán", + "azar", + "azote", + "azúcar", + "azufre", + "azul", + "baba", + "babor", + "bache", + "bahía", + "baile", + "bajar", + "balanza", + "balcón", + "balde", + "bambú", + "banco", + "banda", + "baño", + "barba", + "barco", + "barniz", + "barro", + "báscula", + "bastón", + "basura", + "batalla", + "batería", + "batir", + "batuta", + "baúl", + "bazar", + "bebé", + "bebida", + "bello", + "besar", + "beso", + "bestia", + "bicho", + "bien", + "bingo", + "blanco", + "bloque", + "blusa", + "boa", + "bobina", + "bobo", + "boca", + "bocina", + "boda", + "bodega", + "boina", + "bola", + "bolero", + "bolsa", + "bomba", + "bondad", + "bonito", + "bono", + "bonsái", + "borde", + "borrar", + "bosque", + "bote", + "botín", + "bóveda", + "bozal", + "bravo", + "brazo", + "brecha", + "breve", + "brillo", + "brinco", + "brisa", + "broca", + "broma", + "bronce", + "brote", + "bruja", + "brusco", + "bruto", + "buceo", + "bucle", + "bueno", + "buey", + "bufanda", + "bufón", + "búho", + "buitre", + "bulto", + "burbuja", + "burla", + "burro", + "buscar", + "butaca", + "buzón", + "caballo", + "cabeza", + "cabina", + "cabra", + "cacao", + "cadáver", + "cadena", + "caer", + "café", + "caída", + "caimán", + "caja", + "cajón", + "cal", + "calamar", + "calcio", + "caldo", + "calidad", + "calle", + "calma", + "calor", + "calvo", + "cama", + "cambio", + "camello", + "camino", + "campo", + "cáncer", + "candil", + "canela", + "canguro", + "canica", + "canto", + "caña", + "cañón", + "caoba", + "caos", + "capaz", + "capitán", + "capote", + "captar", + "capucha", + "cara", + "carbón", + "cárcel", + "careta", + "carga", + "cariño", + "carne", + "carpeta", + "carro", + "carta", + "casa", + "casco", + "casero", + "caspa", + "castor", + "catorce", + "catre", + "caudal", + "causa", + "cazo", + "cebolla", + "ceder", + "cedro", + "celda", + "célebre", + "celoso", + "célula", + "cemento", + "ceniza", + "centro", + "cerca", + "cerdo", + "cereza", + "cero", + "cerrar", + "certeza", + "césped", + "cetro", + "chacal", + "chaleco", + "champú", + "chancla", + "chapa", + "charla", + "chico", + "chiste", + "chivo", + "choque", + "choza", + "chuleta", + "chupar", + "ciclón", + "ciego", + "cielo", + "cien", + "cierto", + "cifra", + "cigarro", + "cima", + "cinco", + "cine", + "cinta", + "ciprés", + "circo", + "ciruela", + "cisne", + "cita", + "ciudad", + "clamor", + "clan", + "claro", + "clase", + "clave", + "cliente", + "clima", + "clínica", + "cobre", + "cocción", + "cochino", + "cocina", + "coco", + "código", + "codo", + "cofre", + "coger", + "cohete", + "cojín", + "cojo", + "cola", + "colcha", + "colegio", + "colgar", + "colina", + "collar", + "colmo", + "columna", + "combate", + "comer", + "comida", + "cómodo", + "compra", + "conde", + "conejo", + "conga", + "conocer", + "consejo", + "contar", + "copa", + "copia", + "corazón", + "corbata", + "corcho", + "cordón", + "corona", + "correr", + "coser", + "cosmos", + "costa", + "cráneo", + "cráter", + "crear", + "crecer", + "creído", + "crema", + "cría", + "crimen", + "cripta", + "crisis", + "cromo", + "crónica", + "croqueta", + "crudo", + "cruz", + "cuadro", + "cuarto", + "cuatro", + "cubo", + "cubrir", + "cuchara", + "cuello", + "cuento", + "cuerda", + "cuesta", + "cueva", + "cuidar", + "culebra", + "culpa", + "culto", + "cumbre", + "cumplir", + "cuna", + "cuneta", + "cuota", + "cupón", + "cúpula", + "curar", + "curioso", + "curso", + "curva", + "cutis", + "dama", + "danza", + "dar", + "dardo", + "dátil", + "deber", + "débil", + "década", + "decir", + "dedo", + "defensa", + "definir", + "dejar", + "delfín", + "delgado", + "delito", + "demora", + "denso", + "dental", + "deporte", + "derecho", + "derrota", + "desayuno", + "deseo", + "desfile", + "desnudo", + "destino", + "desvío", + "detalle", + "detener", + "deuda", + "día", + "diablo", + "diadema", + "diamante", + "diana", + "diario", + "dibujo", + "dictar", + "diente", + "dieta", + "diez", + "difícil", + "digno", + "dilema", + "diluir", + "dinero", + "directo", + "dirigir", + "disco", + "diseño", + "disfraz", + "diva", + "divino", + "doble", + "doce", + "dolor", + "domingo", + "don", + "donar", + "dorado", + "dormir", + "dorso", + "dos", + "dosis", + "dragón", + "droga", + "ducha", + "duda", + "duelo", + "dueño", + "dulce", + "dúo", + "duque", + "durar", + "dureza", + "duro", + "ébano", + "ebrio", + "echar", + "eco", + "ecuador", + "edad", + "edición", + "edificio", + "editor", + "educar", + "efecto", + "eficaz", + "eje", + "ejemplo", + "elefante", + "elegir", + "elemento", + "elevar", + "elipse", + "élite", + "elixir", + "elogio", + "eludir", + "embudo", + "emitir", + "emoción", + "empate", + "empeño", + "empleo", + "empresa", + "enano", + "encargo", + "enchufe", + "encía", + "enemigo", + "enero", + "enfado", + "enfermo", + "engaño", + "enigma", + "enlace", + "enorme", + "enredo", + "ensayo", + "enseñar", + "entero", + "entrar", + "envase", + "envío", + "época", + "equipo", + "erizo", + "escala", + "escena", + "escolar", + "escribir", + "escudo", + "esencia", + "esfera", + "esfuerzo", + "espada", + "espejo", + "espía", + "esposa", + "espuma", + "esquí", + "estar", + "este", + "estilo", + "estufa", + "etapa", + "eterno", + "ética", + "etnia", + "evadir", + "evaluar", + "evento", + "evitar", + "exacto", + "examen", + "exceso", + "excusa", + "exento", + "exigir", + "exilio", + "existir", + "éxito", + "experto", + "explicar", + "exponer", + "extremo", + "fábrica", + "fábula", + "fachada", + "fácil", + "factor", + "faena", + "faja", + "falda", + "fallo", + "falso", + "faltar", + "fama", + "familia", + "famoso", + "faraón", + "farmacia", + "farol", + "farsa", + "fase", + "fatiga", + "fauna", + "favor", + "fax", + "febrero", + "fecha", + "feliz", + "feo", + "feria", + "feroz", + "fértil", + "fervor", + "festín", + "fiable", + "fianza", + "fiar", + "fibra", + "ficción", + "ficha", + "fideo", + "fiebre", + "fiel", + "fiera", + "fiesta", + "figura", + "fijar", + "fijo", + "fila", + "filete", + "filial", + "filtro", + "fin", + "finca", + "fingir", + "finito", + "firma", + "flaco", + "flauta", + "flecha", + "flor", + "flota", + "fluir", + "flujo", + "flúor", + "fobia", + "foca", + "fogata", + "fogón", + "folio", + "folleto", + "fondo", + "forma", + "forro", + "fortuna", + "forzar", + "fosa", + "foto", + "fracaso", + "frágil", + "franja", + "frase", + "fraude", + "freír", + "freno", + "fresa", + "frío", + "frito", + "fruta", + "fuego", + "fuente", + "fuerza", + "fuga", + "fumar", + "función", + "funda", + "furgón", + "furia", + "fusil", + "fútbol", + "futuro", + "gacela", + "gafas", + "gaita", + "gajo", + "gala", + "galería", + "gallo", + "gamba", + "ganar", + "gancho", + "ganga", + "ganso", + "garaje", + "garza", + "gasolina", + "gastar", + "gato", + "gavilán", + "gemelo", + "gemir", + "gen", + "género", + "genio", + "gente", + "geranio", + "gerente", + "germen", + "gesto", + "gigante", + "gimnasio", + "girar", + "giro", + "glaciar", + "globo", + "gloria", + "gol", + "golfo", + "goloso", + "golpe", + "goma", + "gordo", + "gorila", + "gorra", + "gota", + "goteo", + "gozar", + "grada", + "gráfico", + "grano", + "grasa", + "gratis", + "grave", + "grieta", + "grillo", + "gripe", + "gris", + "grito", + "grosor", + "grúa", + "grueso", + "grumo", + "grupo", + "guante", + "guapo", + "guardia", + "guerra", + "guía", + "guiño", + "guion", + "guiso", + "guitarra", + "gusano", + "gustar", + "haber", + "hábil", + "hablar", + "hacer", + "hacha", + "hada", + "hallar", + "hamaca", + "harina", + "haz", + "hazaña", + "hebilla", + "hebra", + "hecho", + "helado", + "helio", + "hembra", + "herir", + "hermano", + "héroe", + "hervir", + "hielo", + "hierro", + "hígado", + "higiene", + "hijo", + "himno", + "historia", + "hocico", + "hogar", + "hoguera", + "hoja", + "hombre", + "hongo", + "honor", + "honra", + "hora", + "hormiga", + "horno", + "hostil", + "hoyo", + "hueco", + "huelga", + "huerta", + "hueso", + "huevo", + "huida", + "huir", + "humano", + "húmedo", + "humilde", + "humo", + "hundir", + "huracán", + "hurto", + "icono", + "ideal", + "idioma", + "ídolo", + "iglesia", + "iglú", + "igual", + "ilegal", + "ilusión", + "imagen", + "imán", + "imitar", + "impar", + "imperio", + "imponer", + "impulso", + "incapaz", + "índice", + "inerte", + "infiel", + "informe", + "ingenio", + "inicio", + "inmenso", + "inmune", + "innato", + "insecto", + "instante", + "interés", + "íntimo", + "intuir", + "inútil", + "invierno", + "ira", + "iris", + "ironía", + "isla", + "islote", + "jabalí", + "jabón", + "jamón", + "jarabe", + "jardín", + "jarra", + "jaula", + "jazmín", + "jefe", + "jeringa", + "jinete", + "jornada", + "joroba", + "joven", + "joya", + "juerga", + "jueves", + "juez", + "jugador", + "jugo", + "juguete", + "juicio", + "junco", + "jungla", + "junio", + "juntar", + "júpiter", + "jurar", + "justo", + "juvenil", + "juzgar", + "kilo", + "koala", + "labio", + "lacio", + "lacra", + "lado", + "ladrón", + "lagarto", + "lágrima", + "laguna", + "laico", + "lamer", + "lámina", + "lámpara", + "lana", + "lancha", + "langosta", + "lanza", + "lápiz", + "largo", + "larva", + "lástima", + "lata", + "látex", + "latir", + "laurel", + "lavar", + "lazo", + "leal", + "lección", + "leche", + "lector", + "leer", + "legión", + "legumbre", + "lejano", + "lengua", + "lento", + "leña", + "león", + "leopardo", + "lesión", + "letal", + "letra", + "leve", + "leyenda", + "libertad", + "libro", + "licor", + "líder", + "lidiar", + "lienzo", + "liga", + "ligero", + "lima", + "límite", + "limón", + "limpio", + "lince", + "lindo", + "línea", + "lingote", + "lino", + "linterna", + "líquido", + "liso", + "lista", + "litera", + "litio", + "litro", + "llaga", + "llama", + "llanto", + "llave", + "llegar", + "llenar", + "llevar", + "llorar", + "llover", + "lluvia", + "lobo", + "loción", + "loco", + "locura", + "lógica", + "logro", + "lombriz", + "lomo", + "lonja", + "lote", + "lucha", + "lucir", + "lugar", + "lujo", + "luna", + "lunes", + "lupa", + "lustro", + "luto", + "luz", + "maceta", + "macho", + "madera", + "madre", + "maduro", + "maestro", + "mafia", + "magia", + "mago", + "maíz", + "maldad", + "maleta", + "malla", + "malo", + "mamá", + "mambo", + "mamut", + "manco", + "mando", + "manejar", + "manga", + "maniquí", + "manjar", + "mano", + "manso", + "manta", + "mañana", + "mapa", + "máquina", + "mar", + "marco", + "marea", + "marfil", + "margen", + "marido", + "mármol", + "marrón", + "martes", + "marzo", + "masa", + "máscara", + "masivo", + "matar", + "materia", + "matiz", + "matriz", + "máximo", + "mayor", + "mazorca", + "mecha", + "medalla", + "medio", + "médula", + "mejilla", + "mejor", + "melena", + "melón", + "memoria", + "menor", + "mensaje", + "mente", + "menú", + "mercado", + "merengue", + "mérito", + "mes", + "mesón", + "meta", + "meter", + "método", + "metro", + "mezcla", + "miedo", + "miel", + "miembro", + "miga", + "mil", + "milagro", + "militar", + "millón", + "mimo", + "mina", + "minero", + "mínimo", + "minuto", + "miope", + "mirar", + "misa", + "miseria", + "misil", + "mismo", + "mitad", + "mito", + "mochila", + "moción", + "moda", + "modelo", + "moho", + "mojar", + "molde", + "moler", + "molino", + "momento", + "momia", + "monarca", + "moneda", + "monja", + "monto", + "moño", + "morada", + "morder", + "moreno", + "morir", + "morro", + "morsa", + "mortal", + "mosca", + "mostrar", + "motivo", + "mover", + "móvil", + "mozo", + "mucho", + "mudar", + "mueble", + "muela", + "muerte", + "muestra", + "mugre", + "mujer", + "mula", + "muleta", + "multa", + "mundo", + "muñeca", + "mural", + "muro", + "músculo", + "museo", + "musgo", + "música", + "muslo", + "nácar", + "nación", + "nadar", + "naipe", + "naranja", + "nariz", + "narrar", + "nasal", + "natal", + "nativo", + "natural", + "náusea", + "naval", + "nave", + "navidad", + "necio", + "néctar", + "negar", + "negocio", + "negro", + "neón", + "nervio", + "neto", + "neutro", + "nevar", + "nevera", + "nicho", + "nido", + "niebla", + "nieto", + "niñez", + "niño", + "nítido", + "nivel", + "nobleza", + "noche", + "nómina", + "noria", + "norma", + "norte", + "nota", + "noticia", + "novato", + "novela", + "novio", + "nube", + "nuca", + "núcleo", + "nudillo", + "nudo", + "nuera", + "nueve", + "nuez", + "nulo", + "número", + "nutria", + "oasis", + "obeso", + "obispo", + "objeto", + "obra", + "obrero", + "observar", + "obtener", + "obvio", + "oca", + "ocaso", + "océano", + "ochenta", + "ocho", + "ocio", + "ocre", + "octavo", + "octubre", + "oculto", + "ocupar", + "ocurrir", + "odiar", + "odio", + "odisea", + "oeste", + "ofensa", + "oferta", + "oficio", + "ofrecer", + "ogro", + "oído", + "oír", + "ojo", + "ola", + "oleada", + "olfato", + "olivo", + "olla", + "olmo", + "olor", + "olvido", + "ombligo", + "onda", + "onza", + "opaco", + "opción", + "ópera", + "opinar", + "oponer", + "optar", + "óptica", + "opuesto", + "oración", + "orador", + "oral", + "órbita", + "orca", + "orden", + "oreja", + "órgano", + "orgía", + "orgullo", + "oriente", + "origen", + "orilla", + "oro", + "orquesta", + "oruga", + "osadía", + "oscuro", + "osezno", + "oso", + "ostra", + "otoño", + "otro", + "oveja", + "óvulo", + "óxido", + "oxígeno", + "oyente", + "ozono", + "pacto", + "padre", + "paella", + "página", + "pago", + "país", + "pájaro", + "palabra", + "palco", + "paleta", + "pálido", + "palma", + "paloma", + "palpar", + "pan", + "panal", + "pánico", + "pantera", + "pañuelo", + "papá", + "papel", + "papilla", + "paquete", + "parar", + "parcela", + "pared", + "parir", + "paro", + "párpado", + "parque", + "párrafo", + "parte", + "pasar", + "paseo", + "pasión", + "paso", + "pasta", + "pata", + "patio", + "patria", + "pausa", + "pauta", + "pavo", + "payaso", + "peatón", + "pecado", + "pecera", + "pecho", + "pedal", + "pedir", + "pegar", + "peine", + "pelar", + "peldaño", + "pelea", + "peligro", + "pellejo", + "pelo", + "peluca", + "pena", + "pensar", + "peñón", + "peón", + "peor", + "pepino", + "pequeño", + "pera", + "percha", + "perder", + "pereza", + "perfil", + "perico", + "perla", + "permiso", + "perro", + "persona", + "pesa", + "pesca", + "pésimo", + "pestaña", + "pétalo", + "petróleo", + "pez", + "pezuña", + "picar", + "pichón", + "pie", + "piedra", + "pierna", + "pieza", + "pijama", + "pilar", + "piloto", + "pimienta", + "pino", + "pintor", + "pinza", + "piña", + "piojo", + "pipa", + "pirata", + "pisar", + "piscina", + "piso", + "pista", + "pitón", + "pizca", + "placa", + "plan", + "plata", + "playa", + "plaza", + "pleito", + "pleno", + "plomo", + "pluma", + "plural", + "pobre", + "poco", + "poder", + "podio", + "poema", + "poesía", + "poeta", + "polen", + "policía", + "pollo", + "polvo", + "pomada", + "pomelo", + "pomo", + "pompa", + "poner", + "porción", + "portal", + "posada", + "poseer", + "posible", + "poste", + "potencia", + "potro", + "pozo", + "prado", + "precoz", + "pregunta", + "premio", + "prensa", + "preso", + "previo", + "primo", + "príncipe", + "prisión", + "privar", + "proa", + "probar", + "proceso", + "producto", + "proeza", + "profesor", + "programa", + "prole", + "promesa", + "pronto", + "propio", + "próximo", + "prueba", + "público", + "puchero", + "pudor", + "pueblo", + "puerta", + "puesto", + "pulga", + "pulir", + "pulmón", + "pulpo", + "pulso", + "puma", + "punto", + "puñal", + "puño", + "pupa", + "pupila", + "puré", + "quedar", + "queja", + "quemar", + "querer", + "queso", + "quieto", + "química", + "quince", + "quitar", + "rábano", + "rabia", + "rabo", + "ración", + "radical", + "raíz", + "rama", + "rampa", + "rancho", + "rango", + "rapaz", + "rápido", + "rapto", + "rasgo", + "raspa", + "rato", + "rayo", + "raza", + "razón", + "reacción", + "realidad", + "rebaño", + "rebote", + "recaer", + "receta", + "rechazo", + "recoger", + "recreo", + "recto", + "recurso", + "red", + "redondo", + "reducir", + "reflejo", + "reforma", + "refrán", + "refugio", + "regalo", + "regir", + "regla", + "regreso", + "rehén", + "reino", + "reír", + "reja", + "relato", + "relevo", + "relieve", + "relleno", + "reloj", + "remar", + "remedio", + "remo", + "rencor", + "rendir", + "renta", + "reparto", + "repetir", + "reposo", + "reptil", + "res", + "rescate", + "resina", + "respeto", + "resto", + "resumen", + "retiro", + "retorno", + "retrato", + "reunir", + "revés", + "revista", + "rey", + "rezar", + "rico", + "riego", + "rienda", + "riesgo", + "rifa", + "rígido", + "rigor", + "rincón", + "riñón", + "río", + "riqueza", + "risa", + "ritmo", + "rito" + ]; +} \ No newline at end of file diff --git a/cw_wownero/lib/mywownero.dart b/cw_wownero/lib/mywownero.dart new file mode 100644 index 000000000..d50e48b64 --- /dev/null +++ b/cw_wownero/lib/mywownero.dart @@ -0,0 +1,1689 @@ +const prefixLength = 3; + +String swapEndianBytes(String original) { + if (original.length != 8) { + return ''; + } + + return original[6] + + original[7] + + original[4] + + original[5] + + original[2] + + original[3] + + original[0] + + original[1]; +} + +List tructWords(List wordSet) { + final start = 0; + final end = prefixLength; + + return wordSet.map((word) => word.substring(start, end)).toList(); +} + +String mnemonicDecode(String seed) { + final n = englistWordSet.length; + var out = ''; + var wlist = seed.split(' '); + wlist.removeLast(); + + for (var i = 0; i < wlist.length; i += 3) { + final w1 = + tructWords(englistWordSet).indexOf(wlist[i].substring(0, prefixLength)); + final w2 = tructWords(englistWordSet) + .indexOf(wlist[i + 1].substring(0, prefixLength)); + final w3 = tructWords(englistWordSet) + .indexOf(wlist[i + 2].substring(0, prefixLength)); + + if (w1 == -1 || w2 == -1 || w3 == -1) { + print("invalid word in mnemonic"); + return ''; + } + + final x = w1 + n * (((n - w1) + w2) % n) + n * n * (((n - w2) + w3) % n); + + if (x % n != w1) { + print("Something went wrong when decoding your private key, please try again"); + return ''; + } + + final _res = '0000000' + x.toRadixString(16); + final start = _res.length - 8; + final end = _res.length; + final res = _res.substring(start, end); + + out += swapEndianBytes(res); + } + + return out; +} + +final englistWordSet = [ + "abbey", + "abducts", + "ability", + "ablaze", + "abnormal", + "abort", + "abrasive", + "absorb", + "abyss", + "academy", + "aces", + "aching", + "acidic", + "acoustic", + "acquire", + "across", + "actress", + "acumen", + "adapt", + "addicted", + "adept", + "adhesive", + "adjust", + "adopt", + "adrenalin", + "adult", + "adventure", + "aerial", + "afar", + "affair", + "afield", + "afloat", + "afoot", + "afraid", + "after", + "against", + "agenda", + "aggravate", + "agile", + "aglow", + "agnostic", + "agony", + "agreed", + "ahead", + "aided", + "ailments", + "aimless", + "airport", + "aisle", + "ajar", + "akin", + "alarms", + "album", + "alchemy", + "alerts", + "algebra", + "alkaline", + "alley", + "almost", + "aloof", + "alpine", + "already", + "also", + "altitude", + "alumni", + "always", + "amaze", + "ambush", + "amended", + "amidst", + "ammo", + "amnesty", + "among", + "amply", + "amused", + "anchor", + "android", + "anecdote", + "angled", + "ankle", + "annoyed", + "answers", + "antics", + "anvil", + "anxiety", + "anybody", + "apart", + "apex", + "aphid", + "aplomb", + "apology", + "apply", + "apricot", + "aptitude", + "aquarium", + "arbitrary", + "archer", + "ardent", + "arena", + "argue", + "arises", + "army", + "around", + "arrow", + "arsenic", + "artistic", + "ascend", + "ashtray", + "aside", + "asked", + "asleep", + "aspire", + "assorted", + "asylum", + "athlete", + "atlas", + "atom", + "atrium", + "attire", + "auburn", + "auctions", + "audio", + "august", + "aunt", + "austere", + "autumn", + "avatar", + "avidly", + "avoid", + "awakened", + "awesome", + "awful", + "awkward", + "awning", + "awoken", + "axes", + "axis", + "axle", + "aztec", + "azure", + "baby", + "bacon", + "badge", + "baffles", + "bagpipe", + "bailed", + "bakery", + "balding", + "bamboo", + "banjo", + "baptism", + "basin", + "batch", + "bawled", + "bays", + "because", + "beer", + "befit", + "begun", + "behind", + "being", + "below", + "bemused", + "benches", + "berries", + "bested", + "betting", + "bevel", + "beware", + "beyond", + "bias", + "bicycle", + "bids", + "bifocals", + "biggest", + "bikini", + "bimonthly", + "binocular", + "biology", + "biplane", + "birth", + "biscuit", + "bite", + "biweekly", + "blender", + "blip", + "bluntly", + "boat", + "bobsled", + "bodies", + "bogeys", + "boil", + "boldly", + "bomb", + "border", + "boss", + "both", + "bounced", + "bovine", + "bowling", + "boxes", + "boyfriend", + "broken", + "brunt", + "bubble", + "buckets", + "budget", + "buffet", + "bugs", + "building", + "bulb", + "bumper", + "bunch", + "business", + "butter", + "buying", + "buzzer", + "bygones", + "byline", + "bypass", + "cabin", + "cactus", + "cadets", + "cafe", + "cage", + "cajun", + "cake", + "calamity", + "camp", + "candy", + "casket", + "catch", + "cause", + "cavernous", + "cease", + "cedar", + "ceiling", + "cell", + "cement", + "cent", + "certain", + "chlorine", + "chrome", + "cider", + "cigar", + "cinema", + "circle", + "cistern", + "citadel", + "civilian", + "claim", + "click", + "clue", + "coal", + "cobra", + "cocoa", + "code", + "coexist", + "coffee", + "cogs", + "cohesive", + "coils", + "colony", + "comb", + "cool", + "copy", + "corrode", + "costume", + "cottage", + "cousin", + "cowl", + "criminal", + "cube", + "cucumber", + "cuddled", + "cuffs", + "cuisine", + "cunning", + "cupcake", + "custom", + "cycling", + "cylinder", + "cynical", + "dabbing", + "dads", + "daft", + "dagger", + "daily", + "damp", + "dangerous", + "dapper", + "darted", + "dash", + "dating", + "dauntless", + "dawn", + "daytime", + "dazed", + "debut", + "decay", + "dedicated", + "deepest", + "deftly", + "degrees", + "dehydrate", + "deity", + "dejected", + "delayed", + "demonstrate", + "dented", + "deodorant", + "depth", + "desk", + "devoid", + "dewdrop", + "dexterity", + "dialect", + "dice", + "diet", + "different", + "digit", + "dilute", + "dime", + "dinner", + "diode", + "diplomat", + "directed", + "distance", + "ditch", + "divers", + "dizzy", + "doctor", + "dodge", + "does", + "dogs", + "doing", + "dolphin", + "domestic", + "donuts", + "doorway", + "dormant", + "dosage", + "dotted", + "double", + "dove", + "down", + "dozen", + "dreams", + "drinks", + "drowning", + "drunk", + "drying", + "dual", + "dubbed", + "duckling", + "dude", + "duets", + "duke", + "dullness", + "dummy", + "dunes", + "duplex", + "duration", + "dusted", + "duties", + "dwarf", + "dwelt", + "dwindling", + "dying", + "dynamite", + "dyslexic", + "each", + "eagle", + "earth", + "easy", + "eating", + "eavesdrop", + "eccentric", + "echo", + "eclipse", + "economics", + "ecstatic", + "eden", + "edgy", + "edited", + "educated", + "eels", + "efficient", + "eggs", + "egotistic", + "eight", + "either", + "eject", + "elapse", + "elbow", + "eldest", + "eleven", + "elite", + "elope", + "else", + "eluded", + "emails", + "ember", + "emerge", + "emit", + "emotion", + "empty", + "emulate", + "energy", + "enforce", + "enhanced", + "enigma", + "enjoy", + "enlist", + "enmity", + "enough", + "enraged", + "ensign", + "entrance", + "envy", + "epoxy", + "equip", + "erase", + "erected", + "erosion", + "error", + "eskimos", + "espionage", + "essential", + "estate", + "etched", + "eternal", + "ethics", + "etiquette", + "evaluate", + "evenings", + "evicted", + "evolved", + "examine", + "excess", + "exhale", + "exit", + "exotic", + "exquisite", + "extra", + "exult", + "fabrics", + "factual", + "fading", + "fainted", + "faked", + "fall", + "family", + "fancy", + "farming", + "fatal", + "faulty", + "fawns", + "faxed", + "fazed", + "feast", + "february", + "federal", + "feel", + "feline", + "females", + "fences", + "ferry", + "festival", + "fetches", + "fever", + "fewest", + "fiat", + "fibula", + "fictional", + "fidget", + "fierce", + "fifteen", + "fight", + "films", + "firm", + "fishing", + "fitting", + "five", + "fixate", + "fizzle", + "fleet", + "flippant", + "flying", + "foamy", + "focus", + "foes", + "foggy", + "foiled", + "folding", + "fonts", + "foolish", + "fossil", + "fountain", + "fowls", + "foxes", + "foyer", + "framed", + "friendly", + "frown", + "fruit", + "frying", + "fudge", + "fuel", + "fugitive", + "fully", + "fuming", + "fungal", + "furnished", + "fuselage", + "future", + "fuzzy", + "gables", + "gadget", + "gags", + "gained", + "galaxy", + "gambit", + "gang", + "gasp", + "gather", + "gauze", + "gave", + "gawk", + "gaze", + "gearbox", + "gecko", + "geek", + "gels", + "gemstone", + "general", + "geometry", + "germs", + "gesture", + "getting", + "geyser", + "ghetto", + "ghost", + "giant", + "giddy", + "gifts", + "gigantic", + "gills", + "gimmick", + "ginger", + "girth", + "giving", + "glass", + "gleeful", + "glide", + "gnaw", + "gnome", + "goat", + "goblet", + "godfather", + "goes", + "goggles", + "going", + "goldfish", + "gone", + "goodbye", + "gopher", + "gorilla", + "gossip", + "gotten", + "gourmet", + "governing", + "gown", + "greater", + "grunt", + "guarded", + "guest", + "guide", + "gulp", + "gumball", + "guru", + "gusts", + "gutter", + "guys", + "gymnast", + "gypsy", + "gyrate", + "habitat", + "hacksaw", + "haggled", + "hairy", + "hamburger", + "happens", + "hashing", + "hatchet", + "haunted", + "having", + "hawk", + "haystack", + "hazard", + "hectare", + "hedgehog", + "heels", + "hefty", + "height", + "hemlock", + "hence", + "heron", + "hesitate", + "hexagon", + "hickory", + "hiding", + "highway", + "hijack", + "hiker", + "hills", + "himself", + "hinder", + "hippo", + "hire", + "history", + "hitched", + "hive", + "hoax", + "hobby", + "hockey", + "hoisting", + "hold", + "honked", + "hookup", + "hope", + "hornet", + "hospital", + "hotel", + "hounded", + "hover", + "howls", + "hubcaps", + "huddle", + "huge", + "hull", + "humid", + "hunter", + "hurried", + "husband", + "huts", + "hybrid", + "hydrogen", + "hyper", + "iceberg", + "icing", + "icon", + "identity", + "idiom", + "idled", + "idols", + "igloo", + "ignore", + "iguana", + "illness", + "imagine", + "imbalance", + "imitate", + "impel", + "inactive", + "inbound", + "incur", + "industrial", + "inexact", + "inflamed", + "ingested", + "initiate", + "injury", + "inkling", + "inline", + "inmate", + "innocent", + "inorganic", + "input", + "inquest", + "inroads", + "insult", + "intended", + "inundate", + "invoke", + "inwardly", + "ionic", + "irate", + "iris", + "irony", + "irritate", + "island", + "isolated", + "issued", + "italics", + "itches", + "items", + "itinerary", + "itself", + "ivory", + "jabbed", + "jackets", + "jaded", + "jagged", + "jailed", + "jamming", + "january", + "jargon", + "jaunt", + "javelin", + "jaws", + "jazz", + "jeans", + "jeers", + "jellyfish", + "jeopardy", + "jerseys", + "jester", + "jetting", + "jewels", + "jigsaw", + "jingle", + "jittery", + "jive", + "jobs", + "jockey", + "jogger", + "joining", + "joking", + "jolted", + "jostle", + "journal", + "joyous", + "jubilee", + "judge", + "juggled", + "juicy", + "jukebox", + "july", + "jump", + "junk", + "jury", + "justice", + "juvenile", + "kangaroo", + "karate", + "keep", + "kennel", + "kept", + "kernels", + "kettle", + "keyboard", + "kickoff", + "kidneys", + "king", + "kiosk", + "kisses", + "kitchens", + "kiwi", + "knapsack", + "knee", + "knife", + "knowledge", + "knuckle", + "koala", + "laboratory", + "ladder", + "lagoon", + "lair", + "lakes", + "lamb", + "language", + "laptop", + "large", + "last", + "later", + "launching", + "lava", + "lawsuit", + "layout", + "lazy", + "lectures", + "ledge", + "leech", + "left", + "legion", + "leisure", + "lemon", + "lending", + "leopard", + "lesson", + "lettuce", + "lexicon", + "liar", + "library", + "licks", + "lids", + "lied", + "lifestyle", + "light", + "likewise", + "lilac", + "limits", + "linen", + "lion", + "lipstick", + "liquid", + "listen", + "lively", + "loaded", + "lobster", + "locker", + "lodge", + "lofty", + "logic", + "loincloth", + "long", + "looking", + "lopped", + "lordship", + "losing", + "lottery", + "loudly", + "love", + "lower", + "loyal", + "lucky", + "luggage", + "lukewarm", + "lullaby", + "lumber", + "lunar", + "lurk", + "lush", + "luxury", + "lymph", + "lynx", + "lyrics", + "macro", + "madness", + "magically", + "mailed", + "major", + "makeup", + "malady", + "mammal", + "maps", + "masterful", + "match", + "maul", + "maverick", + "maximum", + "mayor", + "maze", + "meant", + "mechanic", + "medicate", + "meeting", + "megabyte", + "melting", + "memoir", + "menu", + "merger", + "mesh", + "metro", + "mews", + "mice", + "midst", + "mighty", + "mime", + "mirror", + "misery", + "mittens", + "mixture", + "moat", + "mobile", + "mocked", + "mohawk", + "moisture", + "molten", + "moment", + "money", + "moon", + "mops", + "morsel", + "mostly", + "motherly", + "mouth", + "movement", + "mowing", + "much", + "muddy", + "muffin", + "mugged", + "mullet", + "mumble", + "mundane", + "muppet", + "mural", + "musical", + "muzzle", + "myriad", + "mystery", + "myth", + "nabbing", + "nagged", + "nail", + "names", + "nanny", + "napkin", + "narrate", + "nasty", + "natural", + "nautical", + "navy", + "nearby", + "necklace", + "needed", + "negative", + "neither", + "neon", + "nephew", + "nerves", + "nestle", + "network", + "neutral", + "never", + "newt", + "nexus", + "nibs", + "niche", + "niece", + "nifty", + "nightly", + "nimbly", + "nineteen", + "nirvana", + "nitrogen", + "nobody", + "nocturnal", + "nodes", + "noises", + "nomad", + "noodles", + "northern", + "nostril", + "noted", + "nouns", + "novelty", + "nowhere", + "nozzle", + "nuance", + "nucleus", + "nudged", + "nugget", + "nuisance", + "null", + "number", + "nuns", + "nurse", + "nutshell", + "nylon", + "oaks", + "oars", + "oasis", + "oatmeal", + "obedient", + "object", + "obliged", + "obnoxious", + "observant", + "obtains", + "obvious", + "occur", + "ocean", + "october", + "odds", + "odometer", + "offend", + "often", + "oilfield", + "ointment", + "okay", + "older", + "olive", + "olympics", + "omega", + "omission", + "omnibus", + "onboard", + "oncoming", + "oneself", + "ongoing", + "onion", + "online", + "onslaught", + "onto", + "onward", + "oozed", + "opacity", + "opened", + "opposite", + "optical", + "opus", + "orange", + "orbit", + "orchid", + "orders", + "organs", + "origin", + "ornament", + "orphans", + "oscar", + "ostrich", + "otherwise", + "otter", + "ouch", + "ought", + "ounce", + "ourselves", + "oust", + "outbreak", + "oval", + "oven", + "owed", + "owls", + "owner", + "oxidant", + "oxygen", + "oyster", + "ozone", + "pact", + "paddles", + "pager", + "pairing", + "palace", + "pamphlet", + "pancakes", + "paper", + "paradise", + "pastry", + "patio", + "pause", + "pavements", + "pawnshop", + "payment", + "peaches", + "pebbles", + "peculiar", + "pedantic", + "peeled", + "pegs", + "pelican", + "pencil", + "people", + "pepper", + "perfect", + "pests", + "petals", + "phase", + "pheasants", + "phone", + "phrases", + "physics", + "piano", + "picked", + "pierce", + "pigment", + "piloted", + "pimple", + "pinched", + "pioneer", + "pipeline", + "pirate", + "pistons", + "pitched", + "pivot", + "pixels", + "pizza", + "playful", + "pledge", + "pliers", + "plotting", + "plus", + "plywood", + "poaching", + "pockets", + "podcast", + "poetry", + "point", + "poker", + "polar", + "ponies", + "pool", + "popular", + "portents", + "possible", + "potato", + "pouch", + "poverty", + "powder", + "pram", + "present", + "pride", + "problems", + "pruned", + "prying", + "psychic", + "public", + "puck", + "puddle", + "puffin", + "pulp", + "pumpkins", + "punch", + "puppy", + "purged", + "push", + "putty", + "puzzled", + "pylons", + "pyramid", + "python", + "queen", + "quick", + "quote", + "rabbits", + "racetrack", + "radar", + "rafts", + "rage", + "railway", + "raking", + "rally", + "ramped", + "randomly", + "rapid", + "rarest", + "rash", + "rated", + "ravine", + "rays", + "razor", + "react", + "rebel", + "recipe", + "reduce", + "reef", + "refer", + "regular", + "reheat", + "reinvest", + "rejoices", + "rekindle", + "relic", + "remedy", + "renting", + "reorder", + "repent", + "request", + "reruns", + "rest", + "return", + "reunion", + "revamp", + "rewind", + "rhino", + "rhythm", + "ribbon", + "richly", + "ridges", + "rift", + "rigid", + "rims", + "ringing", + "riots", + "ripped", + "rising", + "ritual", + "river", + "roared", + "robot", + "rockets", + "rodent", + "rogue", + "roles", + "romance", + "roomy", + "roped", + "roster", + "rotate", + "rounded", + "rover", + "rowboat", + "royal", + "ruby", + "rudely", + "ruffled", + "rugged", + "ruined", + "ruling", + "rumble", + "runway", + "rural", + "rustled", + "ruthless", + "sabotage", + "sack", + "sadness", + "safety", + "saga", + "sailor", + "sake", + "salads", + "sample", + "sanity", + "sapling", + "sarcasm", + "sash", + "satin", + "saucepan", + "saved", + "sawmill", + "saxophone", + "sayings", + "scamper", + "scenic", + "school", + "science", + "scoop", + "scrub", + "scuba", + "seasons", + "second", + "sedan", + "seeded", + "segments", + "seismic", + "selfish", + "semifinal", + "sensible", + "september", + "sequence", + "serving", + "session", + "setup", + "seventh", + "sewage", + "shackles", + "shelter", + "shipped", + "shocking", + "shrugged", + "shuffled", + "shyness", + "siblings", + "sickness", + "sidekick", + "sieve", + "sifting", + "sighting", + "silk", + "simplest", + "sincerely", + "sipped", + "siren", + "situated", + "sixteen", + "sizes", + "skater", + "skew", + "skirting", + "skulls", + "skydive", + "slackens", + "sleepless", + "slid", + "slower", + "slug", + "smash", + "smelting", + "smidgen", + "smog", + "smuggled", + "snake", + "sneeze", + "sniff", + "snout", + "snug", + "soapy", + "sober", + "soccer", + "soda", + "software", + "soggy", + "soil", + "solved", + "somewhere", + "sonic", + "soothe", + "soprano", + "sorry", + "southern", + "sovereign", + "sowed", + "soya", + "space", + "speedy", + "sphere", + "spiders", + "splendid", + "spout", + "sprig", + "spud", + "spying", + "square", + "stacking", + "stellar", + "stick", + "stockpile", + "strained", + "stunning", + "stylishly", + "subtly", + "succeed", + "suddenly", + "suede", + "suffice", + "sugar", + "suitcase", + "sulking", + "summon", + "sunken", + "superior", + "surfer", + "sushi", + "suture", + "swagger", + "swept", + "swiftly", + "sword", + "swung", + "syllabus", + "symptoms", + "syndrome", + "syringe", + "system", + "taboo", + "tacit", + "tadpoles", + "tagged", + "tail", + "taken", + "talent", + "tamper", + "tanks", + "tapestry", + "tarnished", + "tasked", + "tattoo", + "taunts", + "tavern", + "tawny", + "taxi", + "teardrop", + "technical", + "tedious", + "teeming", + "tell", + "template", + "tender", + "tepid", + "tequila", + "terminal", + "testing", + "tether", + "textbook", + "thaw", + "theatrics", + "thirsty", + "thorn", + "threaten", + "thumbs", + "thwart", + "ticket", + "tidy", + "tiers", + "tiger", + "tilt", + "timber", + "tinted", + "tipsy", + "tirade", + "tissue", + "titans", + "toaster", + "tobacco", + "today", + "toenail", + "toffee", + "together", + "toilet", + "token", + "tolerant", + "tomorrow", + "tonic", + "toolbox", + "topic", + "torch", + "tossed", + "total", + "touchy", + "towel", + "toxic", + "toyed", + "trash", + "trendy", + "tribal", + "trolling", + "truth", + "trying", + "tsunami", + "tubes", + "tucks", + "tudor", + "tuesday", + "tufts", + "tugs", + "tuition", + "tulips", + "tumbling", + "tunnel", + "turnip", + "tusks", + "tutor", + "tuxedo", + "twang", + "tweezers", + "twice", + "twofold", + "tycoon", + "typist", + "tyrant", + "ugly", + "ulcers", + "ultimate", + "umbrella", + "umpire", + "unafraid", + "unbending", + "uncle", + "under", + "uneven", + "unfit", + "ungainly", + "unhappy", + "union", + "unjustly", + "unknown", + "unlikely", + "unmask", + "unnoticed", + "unopened", + "unplugs", + "unquoted", + "unrest", + "unsafe", + "until", + "unusual", + "unveil", + "unwind", + "unzip", + "upbeat", + "upcoming", + "update", + "upgrade", + "uphill", + "upkeep", + "upload", + "upon", + "upper", + "upright", + "upstairs", + "uptight", + "upwards", + "urban", + "urchins", + "urgent", + "usage", + "useful", + "usher", + "using", + "usual", + "utensils", + "utility", + "utmost", + "utopia", + "uttered", + "vacation", + "vague", + "vain", + "value", + "vampire", + "vane", + "vapidly", + "vary", + "vastness", + "vats", + "vaults", + "vector", + "veered", + "vegan", + "vehicle", + "vein", + "velvet", + "venomous", + "verification", + "vessel", + "veteran", + "vexed", + "vials", + "vibrate", + "victim", + "video", + "viewpoint", + "vigilant", + "viking", + "village", + "vinegar", + "violin", + "vipers", + "virtual", + "visited", + "vitals", + "vivid", + "vixen", + "vocal", + "vogue", + "voice", + "volcano", + "vortex", + "voted", + "voucher", + "vowels", + "voyage", + "vulture", + "wade", + "waffle", + "wagtail", + "waist", + "waking", + "wallets", + "wanted", + "warped", + "washing", + "water", + "waveform", + "waxing", + "wayside", + "weavers", + "website", + "wedge", + "weekday", + "weird", + "welders", + "went", + "wept", + "were", + "western", + "wetsuit", + "whale", + "when", + "whipped", + "whole", + "wickets", + "width", + "wield", + "wife", + "wiggle", + "wildly", + "winter", + "wipeout", + "wiring", + "wise", + "withdrawn", + "wives", + "wizard", + "wobbly", + "woes", + "woken", + "wolf", + "womanly", + "wonders", + "woozy", + "worry", + "wounded", + "woven", + "wrap", + "wrist", + "wrong", + "yacht", + "yahoo", + "yanks", + "yard", + "yawning", + "yearbook", + "yellow", + "yesterday", + "yeti", + "yields", + "yodel", + "yoga", + "younger", + "yoyo", + "zapped", + "zeal", + "zebra", + "zero", + "zesty", + "zigzags", + "zinger", + "zippers", + "zodiac", + "zombie", + "zones", + "zoom" +]; diff --git a/cw_wownero/lib/pending_wownero_transaction.dart b/cw_wownero/lib/pending_wownero_transaction.dart new file mode 100644 index 000000000..1fc1805eb --- /dev/null +++ b/cw_wownero/lib/pending_wownero_transaction.dart @@ -0,0 +1,53 @@ +import 'package:cw_wownero/api/structs/pending_transaction.dart'; +import 'package:cw_wownero/api/transaction_history.dart' + as wownero_transaction_history; +import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/amount_converter.dart'; + +import 'package:cw_core/pending_transaction.dart'; + +class DoubleSpendException implements Exception { + DoubleSpendException(); + + @override + String toString() => + 'This transaction cannot be committed. This can be due to many reasons including the wallet not being synced, there is not enough WOW in your available balance, or previous transactions are not yet fully processed.'; +} + +class PendingWowneroTransaction with PendingTransaction { + PendingWowneroTransaction(this.pendingTransactionDescription); + + final PendingTransactionDescription pendingTransactionDescription; + + @override + String get id => pendingTransactionDescription.hash; + + @override + String get hex => pendingTransactionDescription.hex; + + String get txKey => pendingTransactionDescription.txKey; + + @override + String get amountFormatted => + AmountConverter.amountIntToString(CryptoCurrency.wow, pendingTransactionDescription.amount); + + @override + String get feeFormatted => + AmountConverter.amountIntToString(CryptoCurrency.wow, pendingTransactionDescription.fee); + + @override + Future commit() async { + try { + wownero_transaction_history.commitTransactionFromPointerAddress( + address: pendingTransactionDescription.pointerAddress); + } catch (e) { + final message = e.toString(); + + if (message.contains('Reason: double spend')) { + throw DoubleSpendException(); + } + + rethrow; + } + } +} diff --git a/cw_wownero/lib/wownero_account_list.dart b/cw_wownero/lib/wownero_account_list.dart new file mode 100644 index 000000000..6d408ba8f --- /dev/null +++ b/cw_wownero/lib/wownero_account_list.dart @@ -0,0 +1,81 @@ +import 'package:cw_core/wownero_amount_format.dart'; +import 'package:mobx/mobx.dart'; +import 'package:cw_core/account.dart'; +import 'package:cw_wownero/api/account_list.dart' as account_list; +import 'package:monero/wownero.dart' as wownero; + +part 'wownero_account_list.g.dart'; + +class WowneroAccountList = WowneroAccountListBase with _$WowneroAccountList; + +abstract class WowneroAccountListBase with Store { + WowneroAccountListBase() + : accounts = ObservableList(), + _isRefreshing = false, + _isUpdating = false { + refresh(); + } + + @observable + ObservableList accounts; + bool _isRefreshing; + bool _isUpdating; + + void update() async { + if (_isUpdating) { + return; + } + + try { + _isUpdating = true; + refresh(); + final accounts = getAll(); + + if (accounts.isNotEmpty) { + this.accounts.clear(); + this.accounts.addAll(accounts); + } + + _isUpdating = false; + } catch (e) { + _isUpdating = false; + rethrow; + } + } + + List getAll() => account_list.getAllAccount().map((accountRow) { + final balance = wownero.SubaddressAccountRow_getUnlockedBalance(accountRow); + + return Account( + id: wownero.SubaddressAccountRow_getRowId(accountRow), + label: wownero.SubaddressAccountRow_getLabel(accountRow), + balance: wowneroAmountToString(amount: wownero.Wallet_amountFromString(balance)), + ); + }).toList(); + + Future addAccount({required String label}) async { + await account_list.addAccount(label: label); + update(); + } + + Future setLabelAccount({required int accountIndex, required String label}) async { + await account_list.setLabelForAccount(accountIndex: accountIndex, label: label); + update(); + } + + void refresh() { + if (_isRefreshing) { + return; + } + + try { + _isRefreshing = true; + account_list.refreshAccounts(); + _isRefreshing = false; + } catch (e) { + _isRefreshing = false; + print(e); + rethrow; + } + } +} diff --git a/cw_wownero/lib/wownero_subaddress_list.dart b/cw_wownero/lib/wownero_subaddress_list.dart new file mode 100644 index 000000000..61fd09ef9 --- /dev/null +++ b/cw_wownero/lib/wownero_subaddress_list.dart @@ -0,0 +1,162 @@ +import 'package:cw_core/subaddress.dart'; +import 'package:cw_wownero/api/coins_info.dart'; +import 'package:cw_wownero/api/subaddress_list.dart' as subaddress_list; +import 'package:flutter/services.dart'; +import 'package:mobx/mobx.dart'; + +part 'wownero_subaddress_list.g.dart'; + +class WowneroSubaddressList = WowneroSubaddressListBase with _$WowneroSubaddressList; + +abstract class WowneroSubaddressListBase with Store { + WowneroSubaddressListBase() + : _isRefreshing = false, + _isUpdating = false, + subaddresses = ObservableList(); + + final List _usedAddresses = []; + + @observable + ObservableList subaddresses; + + bool _isRefreshing; + bool _isUpdating; + + void update({required int accountIndex}) { + refreshCoins(accountIndex); + + if (_isUpdating) { + return; + } + + try { + _isUpdating = true; + refresh(accountIndex: accountIndex); + subaddresses.clear(); + subaddresses.addAll(getAll()); + _isUpdating = false; + } catch (e) { + _isUpdating = false; + rethrow; + } + } + + List getAll() { + var subaddresses = subaddress_list.getAllSubaddresses(); + + if (subaddresses.length > 2) { + final primary = subaddresses.first; + final rest = subaddresses.sublist(1).reversed; + subaddresses = [primary] + rest.toList(); + } + + return subaddresses.map((s) { + final address = s.address; + final label = s.label; + final id = s.addressIndex; + final hasDefaultAddressName = + label.toLowerCase() == 'Primary account'.toLowerCase() || + label.toLowerCase() == 'Untitled account'.toLowerCase(); + final isPrimaryAddress = id == 0 && hasDefaultAddressName; + return Subaddress( + id: id, + address: address, + label: isPrimaryAddress + ? 'Primary address' + : hasDefaultAddressName + ? '' + : label); + }).toList(); + } + + Future addSubaddress({required int accountIndex, required String label}) async { + await subaddress_list.addSubaddress(accountIndex: accountIndex, label: label); + update(accountIndex: accountIndex); + } + + Future setLabelSubaddress( + {required int accountIndex, required int addressIndex, required String label}) async { + await subaddress_list.setLabelForSubaddress( + accountIndex: accountIndex, addressIndex: addressIndex, label: label); + update(accountIndex: accountIndex); + } + + void refresh({required int accountIndex}) { + if (_isRefreshing) { + return; + } + + try { + _isRefreshing = true; + subaddress_list.refreshSubaddresses(accountIndex: accountIndex); + _isRefreshing = false; + } on PlatformException catch (e) { + _isRefreshing = false; + print(e); + rethrow; + } + } + + Future updateWithAutoGenerate({ + required int accountIndex, + required String defaultLabel, + required List usedAddresses, + }) async { + _usedAddresses.addAll(usedAddresses); + if (_isUpdating) { + return; + } + + try { + _isUpdating = true; + refresh(accountIndex: accountIndex); + subaddresses.clear(); + final newSubAddresses = + await _getAllUnusedAddresses(accountIndex: accountIndex, label: defaultLabel); + subaddresses.addAll(newSubAddresses); + } catch (e) { + rethrow; + } finally { + _isUpdating = false; + } + } + + Future> _getAllUnusedAddresses( + {required int accountIndex, required String label}) async { + final allAddresses = subaddress_list.getAllSubaddresses(); + final lastAddress = allAddresses.last.address; + if (allAddresses.isEmpty || _usedAddresses.contains(lastAddress)) { + final isAddressUnused = await _newSubaddress(accountIndex: accountIndex, label: label); + if (!isAddressUnused) { + return await _getAllUnusedAddresses(accountIndex: accountIndex, label: label); + } + } + + return allAddresses + .map((s) { + final id = s.addressIndex; + final address = s.address; + final label = s.label; + return Subaddress( + id: id, + address: address, + label: id == 0 && + label.toLowerCase() == 'Primary account'.toLowerCase() + ? 'Primary address' + : label); + }) + .toList(); + } + + Future _newSubaddress({required int accountIndex, required String label}) async { + await subaddress_list.addSubaddress(accountIndex: accountIndex, label: label); + + return subaddress_list + .getAllSubaddresses() + .where((s) { + final address = s.address; + return !_usedAddresses.contains(address); + }) + .isNotEmpty; + } +} diff --git a/cw_wownero/lib/wownero_transaction_creation_credentials.dart b/cw_wownero/lib/wownero_transaction_creation_credentials.dart new file mode 100644 index 000000000..85a622b6f --- /dev/null +++ b/cw_wownero/lib/wownero_transaction_creation_credentials.dart @@ -0,0 +1,9 @@ +import 'package:cw_core/monero_transaction_priority.dart'; +import 'package:cw_core/output_info.dart'; + +class WowneroTransactionCreationCredentials { + WowneroTransactionCreationCredentials({required this.outputs, required this.priority}); + + final List outputs; + final MoneroTransactionPriority priority; +} diff --git a/cw_wownero/lib/wownero_transaction_history.dart b/cw_wownero/lib/wownero_transaction_history.dart new file mode 100644 index 000000000..c4d2d1e81 --- /dev/null +++ b/cw_wownero/lib/wownero_transaction_history.dart @@ -0,0 +1,28 @@ +import 'dart:core'; +import 'package:mobx/mobx.dart'; +import 'package:cw_core/transaction_history.dart'; +import 'package:cw_wownero/wownero_transaction_info.dart'; + +part 'wownero_transaction_history.g.dart'; + +class WowneroTransactionHistory = WowneroTransactionHistoryBase + with _$WowneroTransactionHistory; + +abstract class WowneroTransactionHistoryBase + extends TransactionHistoryBase with Store { + WowneroTransactionHistoryBase() { + transactions = ObservableMap(); + } + + @override + Future save() async {} + + @override + void addOne(WowneroTransactionInfo transaction) => + transactions[transaction.id] = transaction; + + @override + void addMany(Map transactions) => + this.transactions.addAll(transactions); + +} diff --git a/cw_wownero/lib/wownero_transaction_info.dart b/cw_wownero/lib/wownero_transaction_info.dart new file mode 100644 index 000000000..2060f1f95 --- /dev/null +++ b/cw_wownero/lib/wownero_transaction_info.dart @@ -0,0 +1,80 @@ +import 'package:cw_core/transaction_info.dart'; +import 'package:cw_core/wownero_amount_format.dart'; +import 'package:cw_wownero/api/structs/transaction_info_row.dart'; +import 'package:cw_core/parseBoolFromString.dart'; +import 'package:cw_core/transaction_direction.dart'; +import 'package:cw_core/format_amount.dart'; +import 'package:cw_wownero/api/transaction_history.dart'; + +class WowneroTransactionInfo extends TransactionInfo { + WowneroTransactionInfo(this.id, this.height, this.direction, this.date, + this.isPending, this.amount, this.accountIndex, this.addressIndex, this.fee, + this.confirmations); + + WowneroTransactionInfo.fromMap(Map map) + : id = (map['hash'] ?? '') as String, + height = (map['height'] ?? 0) as int, + direction = map['direction'] != null + ? parseTransactionDirectionFromNumber(map['direction'] as String) + : TransactionDirection.incoming, + date = DateTime.fromMillisecondsSinceEpoch( + (int.tryParse(map['timestamp'] as String? ?? '') ?? 0) * 1000), + isPending = parseBoolFromString(map['isPending'] as String), + amount = map['amount'] as int, + accountIndex = int.parse(map['accountIndex'] as String), + addressIndex = map['addressIndex'] as int, + confirmations = map['confirmations'] as int, + key = getTxKey((map['hash'] ?? '') as String), + fee = map['fee'] as int? ?? 0 { + additionalInfo = { + 'key': key, + 'accountIndex': accountIndex, + 'addressIndex': addressIndex + }; + } + + WowneroTransactionInfo.fromRow(TransactionInfoRow row) + : id = row.getHash(), + height = row.blockHeight, + direction = parseTransactionDirectionFromInt(row.direction), + date = DateTime.fromMillisecondsSinceEpoch(row.getDatetime() * 1000), + isPending = row.isPending != 0, + amount = row.getAmount(), + accountIndex = row.subaddrAccount, + addressIndex = row.subaddrIndex, + confirmations = row.confirmations, + key = getTxKey(row.getHash()), + fee = row.fee { + additionalInfo = { + 'key': key, + 'accountIndex': accountIndex, + 'addressIndex': addressIndex + }; + } + + final String id; + final int height; + final TransactionDirection direction; + final DateTime date; + final int accountIndex; + final bool isPending; + final int amount; + final int fee; + final int addressIndex; + final int confirmations; + String? recipientAddress; + String? key; + String? _fiatAmount; + + @override + String amountFormatted() => '${formatAmount(wowneroAmountToString(amount: amount))} WOW'; + + @override + String fiatAmount() => _fiatAmount ?? ''; + + @override + void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount); + + @override + String feeFormatted() => '${formatAmount(wowneroAmountToString(amount: fee))} WOW'; +} diff --git a/cw_wownero/lib/wownero_unspent.dart b/cw_wownero/lib/wownero_unspent.dart new file mode 100644 index 000000000..a79106886 --- /dev/null +++ b/cw_wownero/lib/wownero_unspent.dart @@ -0,0 +1,20 @@ +import 'package:cw_core/unspent_transaction_output.dart'; +import 'package:cw_wownero/api/structs/coins_info_row.dart'; + +class WowneroUnspent extends Unspent { + WowneroUnspent( + String address, String hash, String keyImage, int value, bool isFrozen, this.isUnlocked) + : super(address, hash, value, 0, keyImage) { + this.isFrozen = isFrozen; + } + + factory WowneroUnspent.fromCoinsInfoRow(CoinsInfoRow coinsInfoRow) => WowneroUnspent( + coinsInfoRow.getAddress(), + coinsInfoRow.getHash(), + coinsInfoRow.getKeyImage(), + coinsInfoRow.amount, + coinsInfoRow.frozen == 1, + coinsInfoRow.unlocked == 1); + + final bool isUnlocked; +} diff --git a/cw_wownero/lib/wownero_wallet.dart b/cw_wownero/lib/wownero_wallet.dart new file mode 100644 index 000000000..52f84e26a --- /dev/null +++ b/cw_wownero/lib/wownero_wallet.dart @@ -0,0 +1,747 @@ +import 'dart:async'; +import 'dart:ffi'; +import 'dart:io'; +import 'dart:isolate'; + +import 'package:cw_core/account.dart'; +import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/monero_transaction_priority.dart'; +import 'package:cw_core/monero_wallet_keys.dart'; +import 'package:cw_core/monero_wallet_utils.dart'; +import 'package:cw_core/node.dart'; +import 'package:cw_core/pathForWallet.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/transaction_priority.dart'; +import 'package:cw_core/unspent_coins_info.dart'; +import 'package:cw_core/wallet_base.dart'; +import 'package:cw_core/wallet_info.dart'; +import 'package:cw_core/wownero_amount_format.dart'; +import 'package:cw_core/wownero_balance.dart'; +import 'package:cw_wownero/api/coins_info.dart'; +import 'package:cw_wownero/api/structs/pending_transaction.dart'; +import 'package:cw_wownero/api/transaction_history.dart' as transaction_history; +import 'package:cw_wownero/api/wallet.dart' as wownero_wallet; +import 'package:cw_wownero/api/wallet_manager.dart'; +import 'package:cw_wownero/api/wownero_output.dart'; +import 'package:cw_wownero/exceptions/wownero_transaction_creation_exception.dart'; +import 'package:cw_wownero/exceptions/wownero_transaction_no_inputs_exception.dart'; +import 'package:cw_wownero/pending_wownero_transaction.dart'; +import 'package:cw_wownero/wownero_transaction_creation_credentials.dart'; +import 'package:cw_wownero/wownero_transaction_history.dart'; +import 'package:cw_wownero/wownero_transaction_info.dart'; +import 'package:cw_wownero/wownero_unspent.dart'; +import 'package:cw_wownero/wownero_wallet_addresses.dart'; +import 'package:flutter/foundation.dart'; +import 'package:hive/hive.dart'; +import 'package:mobx/mobx.dart'; +import 'package:monero/wownero.dart' as wownero; + +part 'wownero_wallet.g.dart'; + +const wowneroBlockSize = 1000; +// not sure if this should just be 0 but setting it higher feels safer / should catch more cases: +const MIN_RESTORE_HEIGHT = 1000; + +class WowneroWallet = WowneroWalletBase with _$WowneroWallet; + +abstract class WowneroWalletBase + extends WalletBase + with Store { + WowneroWalletBase( + {required WalletInfo walletInfo, required Box unspentCoinsInfo}) + : balance = ObservableMap.of({ + CryptoCurrency.wow: WowneroBalance( + fullBalance: wownero_wallet.getFullBalance(accountIndex: 0), + unlockedBalance: wownero_wallet.getFullBalance(accountIndex: 0)) + }), + _isTransactionUpdating = false, + _hasSyncAfterStartup = false, + isEnabledAutoGenerateSubaddress = false, + syncStatus = NotConnectedSyncStatus(), + unspentCoins = [], + this.unspentCoinsInfo = unspentCoinsInfo, + super(walletInfo) { + transactionHistory = WowneroTransactionHistory(); + walletAddresses = WowneroWalletAddresses(walletInfo, transactionHistory); + + _onAccountChangeReaction = reaction((_) => walletAddresses.account, (Account? account) { + if (account == null) return; + + balance = ObservableMap.of({ + currency: WowneroBalance( + fullBalance: wownero_wallet.getFullBalance(accountIndex: account.id), + unlockedBalance: wownero_wallet.getUnlockedBalance(accountIndex: account.id)) + }); + _updateSubAddress(isEnabledAutoGenerateSubaddress, account: account); + _askForUpdateTransactionHistory(); + }); + + reaction((_) => isEnabledAutoGenerateSubaddress, (bool enabled) { + _updateSubAddress(enabled, account: walletAddresses.account); + }); + } + + static const int _autoSaveInterval = 30; + + Box unspentCoinsInfo; + + void Function(FlutterErrorDetails)? onError; + + @override + late WowneroWalletAddresses walletAddresses; + + @override + @observable + bool isEnabledAutoGenerateSubaddress; + + @override + @observable + SyncStatus syncStatus; + + @override + @observable + ObservableMap balance; + + @override + String get seed => wownero_wallet.getSeed(); + + String seedLegacy(String? language) { + return wownero_wallet.getSeedLegacy(language); + } + + @override + MoneroWalletKeys get keys => MoneroWalletKeys( + privateSpendKey: wownero_wallet.getSecretSpendKey(), + privateViewKey: wownero_wallet.getSecretViewKey(), + publicSpendKey: wownero_wallet.getPublicSpendKey(), + publicViewKey: wownero_wallet.getPublicViewKey()); + + wownero_wallet.SyncListener? _listener; + ReactionDisposer? _onAccountChangeReaction; + bool _isTransactionUpdating; + bool _hasSyncAfterStartup; + Timer? _autoSaveTimer; + List unspentCoins; + + Future init() async { + await walletAddresses.init(); + balance = ObservableMap.of({ + currency: WowneroBalance( + fullBalance: wownero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id), + unlockedBalance: + wownero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id)) + }); + _setListeners(); + await updateTransactions(); + + if (walletInfo.isRecovery) { + wownero_wallet.setRecoveringFromSeed(isRecovery: walletInfo.isRecovery); + + if (wownero_wallet.getCurrentHeight() <= 1) { + wownero_wallet.setRefreshFromBlockHeight(height: walletInfo.restoreHeight); + } + } + + _autoSaveTimer = + Timer.periodic(Duration(seconds: _autoSaveInterval), (_) async => await save()); + } + + @override + Future? updateBalance() => null; + + @override + void close() async { + _listener?.stop(); + _onAccountChangeReaction?.reaction.dispose(); + _autoSaveTimer?.cancel(); + } + + @override + Future connectToNode({required Node node}) async { + try { + syncStatus = ConnectingSyncStatus(); + await wownero_wallet.setupNode( + address: node.uri.toString(), + login: node.login, + password: node.password, + useSSL: node.isSSL, + isLightWallet: false, + // FIXME: hardcoded value + socksProxyAddress: node.socksProxyAddress); + + wownero_wallet.setTrustedDaemon(node.trusted); + syncStatus = ConnectedSyncStatus(); + } catch (e) { + syncStatus = FailedSyncStatus(); + print(e); + } + } + + @override + Future startSync() async { + try { + _setInitialHeight(); + } catch (_) { + // our restore height wasn't correct, so lets see if using the backup works: + try { + await resetCache(name); + _setInitialHeight(); + } catch (e) { + // we still couldn't get a valid height from the backup?!: + // try to use the date instead: + try { + _setHeightFromDate(); + } catch (_) { + // we still couldn't get a valid sync height :/ + } + } + } + + try { + syncStatus = AttemptingSyncStatus(); + wownero_wallet.startRefresh(); + _setListeners(); + _listener?.start(); + } catch (e) { + syncStatus = FailedSyncStatus(); + print(e); + rethrow; + } + } + + @override + Future createTransaction(Object credentials) async { + final _credentials = credentials as WowneroTransactionCreationCredentials; + final inputs = []; + final outputs = _credentials.outputs; + final hasMultiDestination = outputs.length > 1; + final unlockedBalance = + wownero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id); + var allInputsAmount = 0; + + PendingTransactionDescription pendingTransactionDescription; + + if (!(syncStatus is SyncedSyncStatus)) { + throw WowneroTransactionCreationException('The wallet is not synced.'); + } + + if (unspentCoins.isEmpty) { + await updateUnspent(); + } + + for (final utx in unspentCoins) { + if (utx.isSending) { + allInputsAmount += utx.value; + inputs.add(utx.keyImage!); + } + } + final spendAllCoins = inputs.length == unspentCoins.length; + + if (hasMultiDestination) { + if (outputs.any((item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) { + throw WowneroTransactionCreationException( + 'You do not have enough WOW to send this amount.'); + } + + final int totalAmount = + outputs.fold(0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0)); + + final estimatedFee = calculateEstimatedFee(_credentials.priority, totalAmount); + if (unlockedBalance < totalAmount) { + throw WowneroTransactionCreationException( + 'You do not have enough WOW to send this amount.'); + } + + if (!spendAllCoins && (allInputsAmount < totalAmount + estimatedFee)) { + throw WowneroTransactionNoInputsException(inputs.length); + } + + final wowneroOutputs = outputs.map((output) { + final outputAddress = output.isParsedAddress ? output.extractedAddress : output.address; + + return WowneroOutput( + address: outputAddress!, amount: output.cryptoAmount!.replaceAll(',', '.')); + }).toList(); + + pendingTransactionDescription = await transaction_history.createTransactionMultDest( + outputs: wowneroOutputs, + priorityRaw: _credentials.priority.serialize(), + accountIndex: walletAddresses.account!.id, + preferredInputs: inputs); + } else { + final output = outputs.first; + final address = output.isParsedAddress ? output.extractedAddress : output.address; + final amount = output.sendAll ? null : output.cryptoAmount!.replaceAll(',', '.'); + final formattedAmount = output.sendAll ? null : output.formattedCryptoAmount; + + if ((formattedAmount != null && unlockedBalance < formattedAmount) || + (formattedAmount == null && unlockedBalance <= 0)) { + final formattedBalance = wowneroAmountToString(amount: unlockedBalance); + + throw WowneroTransactionCreationException( + 'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.'); + } + + final estimatedFee = calculateEstimatedFee(_credentials.priority, formattedAmount); + if (!spendAllCoins && + ((formattedAmount != null && allInputsAmount < (formattedAmount + estimatedFee)) || + formattedAmount == null)) { + throw WowneroTransactionNoInputsException(inputs.length); + } + + pendingTransactionDescription = await transaction_history.createTransaction( + address: address!, + amount: amount, + priorityRaw: _credentials.priority.serialize(), + accountIndex: walletAddresses.account!.id, + preferredInputs: inputs); + } + + return PendingWowneroTransaction(pendingTransactionDescription); + } + + @override + int calculateEstimatedFee(TransactionPriority priority, int? amount) { + // FIXME: hardcoded value; + + if (priority is MoneroTransactionPriority) { + switch (priority) { + case MoneroTransactionPriority.slow: + return 24590000; + case MoneroTransactionPriority.automatic: + return 123050000; + case MoneroTransactionPriority.medium: + return 245029999; + case MoneroTransactionPriority.fast: + return 614530000; + case MoneroTransactionPriority.fastest: + return 26021600000; + } + } + + return 0; + } + + @override + Future save() async { + await walletAddresses.updateUsedSubaddress(); + + if (isEnabledAutoGenerateSubaddress) { + walletAddresses.updateUnusedSubaddress( + accountIndex: walletAddresses.account?.id ?? 0, + defaultLabel: walletAddresses.account?.label ?? ''); + } + + await walletAddresses.updateAddressesInBox(); + await wownero_wallet.store(); + try { + await backupWalletFiles(name); + } catch (e) { + print("¯\\_(ツ)_/¯"); + print(e); + } + } + + @override + Future renameWalletFiles(String newWalletName) async { + final currentWalletDirPath = await pathForWalletDir(name: name, type: type); + if (openedWalletsByPath["$currentWalletDirPath/$name"] != null) { + // NOTE: this is realistically only required on windows. + print("closing wallet"); + final wmaddr = wmPtr.address; + final waddr = openedWalletsByPath["$currentWalletDirPath/$name"]!.address; + await Isolate.run(() { + wownero.WalletManager_closeWallet( + Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true); + }); + openedWalletsByPath.remove("$currentWalletDirPath/$name"); + print("wallet closed"); + } + try { + // -- rename the waller folder -- + final currentWalletDir = Directory(await pathForWalletDir(name: name, type: type)); + final newWalletDirPath = await pathForWalletDir(name: newWalletName, type: type); + await currentWalletDir.rename(newWalletDirPath); + + // -- use new waller folder to rename files with old names still -- + final renamedWalletPath = newWalletDirPath + '/$name'; + + final currentCacheFile = File(renamedWalletPath); + final currentKeysFile = File('$renamedWalletPath.keys'); + final currentAddressListFile = File('$renamedWalletPath.address.txt'); + + final newWalletPath = await pathForWallet(name: newWalletName, type: type); + + if (currentCacheFile.existsSync()) { + await currentCacheFile.rename(newWalletPath); + } + if (currentKeysFile.existsSync()) { + await currentKeysFile.rename('$newWalletPath.keys'); + } + if (currentAddressListFile.existsSync()) { + await currentAddressListFile.rename('$newWalletPath.address.txt'); + } + + await backupWalletFiles(newWalletName); + } catch (e) { + final currentWalletPath = await pathForWallet(name: name, type: type); + + final currentCacheFile = File(currentWalletPath); + final currentKeysFile = File('$currentWalletPath.keys'); + final currentAddressListFile = File('$currentWalletPath.address.txt'); + + final newWalletPath = await pathForWallet(name: newWalletName, type: type); + + // Copies current wallet files into new wallet name's dir and files + if (currentCacheFile.existsSync()) { + await currentCacheFile.copy(newWalletPath); + } + if (currentKeysFile.existsSync()) { + await currentKeysFile.copy('$newWalletPath.keys'); + } + if (currentAddressListFile.existsSync()) { + await currentAddressListFile.copy('$newWalletPath.address.txt'); + } + + // Delete old name's dir and files + await Directory(currentWalletDirPath).delete(recursive: true); + } + } + + @override + Future changePassword(String password) async => wownero_wallet.setPasswordSync(password); + + Future getNodeHeight() async => wownero_wallet.getNodeHeight(); + + Future isConnected() async => wownero_wallet.isConnected(); + + Future setAsRecovered() async { + walletInfo.isRecovery = false; + await walletInfo.save(); + } + + @override + Future rescan({required int height}) async { + walletInfo.restoreHeight = height; + walletInfo.isRecovery = true; + wownero_wallet.setRefreshFromBlockHeight(height: height); + wownero_wallet.rescanBlockchainAsync(); + await startSync(); + _askForUpdateBalance(); + walletAddresses.accountList.update(); + await _askForUpdateTransactionHistory(); + await save(); + await walletInfo.save(); + } + + Future updateUnspent() async { + try { + refreshCoins(walletAddresses.account!.id); + + unspentCoins.clear(); + + final coinCount = countOfCoins(); + for (var i = 0; i < coinCount; i++) { + final coin = getCoin(i); + final coinSpent = wownero.CoinsInfo_spent(coin); + if (coinSpent == false) { + final unspent = WowneroUnspent( + wownero.CoinsInfo_address(coin), + wownero.CoinsInfo_hash(coin), + wownero.CoinsInfo_keyImage(coin), + wownero.CoinsInfo_amount(coin), + wownero.CoinsInfo_frozen(coin), + wownero.CoinsInfo_unlocked(coin), + ); + if (unspent.hash.isNotEmpty) { + unspent.isChange = transaction_history.getTransaction(unspent.hash) == 1; + } + unspentCoins.add(unspent); + } + } + + if (unspentCoinsInfo.isEmpty) { + unspentCoins.forEach((coin) => _addCoinInfo(coin)); + return; + } + + if (unspentCoins.isNotEmpty) { + unspentCoins.forEach((coin) { + final coinInfoList = unspentCoinsInfo.values.where((element) => + element.walletId.contains(id) && + element.accountIndex == walletAddresses.account!.id && + element.keyImage!.contains(coin.keyImage!)); + + if (coinInfoList.isNotEmpty) { + final coinInfo = coinInfoList.first; + + coin.isFrozen = coinInfo.isFrozen; + coin.isSending = coinInfo.isSending; + coin.note = coinInfo.note; + } else { + _addCoinInfo(coin); + } + }); + } + + await _refreshUnspentCoinsInfo(); + _askForUpdateBalance(); + } catch (e, s) { + print(e.toString()); + onError?.call(FlutterErrorDetails( + exception: e, + stack: s, + library: this.runtimeType.toString(), + )); + } + } + + Future _addCoinInfo(WowneroUnspent coin) async { + final newInfo = UnspentCoinsInfo( + walletId: id, + hash: coin.hash, + isFrozen: coin.isFrozen, + isSending: coin.isSending, + noteRaw: coin.note, + address: coin.address, + value: coin.value, + vout: 0, + keyImage: coin.keyImage, + isChange: coin.isChange, + accountIndex: walletAddresses.account!.id); + + await unspentCoinsInfo.add(newInfo); + } + + Future _refreshUnspentCoinsInfo() async { + try { + final List keys = []; + final currentWalletUnspentCoins = unspentCoinsInfo.values.where((element) => + element.walletId.contains(id) && element.accountIndex == walletAddresses.account!.id); + + if (currentWalletUnspentCoins.isNotEmpty) { + currentWalletUnspentCoins.forEach((element) { + final existUnspentCoins = + unspentCoins.where((coin) => element.keyImage!.contains(coin.keyImage!)); + + if (existUnspentCoins.isEmpty) { + keys.add(element.key); + } + }); + } + + if (keys.isNotEmpty) { + await unspentCoinsInfo.deleteAll(keys); + } + } catch (e) { + print(e.toString()); + } + } + + String getTransactionAddress(int accountIndex, int addressIndex) => + wownero_wallet.getAddress(accountIndex: accountIndex, addressIndex: addressIndex); + + @override + Future> fetchTransactions() async { + transaction_history.refreshTransactions(); + return _getAllTransactionsOfAccount(walletAddresses.account?.id) + .fold>({}, + (Map acc, WowneroTransactionInfo tx) { + acc[tx.id] = tx; + return acc; + }); + } + + Future updateTransactions() async { + try { + if (_isTransactionUpdating) { + return; + } + + _isTransactionUpdating = true; + transactionHistory.clear(); + final transactions = await fetchTransactions(); + transactionHistory.addMany(transactions); + await transactionHistory.save(); + _isTransactionUpdating = false; + } catch (e) { + print(e); + _isTransactionUpdating = false; + } + } + + String getSubaddressLabel(int accountIndex, int addressIndex) => + wownero_wallet.getSubaddressLabel(accountIndex, addressIndex); + + List _getAllTransactionsOfAccount(int? accountIndex) => + transaction_history + .getAllTransactions() + .map( + (row) => WowneroTransactionInfo( + row.hash, + row.blockheight, + row.isSpend ? TransactionDirection.outgoing : TransactionDirection.incoming, + row.timeStamp, + row.isPending, + row.amount, + row.accountIndex, + 0, + row.fee, + row.confirmations, + )..additionalInfo = { + 'key': row.key, + 'accountIndex': row.accountIndex, + 'addressIndex': row.addressIndex + }, + ) + .where((element) => element.accountIndex == (accountIndex ?? 0)) + .toList(); + + void _setListeners() { + _listener?.stop(); + _listener = wownero_wallet.setListeners(_onNewBlock, _onNewTransaction); + } + + // check if the height is correct: + void _setInitialHeight() { + if (walletInfo.isRecovery) { + return; + } + + final height = wownero_wallet.getCurrentHeight(); + + if (height > MIN_RESTORE_HEIGHT) { + // the restore height is probably correct, so we do nothing: + return; + } + + throw Exception("height isn't > $MIN_RESTORE_HEIGHT!"); + } + + void _setHeightFromDate() { + if (walletInfo.isRecovery) { + return; + } + + int height = 0; + try { + height = _getHeightByDate(walletInfo.date); + } catch (_) {} + + wownero_wallet.setRecoveringFromSeed(isRecovery: true); + wownero_wallet.setRefreshFromBlockHeight(height: height); + } + + int _getHeightDistance(DateTime date) { + final distance = DateTime.now().millisecondsSinceEpoch - date.millisecondsSinceEpoch; + final daysTmp = (distance / 86400).round(); + final days = daysTmp < 1 ? 1 : daysTmp; + + return days * 1000; + } + + int _getHeightByDate(DateTime date) { + final nodeHeight = wownero_wallet.getNodeHeightSync(); + final heightDistance = _getHeightDistance(date); + + if (nodeHeight <= 0) { + // the node returned 0 (an error state) + throw Exception("nodeHeight is <= 0!"); + } + + return nodeHeight - heightDistance; + } + + void _askForUpdateBalance() { + final unlockedBalance = _getUnlockedBalance(); + final fullBalance = _getFullBalance(); + final frozenBalance = _getFrozenBalance(); + + if (balance[currency]!.fullBalance != fullBalance || + balance[currency]!.unlockedBalance != unlockedBalance || + balance[currency]!.frozenBalance != frozenBalance) { + balance[currency] = WowneroBalance( + fullBalance: fullBalance, unlockedBalance: unlockedBalance, frozenBalance: frozenBalance); + } + } + + Future _askForUpdateTransactionHistory() async => await updateTransactions(); + + int _getFullBalance() => wownero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id); + + int _getUnlockedBalance() => + wownero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id); + + int _getFrozenBalance() { + var frozenBalance = 0; + + for (var coin in unspentCoinsInfo.values.where((element) => + element.walletId == id && element.accountIndex == walletAddresses.account!.id)) { + if (coin.isFrozen) frozenBalance += coin.value; + } + + return frozenBalance; + } + + void _onNewBlock(int height, int blocksLeft, double ptc) async { + try { + if (walletInfo.isRecovery) { + await _askForUpdateTransactionHistory(); + _askForUpdateBalance(); + walletAddresses.accountList.update(); + } + + if (blocksLeft < 100) { + await _askForUpdateTransactionHistory(); + _askForUpdateBalance(); + walletAddresses.accountList.update(); + syncStatus = SyncedSyncStatus(); + + if (!_hasSyncAfterStartup) { + _hasSyncAfterStartup = true; + await save(); + } + + if (walletInfo.isRecovery) { + await setAsRecovered(); + } + } else { + syncStatus = SyncingSyncStatus(blocksLeft, ptc); + } + } catch (e) { + print(e.toString()); + } + } + + void _onNewTransaction() async { + try { + await _askForUpdateTransactionHistory(); + _askForUpdateBalance(); + await Future.delayed(Duration(seconds: 1)); + } catch (e) { + print(e.toString()); + } + } + + void _updateSubAddress(bool enableAutoGenerate, {Account? account}) { + if (enableAutoGenerate) { + walletAddresses.updateUnusedSubaddress( + accountIndex: account?.id ?? 0, + defaultLabel: account?.label ?? '', + ); + } else { + walletAddresses.updateSubaddressList(accountIndex: account?.id ?? 0); + } + } + + @override + void setExceptionHandler(void Function(FlutterErrorDetails) e) => onError = e; + + @override + Future signMessage(String message, {String? address}) async { + final useAddress = address ?? ""; + return wownero_wallet.signMessage(message, address: useAddress); + } +} diff --git a/cw_wownero/lib/wownero_wallet_addresses.dart b/cw_wownero/lib/wownero_wallet_addresses.dart new file mode 100644 index 000000000..dc4b42840 --- /dev/null +++ b/cw_wownero/lib/wownero_wallet_addresses.dart @@ -0,0 +1,119 @@ +import 'package:cw_core/account.dart'; +import 'package:cw_core/address_info.dart'; +import 'package:cw_core/subaddress.dart'; +import 'package:cw_core/wallet_addresses.dart'; +import 'package:cw_core/wallet_info.dart'; +import 'package:cw_wownero/api/wallet.dart'; +import 'package:cw_wownero/wownero_account_list.dart'; +import 'package:cw_wownero/wownero_subaddress_list.dart'; +import 'package:cw_wownero/wownero_transaction_history.dart'; +import 'package:mobx/mobx.dart'; + +part 'wownero_wallet_addresses.g.dart'; + +class WowneroWalletAddresses = WowneroWalletAddressesBase with _$WowneroWalletAddresses; + +abstract class WowneroWalletAddressesBase extends WalletAddresses with Store { + WowneroWalletAddressesBase( + WalletInfo walletInfo, WowneroTransactionHistory wowneroTransactionHistory) + : accountList = WowneroAccountList(), + _wowneroTransactionHistory = wowneroTransactionHistory, + subaddressList = WowneroSubaddressList(), + address = '', + super(walletInfo); + + final WowneroTransactionHistory _wowneroTransactionHistory; + @override + @observable + String address; + + @observable + Account? account; + + @observable + Subaddress? subaddress; + + WowneroSubaddressList subaddressList; + + WowneroAccountList accountList; + + @override + Future init() async { + accountList.update(); + account = accountList.accounts.first; + updateSubaddressList(accountIndex: account?.id ?? 0); + await updateAddressesInBox(); + } + + @override + Future updateAddressesInBox() async { + try { + final _subaddressList = WowneroSubaddressList(); + + addressesMap.clear(); + addressInfos.clear(); + + accountList.accounts.forEach((account) { + _subaddressList.update(accountIndex: account.id); + _subaddressList.subaddresses.forEach((subaddress) { + addressesMap[subaddress.address] = subaddress.label; + addressInfos[account.id] ??= []; + addressInfos[account.id]?.add(AddressInfo( + address: subaddress.address, label: subaddress.label, accountIndex: account.id)); + }); + }); + + await saveAddressesInBox(); + } catch (e) { + print(e.toString()); + } + } + + bool validate() { + accountList.update(); + final accountListLength = accountList.accounts.length; + + if (accountListLength <= 0) { + return false; + } + + subaddressList.update(accountIndex: accountList.accounts.first.id); + final subaddressListLength = subaddressList.subaddresses.length; + + if (subaddressListLength <= 0) { + return false; + } + + return true; + } + + void updateSubaddressList({required int accountIndex}) { + subaddressList.update(accountIndex: accountIndex); + subaddress = subaddressList.subaddresses.first; + address = subaddress!.address; + } + + Future updateUsedSubaddress() async { + final transactions = _wowneroTransactionHistory.transactions.values.toList(); + + transactions.forEach((element) { + final accountIndex = element.accountIndex; + final addressIndex = element.addressIndex; + usedAddresses.add(getAddress(accountIndex: accountIndex, addressIndex: addressIndex)); + }); + } + + Future updateUnusedSubaddress( + {required int accountIndex, required String defaultLabel}) async { + await subaddressList.updateWithAutoGenerate( + accountIndex: accountIndex, + defaultLabel: defaultLabel, + usedAddresses: usedAddresses.toList()); + subaddress = subaddressList.subaddresses.last; + address = subaddress!.address; + } + + @override + bool containsAddress(String address) => + addressInfos[account?.id ?? 0]?.any((it) => it.address == address) ?? false; +} diff --git a/cw_wownero/lib/wownero_wallet_service.dart b/cw_wownero/lib/wownero_wallet_service.dart new file mode 100644 index 000000000..13cab8f61 --- /dev/null +++ b/cw_wownero/lib/wownero_wallet_service.dart @@ -0,0 +1,354 @@ +import 'dart:ffi'; +import 'dart:io'; +import 'package:cw_core/monero_wallet_utils.dart'; +import 'package:cw_core/pathForWallet.dart'; +import 'package:cw_core/unspent_coins_info.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_service.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'package:cw_core/get_height_by_date.dart'; +import 'package:cw_wownero/api/exceptions/wallet_opening_exception.dart'; +import 'package:cw_wownero/api/wallet_manager.dart' as wownero_wallet_manager; +import 'package:cw_wownero/api/wallet_manager.dart'; +import 'package:cw_wownero/wownero_wallet.dart'; +import 'package:flutter/widgets.dart'; +import 'package:hive/hive.dart'; +import 'package:polyseed/polyseed.dart'; +import 'package:monero/wownero.dart' as wownero; + +class WowneroNewWalletCredentials extends WalletCredentials { + WowneroNewWalletCredentials( + {required String name, required this.language, required this.isPolyseed, String? password}) + : super(name: name, password: password); + + final String language; + final bool isPolyseed; +} + +class WowneroRestoreWalletFromSeedCredentials extends WalletCredentials { + WowneroRestoreWalletFromSeedCredentials( + {required String name, required this.mnemonic, int height = 0, String? password}) + : super(name: name, password: password, height: height); + + final String mnemonic; +} + +class WowneroWalletLoadingException implements Exception { + @override + String toString() => 'Failure to load the wallet.'; +} + +class WowneroRestoreWalletFromKeysCredentials extends WalletCredentials { + WowneroRestoreWalletFromKeysCredentials( + {required String name, + required String password, + required this.language, + required this.address, + required this.viewKey, + required this.spendKey, + int height = 0}) + : super(name: name, password: password, height: height); + + final String language; + final String address; + final String viewKey; + final String spendKey; +} + +class WowneroWalletService extends WalletService< + WowneroNewWalletCredentials, + WowneroRestoreWalletFromSeedCredentials, + WowneroRestoreWalletFromKeysCredentials, + WowneroNewWalletCredentials> { + WowneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource); + + final Box walletInfoSource; + final Box unspentCoinsInfoSource; + + static bool walletFilesExist(String path) => + !File(path).existsSync() && !File('$path.keys').existsSync(); + + @override + WalletType getType() => WalletType.wownero; + + @override + Future create(WowneroNewWalletCredentials credentials, {bool? isTestnet}) async { + try { + final path = await pathForWallet(name: credentials.name, type: getType()); + + if (credentials.isPolyseed) { + final polyseed = Polyseed.create(); + final lang = PolyseedLang.getByEnglishName(credentials.language); + + final heightOverride = + getWowneroHeightByDate(date: DateTime.now().subtract(Duration(days: 2))); + + return _restoreFromPolyseed( + path, credentials.password!, polyseed, credentials.walletInfo!, lang, + overrideHeight: heightOverride); + } + + await wownero_wallet_manager.createWallet( + path: path, password: credentials.password!, language: credentials.language); + final wallet = WowneroWallet( + walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource); + await wallet.init(); + + return wallet; + } catch (e) { + // TODO: Implement Exception for wallet list service. + print('WowneroWalletsManager Error: ${e.toString()}'); + rethrow; + } + } + + @override + Future isWalletExit(String name) async { + try { + final path = await pathForWallet(name: name, type: getType()); + return wownero_wallet_manager.isWalletExist(path: path); + } catch (e) { + // TODO: Implement Exception for wallet list service. + print('WowneroWalletsManager Error: $e'); + rethrow; + } + } + + @override + Future openWallet(String name, String password) async { + WowneroWallet? wallet; + try { + final path = await pathForWallet(name: name, type: getType()); + + if (walletFilesExist(path)) { + await repairOldAndroidWallet(name); + } + + await wownero_wallet_manager.openWalletAsync({'path': path, 'password': password}); + final walletInfo = walletInfoSource.values + .firstWhere((info) => info.id == WalletBase.idFor(name, getType())); + wallet = WowneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource); + final isValid = wallet.walletAddresses.validate(); + + if (!isValid) { + await restoreOrResetWalletFiles(name); + wallet.close(); + return openWallet(name, password); + } + + await wallet.init(); + + return wallet; + } catch (e, s) { + // TODO: Implement Exception for wallet list service. + + final bool isBadAlloc = e.toString().contains('bad_alloc') || + (e is WalletOpeningException && + (e.message == 'std::bad_alloc' || e.message.contains('bad_alloc'))); + + final bool doesNotCorrespond = e.toString().contains('does not correspond') || + (e is WalletOpeningException && e.message.contains('does not correspond')); + + final bool isMissingCacheFilesIOS = e.toString().contains('basic_string') || + (e is WalletOpeningException && e.message.contains('basic_string')); + + final bool isMissingCacheFilesAndroid = e.toString().contains('input_stream') || + e.toString().contains('input stream error') || + (e is WalletOpeningException && + (e.message.contains('input_stream') || e.message.contains('input stream error'))); + + final bool invalidSignature = e.toString().contains('invalid signature') || + (e is WalletOpeningException && e.message.contains('invalid signature')); + + if (!isBadAlloc && + !doesNotCorrespond && + !isMissingCacheFilesIOS && + !isMissingCacheFilesAndroid && + !invalidSignature && + wallet != null && + wallet.onError != null) { + wallet.onError!(FlutterErrorDetails(exception: e, stack: s)); + } + + await restoreOrResetWalletFiles(name); + return openWallet(name, password); + } + } + + @override + Future remove(String wallet) async { + final path = await pathForWalletDir(name: wallet, type: getType()); + if (openedWalletsByPath["$path/$wallet"] != null) { + // NOTE: this is realistically only required on windows. + print("closing wallet"); + final wmaddr = wmPtr.address; + final waddr = openedWalletsByPath["$path/$wallet"]!.address; + // await Isolate.run(() { + wownero.WalletManager_closeWallet( + Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), false); + // }); + openedWalletsByPath.remove("$path/$wallet"); + print("wallet closed"); + } + + final file = Directory(path); + final isExist = file.existsSync(); + + if (isExist) { + await file.delete(recursive: true); + } + + final walletInfo = walletInfoSource.values + .firstWhere((info) => info.id == WalletBase.idFor(wallet, getType())); + await walletInfoSource.delete(walletInfo.key); + } + + @override + Future rename(String currentName, String password, String newName) async { + final currentWalletInfo = walletInfoSource.values + .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType())); + final currentWallet = + WowneroWallet(walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource); + + await currentWallet.renameWalletFiles(newName); + + final newWalletInfo = currentWalletInfo; + newWalletInfo.id = WalletBase.idFor(newName, getType()); + newWalletInfo.name = newName; + + await walletInfoSource.put(currentWalletInfo.key, newWalletInfo); + } + + @override + Future restoreFromKeys(WowneroRestoreWalletFromKeysCredentials credentials, + {bool? isTestnet}) async { + try { + final path = await pathForWallet(name: credentials.name, type: getType()); + await wownero_wallet_manager.restoreFromKeys( + path: path, + password: credentials.password!, + language: credentials.language, + restoreHeight: credentials.height!, + address: credentials.address, + viewKey: credentials.viewKey, + spendKey: credentials.spendKey); + final wallet = WowneroWallet( + walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource); + await wallet.init(); + + return wallet; + } catch (e) { + // TODO: Implement Exception for wallet list service. + print('WowneroWalletsManager Error: $e'); + rethrow; + } + } + + @override + Future restoreFromHardwareWallet(WowneroNewWalletCredentials credentials) { + throw UnimplementedError( + "Restoring a Wownero wallet from a hardware wallet is not yet supported!"); + } + + @override + Future restoreFromSeed(WowneroRestoreWalletFromSeedCredentials credentials, + {bool? isTestnet}) async { + // Restore from Polyseed + if (Polyseed.isValidSeed(credentials.mnemonic)) { + return restoreFromPolyseed(credentials); + } + + try { + final path = await pathForWallet(name: credentials.name, type: getType()); + await wownero_wallet_manager.restoreFromSeed( + path: path, + password: credentials.password!, + seed: credentials.mnemonic, + restoreHeight: credentials.height!); + final wallet = WowneroWallet( + walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource); + await wallet.init(); + + return wallet; + } catch (e) { + // TODO: Implement Exception for wallet list service. + print('WowneroWalletsManager Error: $e'); + rethrow; + } + } + + Future restoreFromPolyseed( + WowneroRestoreWalletFromSeedCredentials credentials) async { + try { + final path = await pathForWallet(name: credentials.name, type: getType()); + final polyseedCoin = PolyseedCoin.POLYSEED_WOWNERO; + final lang = PolyseedLang.getByPhrase(credentials.mnemonic); + final polyseed = Polyseed.decode(credentials.mnemonic, lang, polyseedCoin); + + return _restoreFromPolyseed( + path, credentials.password!, polyseed, credentials.walletInfo!, lang); + } catch (e) { + // TODO: Implement Exception for wallet list service. + print('WowneroWalletsManager Error: $e'); + rethrow; + } + } + + Future _restoreFromPolyseed( + String path, String password, Polyseed polyseed, WalletInfo walletInfo, PolyseedLang lang, + {PolyseedCoin coin = PolyseedCoin.POLYSEED_WOWNERO, int? overrideHeight}) async { + final height = overrideHeight ?? + getWowneroHeightByDate(date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000)); + final spendKey = polyseed.generateKey(coin, 32).toHexString(); + final seed = polyseed.encode(lang, coin); + + walletInfo.isRecovery = true; + walletInfo.restoreHeight = height; + + await wownero_wallet_manager.restoreFromSpendKey( + path: path, + password: password, + seed: seed, + language: lang.nameEnglish, + restoreHeight: height, + spendKey: spendKey); + + final wallet = WowneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource); + await wallet.init(); + + return wallet; + } + + Future repairOldAndroidWallet(String name) async { + try { + if (!Platform.isAndroid) { + return; + } + + final oldAndroidWalletDirPath = await outdatedAndroidPathForWalletDir(name: name); + final dir = Directory(oldAndroidWalletDirPath); + + if (!dir.existsSync()) { + return; + } + + final newWalletDirPath = await pathForWalletDir(name: name, type: getType()); + + dir.listSync().forEach((f) { + final file = File(f.path); + final name = f.path.split('/').last; + final newPath = newWalletDirPath + '/$name'; + final newFile = File(newPath); + + if (!newFile.existsSync()) { + newFile.createSync(); + } + newFile.writeAsBytesSync(file.readAsBytesSync()); + }); + } catch (e) { + print(e.toString()); + } + } +} diff --git a/cw_monero/example/pubspec.lock b/cw_wownero/pubspec.lock similarity index 57% rename from cw_monero/example/pubspec.lock rename to cw_wownero/pubspec.lock index 7816a7fad..f5dc3de3f 100644 --- a/cw_monero/example/pubspec.lock +++ b/cw_wownero/pubspec.lock @@ -1,6 +1,22 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8" + url: "https://pub.dev" + source: hosted + version: "47.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80" + url: "https://pub.dev" + source: hosted + version: "4.7.0" args: dependency: transitive description: @@ -33,6 +49,70 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + build_resolvers: + dependency: "direct dev" + description: + name: build_resolvers + sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6" + url: "https://pub.dev" + source: hosted + version: "2.0.10" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" + url: "https://pub.dev" + source: hosted + version: "2.4.9" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292" + url: "https://pub.dev" + source: hosted + version: "7.2.7" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725" + url: "https://pub.dev" + source: hosted + version: "8.4.3" characters: dependency: transitive description: @@ -41,6 +121,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311" + url: "https://pub.dev" + source: hosted + version: "2.0.2" clock: dependency: transitive description: @@ -49,6 +137,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" + url: "https://pub.dev" + source: hosted + version: "4.4.0" collection: dependency: transitive description: @@ -73,30 +169,23 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" - cupertino_icons: + cw_core: dependency: "direct main" description: - name: cupertino_icons - sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be + path: "../cw_core" + relative: true + source: path + version: "0.0.1" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4" url: "https://pub.dev" source: hosted - version: "1.0.5" - cw_core: - dependency: transitive - description: - path: "../../cw_core" - relative: true - source: path - version: "0.0.1" - cw_monero: - dependency: "direct main" - description: - path: ".." - relative: true - source: path - version: "0.0.1" + version: "2.2.4" encrypt: - dependency: transitive + dependency: "direct main" description: name: encrypt sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb" @@ -112,13 +201,13 @@ packages: source: hosted version: "1.3.1" ffi: - dependency: transitive + dependency: "direct main" description: name: ffi - sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" file: dependency: transitive description: @@ -127,21 +216,21 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.4" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c - url: "https://pub.dev" - source: hosted - version: "2.0.1" flutter_mobx: - dependency: transitive + dependency: "direct main" description: name: flutter_mobx sha256: "0da4add0016387a7bf309a0d0c41d36c6b3ae25ed7a176409267f166509e723e" @@ -153,6 +242,30 @@ packages: description: flutter source: sdk version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + glob: + dependency: transitive + description: + name: glob + sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + graphs: + dependency: transitive + description: + name: graphs + sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2 + url: "https://pub.dev" + source: hosted + version: "2.2.0" hashlib: dependency: transitive description: @@ -169,14 +282,38 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.0" - http: + hive: dependency: transitive + description: + name: hive + sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + url: "https://pub.dev" + source: hosted + version: "2.2.3" + hive_generator: + dependency: "direct dev" + description: + name: hive_generator + sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938" + url: "https://pub.dev" + source: hosted + version: "1.1.3" + http: + dependency: "direct main" description: name: http sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted version: "1.1.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" http_parser: dependency: transitive description: @@ -186,13 +323,21 @@ packages: source: hosted version: "4.0.2" intl: - dependency: transitive + dependency: "direct main" description: name: intl sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted version: "0.18.1" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" js: dependency: transitive description: @@ -201,6 +346,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317 + url: "https://pub.dev" + source: hosted + version: "4.8.0" leak_tracker: dependency: transitive description: @@ -225,14 +378,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" - lints: + logging: dependency: transitive description: - name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + name: logging + sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "1.1.1" matcher: dependency: transitive description: @@ -257,14 +410,55 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.0" - mobx: + mime: dependency: transitive + description: + name: mime + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" + source: hosted + version: "1.0.4" + mobx: + dependency: "direct main" description: name: mobx sha256: f1862bd92c6a903fab67338f27e2f731117c3cb9ea37cee1a487f9e4e0de314a url: "https://pub.dev" source: hosted version: "2.1.3+1" + mobx_codegen: + dependency: "direct dev" + description: + name: mobx_codegen + sha256: "86122e410d8ea24dda0c69adb5c2a6ccadd5ce02ad46e144764e0d0184a06181" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + 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: "8827da25de792088eb33e572115a5eb0d61d61a3c01acbc8bcbe76ed78f1a1f2" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" path: dependency: transitive description: @@ -274,7 +468,7 @@ packages: source: hosted version: "1.9.0" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa @@ -346,13 +540,21 @@ packages: source: hosted version: "3.7.3" polyseed: - dependency: transitive + dependency: "direct main" description: name: polyseed - sha256: "9b48ec535b10863f78f6354ec983b4cc0c88ca69ff48fee469d0fd1954b01d4f" + sha256: a340962242d7917b0f3e6bd02c4acc3f90eae8ff766f1244f793ae7a6414dd68 url: "https://pub.dev" source: hosted - version: "0.0.2" + version: "0.0.4" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" process: dependency: transitive description: @@ -361,6 +563,38 @@ packages: url: "https://pub.dev" source: hosted version: "4.2.4" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + shelf: + dependency: transitive + description: + name: shelf + sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c + url: "https://pub.dev" + source: hosted + version: "1.4.0" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8 + url: "https://pub.dev" + source: hosted + version: "1.0.3" sky_engine: dependency: transitive description: flutter @@ -374,6 +608,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d" + url: "https://pub.dev" + source: hosted + version: "1.2.6" + source_helper: + dependency: transitive + description: + name: source_helper + sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f" + url: "https://pub.dev" + source: hosted + version: "1.3.3" source_span: dependency: transitive description: @@ -398,6 +648,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" string_scanner: dependency: transitive description: @@ -422,6 +680,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.1" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" + source: hosted + version: "1.0.1" typed_data: dependency: transitive description: @@ -446,14 +712,30 @@ packages: url: "https://pub.dev" source: hosted version: "13.0.0" + watcher: + dependency: "direct overridden" + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b + url: "https://pub.dev" + source: hosted + version: "2.3.0" win32: dependency: transitive description: name: win32 - sha256: a6f0236dbda0f63aa9a25ad1ff9a9d8a4eaaa5012da0dc59d21afdb1dc361ca4 + sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.3" xdg_directories: dependency: transitive description: @@ -462,6 +744,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.0+3" + yaml: + dependency: transitive + description: + name: yaml + sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + url: "https://pub.dev" + source: hosted + version: "3.1.1" sdks: dart: ">=3.2.0-0 <4.0.0" flutter: ">=3.7.0" diff --git a/cw_wownero/pubspec.yaml b/cw_wownero/pubspec.yaml new file mode 100644 index 000000000..5e2a11461 --- /dev/null +++ b/cw_wownero/pubspec.yaml @@ -0,0 +1,82 @@ +name: cw_wownero +description: A new flutter plugin project. +version: 0.0.1 +publish_to: none +author: Cake Wallet +homepage: https://cakewallet.com + +environment: + sdk: ">=2.19.0 <3.0.0" + flutter: ">=1.20.0" + +dependencies: + flutter: + sdk: flutter + ffi: ^2.0.1 + http: ^1.1.0 + path_provider: ^2.0.11 + mobx: ^2.0.7+4 + flutter_mobx: ^2.0.6+1 + intl: ^0.18.0 + encrypt: ^5.0.1 + polyseed: ^0.0.4 + cw_core: + path: ../cw_core + monero: + git: + url: https://github.com/mrcyjanek/monero.dart + ref: d46753eca865e9e56c2f0ef6fe485c42e11982c5 + mutex: ^3.1.0 + +dev_dependencies: + flutter_test: + sdk: flutter + build_runner: ^2.4.7 + build_resolvers: ^2.0.9 + mobx_codegen: ^2.0.7 + hive_generator: ^1.1.3 + +dependency_overrides: + watcher: ^1.1.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + # This section identifies this Flutter project as a plugin project. + # The androidPackage and pluginClass identifiers should not ordinarily + # be modified. They are used by the tooling to maintain consistency when + # adding or updating assets for this project. + + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # To add custom fonts to your plugin package, 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 in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/env.json b/env.json new file mode 100644 index 000000000..33a4bc52b --- /dev/null +++ b/env.json @@ -0,0 +1,6 @@ +{ + "CW_WIN_APP_NAME":"Cake Wallet", + "CW_WIN_APP_PACKAGE_NAME": "com.cakewallet.cake_wallet", + "CW_WIN_APP_VERSION": "1.0.0", + "CW_WIN_APP_BUILD_NUMBER": "1" +} diff --git a/how_to_add_new_wallet_type.md b/how_to_add_new_wallet_type.md index 95b82d802..917e87cf4 100644 --- a/how_to_add_new_wallet_type.md +++ b/how_to_add_new_wallet_type.md @@ -7,7 +7,7 @@ **Core Folder/Files Setup** - Idenitify your core component/package (major project component), which would power the integration e.g web3dart, solana, onchain etc - Add a new entry to `WalletType` class in `cw_core/wallet_type.dart`. -- Fill out the necessary information int he various functions in the files, concerning the wallet name, the native currency type, symbol etc. +- Fill out the necessary information in the various functions in the files, concerning the wallet name, the native currency type, symbol etc. - Go to `cw_core/lib/currency_for_wallet_type.dart`, in the `currencyForWalletType` function, add a case for `walletx`, returning the native cryptocurrency for `walletx`. - If the cryptocurrency for walletx is not available among the default cryptocurrencies, add a new cryptocurrency entry in `cw_core/lib/cryptocurrency.dart`. - Add the newly created cryptocurrency name to the list named `all` in this file. @@ -70,7 +70,7 @@ A `Proxy` class is used to communicate with the specific wallet package we have. outputContent += '\tWalletType.walletx,\n’; } -- Head over to `scripts/android/pubspec.sh` script, and modify the `CONFIG_ARGS` under `$CAKEWALLET`. Add `"—walletx”` to the end of the passed in params. +- Head over to `scripts/android/pubspec_gen.sh` script, and modify the `CONFIG_ARGS` under `$CAKEWALLET`. Add `"—walletx”` to the end of the passed in params. - Repeat this in `scripts/ios/app_config.sh` and `scripts/macos/app_config.sh` - Open a terminal and cd into `scripts/android/`. Run the following commands to run setup configuration scripts(proxy class, add walletx to list of wallet types and add cw_walletx to pubspec). @@ -214,7 +214,7 @@ Now you can run the codebase and successfully create a wallet for type walletX s **Restore Wallet** - Go to `lib/core/seed_validator.dart` - In the `getWordList` method, add a case to handle `WalletType.walletx` which would return the word list to be used to validate the passed in seeds. -- Next, go to `lib/restore_view_model.dart` +- Next, go to `lib/wallet_restore_view_model.dart` - Modify the `hasRestoreFromPrivateKey` to reflect if walletx supports restore from Key - Add a switch case to handle the various restore modes that walletX supports - Modify the `getCredential` method to handle the restore flows for `WalletType.walletx` diff --git a/howto-build-android.md b/howto-build-android.md index c3fe415ee..4ad88ea0d 100644 --- a/howto-build-android.md +++ b/howto-build-android.md @@ -8,7 +8,7 @@ The following are the system requirements to build CakeWallet for your Android d Ubuntu >= 20.04 Android SDK 29 or higher (better to have the latest one 33) Android NDK 17c -Flutter 3.10.x or earlier +Flutter 3.19.x or earlier ``` ## Building CakeWallet on Android @@ -55,7 +55,7 @@ You may download and install the latest version of Android Studio [here](https:/ ### 3. Installing Flutter -Need to install flutter with version `3.7.x`. For this please check section [Install Flutter manually](https://docs.flutter.dev/get-started/install/linux#install-flutter-manually). +Need to install flutter with version `3.19.x`. For this please check section [Install Flutter manually](https://docs.flutter.dev/get-started/install/linux#install-flutter-manually). ### 4. Verify Installations @@ -66,7 +66,7 @@ Verify that the Android toolchain, Flutter, and Android Studio have been correct The output of this command will appear like this, indicating successful installations. If there are problems with your installation, they **must** be corrected before proceeding. ``` Doctor summary (to see all details, run flutter doctor -v): -[✓] Flutter (Channel stable, 3.10.x, on Linux, locale en_US.UTF-8) +[✓] Flutter (Channel stable, 3.19.x, on Linux, locale en_US.UTF-8) [✓] Android toolchain - develop for Android devices (Android SDK version 29 or higher) [✓] Android Studio (version 4.0 or higher) ``` @@ -116,10 +116,6 @@ Build the Monero libraries and their dependencies: `$ ./build_all.sh` -Now the dependencies need to be copied into the CakeWallet project with this command: - -`$ ./copy_monero_deps.sh` - It is now time to change back to the base directory of the CakeWallet source code: `$ cd ../../` diff --git a/howto-build-ios.md b/howto-build-ios.md new file mode 100644 index 000000000..3bb345861 --- /dev/null +++ b/howto-build-ios.md @@ -0,0 +1,101 @@ +# Building CakeWallet for iOS + +## Requirements and Setup + +The following are the system requirements to build CakeWallet for your iOS device. + +``` +macOS >= 14.0 +Xcode 15.3 +Flutter 3.19.x +``` + +## Building CakeWallet on iOS + +These steps will help you configure and execute a build of CakeWallet from its source code. + +### 1. Installing Package Dependencies + +CakeWallet cannot be built without the following packages installed on your build system. + +For installing dependency tools you can use brew [Install brew](https://brew.sh). + +You may easily install them on your build system with the following command: + +`$ brew install cmake xz cocoapods` + +### 2. Installing Xcode + +You may download and install the latest version of [Xcode](https://developer.apple.com/xcode/) from macOS App Store. + +### 3. Installing Flutter + +Need to install flutter with version `3.19.x`. For this please check section [Install Flutter](https://docs.flutter.dev/get-started/install/macos/mobile-ios?tab=download). + +### 4. Verify Installations + +Verify that the Flutter and Xcode have been correctly installed on your system with the following command: + +`$ flutter doctor` + +The output of this command will appear like this, indicating successful installations. If there are problems with your installation, they **must** be corrected before proceeding. +``` +Doctor summary (to see all details, run flutter doctor -v): +[✓] Flutter (Channel stable, 3.19.x, on macOS 14.x.x) +[✓] Xcode - develop for iOS and macOS (Xcode 15.3) +``` + +### 5. Acquiring the CakeWallet source code + +Download the source code. + +`$ git clone https://github.com/cake-tech/cake_wallet.git --branch main` + +Proceed into the source code before proceeding with the next steps: + +`$ cd cake_wallet/scripts/ios/` + +### 6. Execute Build & Setup Commands for CakeWallet + +We need to generate project settings like app name, app icon, package name, etc. For this need to setup environment variables and configure project files. + +Please pick what app you want to build: cakewallet or monero.com. + +`$ source ./app_env.sh ` +(it should be like `$ source ./app_env.sh cakewallet` or `$ source ./app_env.sh monero.com`) + +Then run configuration script for setup app name, app icon and etc: + +`$ ./app_config.sh` + +Build the Monero libraries and their dependencies: + +`$ ./build_monero_all.sh` + +It is now time to change back to the base directory of the CakeWallet source code: + +`$ cd ../../` + +Install Flutter package dependencies with this command: + +`$ flutter pub get` + +Your CakeWallet binary will be built with cryptographic salts, which are used for secure encryption of your data. You may generate these secret salts with the following command: + +`$ flutter packages pub run tool/generate_new_secrets.dart` + +Then we need to generate localization files and mobx models. + +`$ ./configure_cake_wallet.sh ios` + +### 7. Build! + +`$ flutter build ios --release` + +Then you can open `ios/Runner.xcworkspace` with Xcode and you can to archive the application. + +Or if you want to run to connected device: + +`$ flutter run --release` + +Copyright (c) 2024 Cake Technologies LLC. diff --git a/howto-build-macos.md b/howto-build-macos.md new file mode 100644 index 000000000..24d3a9d85 --- /dev/null +++ b/howto-build-macos.md @@ -0,0 +1,112 @@ +# Building CakeWallet for macOS + +## Requirements and Setup + +The following are the system requirements to build CakeWallet for your macOS device. + +``` +macOS >= 14.0 +Xcode 15.3 +Flutter 3.19.x +``` + +## Building CakeWallet on macOS + +These steps will help you configure and execute a build of CakeWallet from its source code. + +### 1. Installing Package Dependencies + +CakeWallet cannot be built without the following packages installed on your build system. + +For installing dependency tools you can use brew [Install brew](https://brew.sh). + +You may easily install them on your build system with the following command: + +`$ brew install cmake xz automake autoconf libtool boost@1.76 zmq cocoapods` + +`$ brew link boost@1.76` + +### 2. Installing Xcode + +You may download and install the latest version of [Xcode](https://developer.apple.com/xcode/) from macOS App Store. + +### 3. Installing Flutter + +Need to install flutter with version `3.19.x`. For this please check section [Install Flutter](https://docs.flutter.dev/get-started/install/macos/desktop?tab=download). + +### 4. Verify Installations + +Verify that Flutter and Xcode have been correctly installed on your system with the following command: + +`$ flutter doctor` + +The output of this command will appear like this, indicating successful installations. If there are problems with your installation, they **must** be corrected before proceeding. +``` +Doctor summary (to see all details, run flutter doctor -v): +[✓] Flutter (Channel stable, 3.19.x, on macOS 14.x.x) +[✓] Xcode - develop for iOS and macOS (Xcode 15.3) +``` + +### 5. Acquiring the CakeWallet source code + +Download the source code. + +`$ git clone https://github.com/cake-tech/cake_wallet.git --branch main` + +Proceed into the source code before proceeding with the next steps: + +`$ cd cake_wallet/scripts/macos/` + +### 6. Execute Build & Setup Commands for CakeWallet + +We need to generate project settings like app name, app icon, package name, etc. For this need to setup environment variables and configure project files. + +Please pick what app you want to build: cakewallet or monero.com. + +`$ source ./app_env.sh ` +(it should be like `$ source ./app_env.sh cakewallet` or `$ source ./app_env.sh monero.com`) + +Then run configuration script for setup app name, app icon and etc: + +`$ ./app_config.sh` + +Build the Monero libraries and their dependencies: + +`$ ./build_monero_all.sh` + +If you be needed to build universal monero lib, then it will require additional steps. Steps for build universal monero lib on mac with Apple Silicon (arm64): + +- Need to install Rosetta: `$ softwareupdate --install-rosetta` +- Need to install [Brew](https://brew.sh/) with rosetta: `$ arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"` (or take another way to install brew, but be use that you have installed it into /usr/local as it's using for x86_64 macs) +- Install dependencies for build monero wallet lib for x86_64 with brew: `$ arch -x86_64 /usr/local/bin/brew install automake autoconf libtool openssl boost@1.76 zmq` and link installed boost@1.76 for x86_64 `$ arch -x86_64 /usr/local/bin/brew link boost@1.76` +- Run building script with additional argument: `$ ./build_monero_all.sh universal` + +If you will be needed to build monero wallet lib only for x86_64 on arm64 mac, then you need use steps above, but run build script with rosetta without arguments: `$ arch -x86_64 ./build_monero_all.sh`. + +It is now time to change back to the base directory of the CakeWallet source code: + +`$ cd ../../` + +Install Flutter package dependencies with this command: + +`$ flutter pub get` + +Your CakeWallet binary will be built with cryptographic salts, which are used for secure encryption of your data. You may generate these secret salts with the following command: + +`$ flutter packages pub run tool/generate_new_secrets.dart` + +Then we need to generate localization files and mobx models. + +`$ ./configure_cake_wallet.sh macos` + +### 7. Build! + +`$ flutter build macos --release` + +Then you can open `macos/Runner.xcworkspace` with Xcode and you can to archive the application. + +Or if you want to run to connected device: + +`$ flutter run --release` + +Copyright (c) 2024 Cake Technologies LLC. diff --git a/ios/MoneroWallet.framework/Info.plist b/ios/MoneroWallet.framework/Info.plist new file mode 100644 index 000000000..8858589f7 Binary files /dev/null and b/ios/MoneroWallet.framework/Info.plist differ diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 170db929c..50295b3bb 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -7,7 +7,7 @@ PODS: - connectivity_plus (0.0.1): - Flutter - ReachabilitySwift - - CryptoSwift (1.8.1) + - CryptoSwift (1.8.2) - cw_haven (0.0.1): - cw_haven/Boost (= 0.0.1) - cw_haven/Haven (= 0.0.1) @@ -27,29 +27,6 @@ PODS: - cw_haven/Sodium (0.0.1): - cw_shared_external - Flutter - - cw_monero (0.0.2): - - cw_monero/Boost (= 0.0.2) - - cw_monero/Monero (= 0.0.2) - - cw_monero/OpenSSL (= 0.0.2) - - cw_monero/Sodium (= 0.0.2) - - cw_monero/Unbound (= 0.0.2) - - cw_shared_external - - Flutter - - cw_monero/Boost (0.0.2): - - cw_shared_external - - Flutter - - cw_monero/Monero (0.0.2): - - cw_shared_external - - Flutter - - cw_monero/OpenSSL (0.0.2): - - cw_shared_external - - Flutter - - cw_monero/Sodium (0.0.2): - - cw_shared_external - - Flutter - - cw_monero/Unbound (0.0.2): - - cw_shared_external - - Flutter - cw_shared_external (0.0.1): - cw_shared_external/Boost (= 0.0.1) - cw_shared_external/OpenSSL (= 0.0.1) @@ -67,35 +44,35 @@ PODS: - Flutter - devicelocale (0.0.1): - Flutter - - DKImagePickerController/Core (4.3.4): + - DKImagePickerController/Core (4.3.9): - DKImagePickerController/ImageDataManager - DKImagePickerController/Resource - - DKImagePickerController/ImageDataManager (4.3.4) - - DKImagePickerController/PhotoGallery (4.3.4): + - DKImagePickerController/ImageDataManager (4.3.9) + - DKImagePickerController/PhotoGallery (4.3.9): - DKImagePickerController/Core - DKPhotoGallery - - DKImagePickerController/Resource (4.3.4) - - DKPhotoGallery (0.0.17): - - DKPhotoGallery/Core (= 0.0.17) - - DKPhotoGallery/Model (= 0.0.17) - - DKPhotoGallery/Preview (= 0.0.17) - - DKPhotoGallery/Resource (= 0.0.17) + - DKImagePickerController/Resource (4.3.9) + - DKPhotoGallery (0.0.19): + - DKPhotoGallery/Core (= 0.0.19) + - DKPhotoGallery/Model (= 0.0.19) + - DKPhotoGallery/Preview (= 0.0.19) + - DKPhotoGallery/Resource (= 0.0.19) - SDWebImage - SwiftyGif - - DKPhotoGallery/Core (0.0.17): + - DKPhotoGallery/Core (0.0.19): - DKPhotoGallery/Model - DKPhotoGallery/Preview - SDWebImage - SwiftyGif - - DKPhotoGallery/Model (0.0.17): + - DKPhotoGallery/Model (0.0.19): - SDWebImage - SwiftyGif - - DKPhotoGallery/Preview (0.0.17): + - DKPhotoGallery/Preview (0.0.19): - DKPhotoGallery/Model - DKPhotoGallery/Resource - SDWebImage - SwiftyGif - - DKPhotoGallery/Resource (0.0.17): + - DKPhotoGallery/Resource (0.0.19): - SDWebImage - SwiftyGif - file_picker (0.0.1): @@ -131,15 +108,15 @@ PODS: - FlutterMacOS - permission_handler_apple (9.1.1): - Flutter - - Protobuf (3.25.3) - - ReachabilitySwift (5.0.0) + - Protobuf (3.27.2) + - ReachabilitySwift (5.2.3) - reactive_ble_mobile (0.0.1): - Flutter - Protobuf (~> 3.5) - SwiftProtobuf (~> 1.0) - - SDWebImage (5.18.11): - - SDWebImage/Core (= 5.18.11) - - SDWebImage/Core (5.18.11) + - SDWebImage (5.19.4): + - SDWebImage/Core (= 5.19.4) + - SDWebImage/Core (5.19.4) - sensitive_clipboard (0.0.1): - Flutter - share_plus (0.0.1): @@ -149,9 +126,9 @@ PODS: - FlutterMacOS - sp_scanner (0.0.1): - Flutter - - SwiftProtobuf (1.25.2) - - SwiftyGif (5.4.4) - - Toast (4.1.0) + - SwiftProtobuf (1.26.0) + - SwiftyGif (5.4.5) + - Toast (4.1.1) - uni_links (0.0.1): - Flutter - UnstoppableDomainsResolution (4.0.0): @@ -169,7 +146,6 @@ DEPENDENCIES: - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - CryptoSwift - cw_haven (from `.symlinks/plugins/cw_haven/ios`) - - cw_monero (from `.symlinks/plugins/cw_monero/ios`) - cw_shared_external (from `.symlinks/plugins/cw_shared_external/ios`) - device_display_brightness (from `.symlinks/plugins/device_display_brightness/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) @@ -220,8 +196,6 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/connectivity_plus/ios" cw_haven: :path: ".symlinks/plugins/cw_haven/ios" - cw_monero: - :path: ".symlinks/plugins/cw_monero/ios" cw_shared_external: :path: ".symlinks/plugins/cw_shared_external/ios" device_display_brightness: @@ -277,15 +251,14 @@ SPEC CHECKSUMS: barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0 BigInt: f668a80089607f521586bbe29513d708491ef2f7 connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d - CryptoSwift: b9c701d6f5011df23794dbf7f2e480a77835d83d + CryptoSwift: c63a805d8bb5e5538e88af4e44bb537776af11ea cw_haven: b3e54e1fbe7b8e6fda57a93206bc38f8e89b898a - cw_monero: 4cf3b96f2da8e95e2ef7d6703dd4d2c509127b7d cw_shared_external: 2972d872b8917603478117c9957dfca611845a92 device_display_brightness: 1510e72c567a1f6ce6ffe393dcd9afd1426034f7 device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 devicelocale: b22617f40038496deffba44747101255cee005b0 - DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac - DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 + DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c + DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 file_picker: 15fd9539e4eb735dc54bae8c0534a7a9511a03de Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_inappwebview_ios: 97215cf7d4677db55df76782dbd2930c5e1c1ea0 @@ -300,17 +273,17 @@ SPEC CHECKSUMS: package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 - Protobuf: 8e9074797a13c484a79959fdb819ef4ae6da7dbe - ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 + Protobuf: fb2c13674723f76ff6eede14f78847a776455fa2 + ReachabilitySwift: 7f151ff156cea1481a8411701195ac6a984f4979 reactive_ble_mobile: 9ce6723d37ccf701dbffd202d487f23f5de03b4c - SDWebImage: a3ba0b8faac7228c3c8eadd1a55c9c9fe5e16457 + SDWebImage: 066c47b573f408f18caa467d71deace7c0f8280d sensitive_clipboard: d4866e5d176581536c27bb1618642ee83adca986 share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 sp_scanner: eaa617fa827396b967116b7f1f43549ca62e9a12 - SwiftProtobuf: 407a385e97fd206c4fbe880cc84123989167e0d1 - SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f - Toast: ec33c32b8688982cecc6348adeae667c1b9938da + SwiftProtobuf: 5e8349171e7c2f88f5b9e683cb3cb79d1dc780b3 + SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 + Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e uni_links: d97da20c7701486ba192624d99bffaaffcfc298a UnstoppableDomainsResolution: c3c67f4d0a5e2437cb00d4bd50c2e00d6e743841 url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 2196bc289..417c522a6 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -8,32 +8,70 @@ /* Begin PBXBuildFile section */ 0C44A71A2518EF8000B570ED /* decrypt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C44A7192518EF8000B570ED /* decrypt.swift */; }; + 0C50DFB92BF3CB56002B0EB3 /* MoneroWallet.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0C50DFB82BF3CB56002B0EB3 /* MoneroWallet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 0C9D68C9264854B60011B691 /* secRandom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C9D68C8264854B60011B691 /* secRandom.swift */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 2193F104374FA2746CE8945B /* ResourceHelper.swift in Resources */ = {isa = PBXBuildFile; fileRef = 78D25C60B94E9D9E48D52E5E /* ResourceHelper.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 495FEFF9B395392FED3425DE /* TaskProtocol.swift in Resources */ = {isa = PBXBuildFile; fileRef = 0F42D8065219E0653321EE2B /* TaskProtocol.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; 4DFD1BB54A3A50573E19A583 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C663361C56EBB242598F609 /* Pods_Runner.framework */; }; + 525A2200C6C2A43EDC5C8FC5 /* BreezSDKConnector.swift in Resources */ = {isa = PBXBuildFile; fileRef = 1FB06A93B13D606F06B3924D /* BreezSDKConnector.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; + 6909E1D79C9986ADF2DE41E9 /* LnurlPayInvoice.swift in Resources */ = {isa = PBXBuildFile; fileRef = DCEA540E3586164FB47AD13E /* LnurlPayInvoice.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; + 724FDA327BF191BC29DCAA2E /* Constants.swift in Resources */ = {isa = PBXBuildFile; fileRef = 0CCA7ADAD6FF9185EBBB2BCA /* Constants.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; + 73138617307FA4F838D21D62 /* ServiceLogger.swift in Resources */ = {isa = PBXBuildFile; fileRef = F42258C3697CFE3C8C8D1933 /* ServiceLogger.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 9F46EE5E2BC11178009318F5 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 9F46EE5D2BC11178009318F5 /* PrivacyInfo.xcprivacy */; }; + A1B4A70C9CFA13AB71662216 /* LnurlPay.swift in Resources */ = {isa = PBXBuildFile; fileRef = 7D3364C03978A8A74B6D586E /* LnurlPay.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; + A3D5E17CC53DF13FA740DEFA /* RedeemSwap.swift in Resources */ = {isa = PBXBuildFile; fileRef = 9D2F2C9F2555316C95EE7EA3 /* RedeemSwap.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; + B6C6E59403ACDE44724C12F4 /* ServiceConfig.swift in Resources */ = {isa = PBXBuildFile; fileRef = B3D5E78267F5F18D882FDC3B /* ServiceConfig.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; + CE291CFE2C15DB9A00B9F709 /* WowneroWallet.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE291CFD2C15DB9A00B9F709 /* WowneroWallet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + CFEFC24F82F78FE747DF1D22 /* LnurlPayInfo.swift in Resources */ = {isa = PBXBuildFile; fileRef = 58C22CBD8C22B9D6023D59F8 /* LnurlPayInfo.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; + D0D7A0D4E13F31C4E02E235B /* ReceivePayment.swift in Resources */ = {isa = PBXBuildFile; fileRef = 91C524F800843E0A3F17E004 /* ReceivePayment.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; + D3AD73A327249AFE8F016A51 /* BreezSDK.swift in Resources */ = {isa = PBXBuildFile; fileRef = ABD6FCBB0F4244B090459128 /* BreezSDK.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; + F5EE19868D6F10D814BF73AD /* SDKNotificationService.swift in Resources */ = {isa = PBXBuildFile; fileRef = 41102141140E57B1DC27FBA1 /* SDKNotificationService.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; /* End PBXBuildFile section */ +/* Begin PBXCopyFilesBuildPhase section */ + CE5E8A222BEE19C700608EA1 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + CE291CFE2C15DB9A00B9F709 /* WowneroWallet.framework in CopyFiles */, + 0C50DFB92BF3CB56002B0EB3 /* MoneroWallet.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 0C400E0F25B21ABB0025E469 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 0C44A7192518EF8000B570ED /* decrypt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = decrypt.swift; sourceTree = ""; }; + 0C50DFB82BF3CB56002B0EB3 /* MoneroWallet.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = MoneroWallet.framework; sourceTree = ""; }; 0C9986A3251A932F00D566FD /* CryptoSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CryptoSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0C9D68C8264854B60011B691 /* secRandom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = secRandom.swift; sourceTree = ""; }; + 0CCA7ADAD6FF9185EBBB2BCA /* Constants.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Constants.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/Constants.swift"; sourceTree = ""; }; + 0F42D8065219E0653321EE2B /* TaskProtocol.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TaskProtocol.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/TaskProtocol.swift"; sourceTree = ""; }; 11F9FC13F9EE2A705B213FA9 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 1F083F2041D1F553F2AF8B62 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 1FB06A93B13D606F06B3924D /* BreezSDKConnector.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BreezSDKConnector.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/BreezSDKConnector.swift"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 3C663361C56EBB242598F609 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 41102141140E57B1DC27FBA1 /* SDKNotificationService.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SDKNotificationService.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/SDKNotificationService.swift"; sourceTree = ""; }; + 58C22CBD8C22B9D6023D59F8 /* LnurlPayInfo.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LnurlPayInfo.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/Task/LnurlPayInfo.swift"; sourceTree = ""; }; 5AFFEBFC279AD49C00F906A4 /* wakeLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = wakeLock.swift; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 78D25C60B94E9D9E48D52E5E /* ResourceHelper.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ResourceHelper.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/ResourceHelper.swift"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7D3364C03978A8A74B6D586E /* LnurlPay.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LnurlPay.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/Task/LnurlPay.swift"; sourceTree = ""; }; + 91C524F800843E0A3F17E004 /* ReceivePayment.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ReceivePayment.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/Task/ReceivePayment.swift"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -41,8 +79,14 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9D2F2C9F2555316C95EE7EA3 /* RedeemSwap.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RedeemSwap.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/Task/RedeemSwap.swift"; sourceTree = ""; }; 9F46EE5D2BC11178009318F5 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; + ABD6FCBB0F4244B090459128 /* BreezSDK.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BreezSDK.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/BreezSDK.swift"; sourceTree = ""; }; AD0937B0140D5A4C24E73BEA /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + B3D5E78267F5F18D882FDC3B /* ServiceConfig.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ServiceConfig.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/ServiceConfig.swift"; sourceTree = ""; }; + CE291CFD2C15DB9A00B9F709 /* WowneroWallet.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = WowneroWallet.framework; sourceTree = ""; }; + DCEA540E3586164FB47AD13E /* LnurlPayInvoice.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LnurlPayInvoice.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/Task/LnurlPayInvoice.swift"; sourceTree = ""; }; + F42258C3697CFE3C8C8D1933 /* ServiceLogger.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ServiceLogger.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/ServiceLogger.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -66,6 +110,14 @@ name = Frameworks; sourceTree = ""; }; + 0B80439B9064C9708DDB0ADA /* breez_sdk-OnDemandResources */ = { + isa = PBXGroup; + children = ( + ADEC151FA90C8F1EBCDA8CA3 /* BreezSDK */, + ); + name = "breez_sdk-OnDemandResources"; + sourceTree = ""; + }; 0C44A7182518EF4A00B570ED /* CakeWallet */ = { isa = PBXGroup; children = ( @@ -82,6 +134,7 @@ 11F9FC13F9EE2A705B213FA9 /* Pods-Runner.debug.xcconfig */, 1F083F2041D1F553F2AF8B62 /* Pods-Runner.release.xcconfig */, AD0937B0140D5A4C24E73BEA /* Pods-Runner.profile.xcconfig */, + 0B80439B9064C9708DDB0ADA /* breez_sdk-OnDemandResources */, ); path = Pods; sourceTree = ""; @@ -100,6 +153,8 @@ 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( + CE291CFD2C15DB9A00B9F709 /* WowneroWallet.framework */, + 0C50DFB82BF3CB56002B0EB3 /* MoneroWallet.framework */, 0C44A7182518EF4A00B570ED /* CakeWallet */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, @@ -134,6 +189,26 @@ path = Runner; sourceTree = ""; }; + ADEC151FA90C8F1EBCDA8CA3 /* BreezSDK */ = { + isa = PBXGroup; + children = ( + ABD6FCBB0F4244B090459128 /* BreezSDK.swift */, + 1FB06A93B13D606F06B3924D /* BreezSDKConnector.swift */, + 0CCA7ADAD6FF9185EBBB2BCA /* Constants.swift */, + 78D25C60B94E9D9E48D52E5E /* ResourceHelper.swift */, + 41102141140E57B1DC27FBA1 /* SDKNotificationService.swift */, + B3D5E78267F5F18D882FDC3B /* ServiceConfig.swift */, + F42258C3697CFE3C8C8D1933 /* ServiceLogger.swift */, + 0F42D8065219E0653321EE2B /* TaskProtocol.swift */, + 7D3364C03978A8A74B6D586E /* LnurlPay.swift */, + 58C22CBD8C22B9D6023D59F8 /* LnurlPayInfo.swift */, + DCEA540E3586164FB47AD13E /* LnurlPayInvoice.swift */, + 91C524F800843E0A3F17E004 /* ReceivePayment.swift */, + 9D2F2C9F2555316C95EE7EA3 /* RedeemSwap.swift */, + ); + name = BreezSDK; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -142,6 +217,7 @@ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( B91154210ADCED81FBF06A85 /* [CP] Check Pods Manifest.lock */, + CE5E8A222BEE19C700608EA1 /* CopyFiles */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, @@ -164,6 +240,9 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { + KnownAssetTags = ( + BreezSDK, + ); LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { @@ -201,6 +280,19 @@ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 9F46EE5E2BC11178009318F5 /* PrivacyInfo.xcprivacy in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + D3AD73A327249AFE8F016A51 /* BreezSDK.swift in Resources */, + 525A2200C6C2A43EDC5C8FC5 /* BreezSDKConnector.swift in Resources */, + 724FDA327BF191BC29DCAA2E /* Constants.swift in Resources */, + 2193F104374FA2746CE8945B /* ResourceHelper.swift in Resources */, + F5EE19868D6F10D814BF73AD /* SDKNotificationService.swift in Resources */, + B6C6E59403ACDE44724C12F4 /* ServiceConfig.swift in Resources */, + 73138617307FA4F838D21D62 /* ServiceLogger.swift in Resources */, + 495FEFF9B395392FED3425DE /* TaskProtocol.swift in Resources */, + A1B4A70C9CFA13AB71662216 /* LnurlPay.swift in Resources */, + CFEFC24F82F78FE747DF1D22 /* LnurlPayInfo.swift in Resources */, + 6909E1D79C9986ADF2DE41E9 /* LnurlPayInvoice.swift in Resources */, + D0D7A0D4E13F31C4E02E235B /* ReceivePayment.swift in Resources */, + A3D5E17CC53DF13FA740DEFA /* RedeemSwap.swift in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -391,7 +483,7 @@ "$(PROJECT_DIR)/Flutter", ); MARKETING_VERSION = 1.0.1; - PRODUCT_BUNDLE_IDENTIFIER = ""; + PRODUCT_BUNDLE_IDENTIFIER = "com.fotolockr.cakewallet"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -537,7 +629,7 @@ "$(PROJECT_DIR)/Flutter", ); MARKETING_VERSION = 1.0.1; - PRODUCT_BUNDLE_IDENTIFIER = ""; + PRODUCT_BUNDLE_IDENTIFIER = "com.fotolockr.cakewallet"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -575,7 +667,7 @@ "$(PROJECT_DIR)/Flutter", ); MARKETING_VERSION = 1.0.1; - PRODUCT_BUNDLE_IDENTIFIER = ""; + PRODUCT_BUNDLE_IDENTIFIER = "com.fotolockr.cakewallet"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/ios/Runner/InfoBase.plist b/ios/Runner/InfoBase.plist index 83e60b542..aec00022b 100644 --- a/ios/Runner/InfoBase.plist +++ b/ios/Runner/InfoBase.plist @@ -200,7 +200,7 @@ solana-wallet - + CFBundleTypeRole Viewer CFBundleURLName @@ -220,6 +220,26 @@ tron-wallet + + CFBundleTypeRole + Viewer + CFBundleURLName + wownero + CFBundleURLSchemes + + wownero + + + + CFBundleTypeRole + Viewer + CFBundleURLName + wownero-wallet + CFBundleURLSchemes + + wownero-wallet + + CFBundleVersion $(CURRENT_PROJECT_VERSION) diff --git a/ios/WowneroWallet.framework/Info.plist b/ios/WowneroWallet.framework/Info.plist new file mode 100644 index 000000000..61ab961f4 Binary files /dev/null and b/ios/WowneroWallet.framework/Info.plist differ diff --git a/ios/monero_libwallet2_api_c.dylib b/ios/monero_libwallet2_api_c.dylib new file mode 120000 index 000000000..4ad2b0213 --- /dev/null +++ b/ios/monero_libwallet2_api_c.dylib @@ -0,0 +1 @@ +../scripts/monero_c/release/monero/host-apple-ios_libwallet2_api_c.dylib \ No newline at end of file diff --git a/ios/wownero_libwallet2_api_c.dylib b/ios/wownero_libwallet2_api_c.dylib new file mode 120000 index 000000000..9baa13cc8 --- /dev/null +++ b/ios/wownero_libwallet2_api_c.dylib @@ -0,0 +1 @@ +../scripts/monero_c/release/wownero/host-apple-ios_libwallet2_api_c.dylib \ No newline at end of file diff --git a/lib/core/address_validator.dart b/lib/core/address_validator.dart index fe6629f51..92a735481 100644 --- a/lib/core/address_validator.dart +++ b/lib/core/address_validator.dart @@ -91,6 +91,7 @@ class AddressValidator extends TextValidator { case CryptoCurrency.dai: case CryptoCurrency.dash: case CryptoCurrency.eos: + case CryptoCurrency.wow: return '[0-9a-zA-Z]'; case CryptoCurrency.bch: return '^(?!bitcoincash:)[0-9a-zA-Z]*\$|^(?!bitcoincash:)q|p[0-9a-zA-Z]{41}\$|^(?!bitcoincash:)q|p[0-9a-zA-Z]{42}\$|^bitcoincash:q|p[0-9a-zA-Z]{41}\$|^bitcoincash:q|p[0-9a-zA-Z]{42}\$'; @@ -139,6 +140,7 @@ class AddressValidator extends TextValidator { switch (type) { case CryptoCurrency.xmr: + case CryptoCurrency.wow: return null; case CryptoCurrency.ada: return null; @@ -266,6 +268,7 @@ class AddressValidator extends TextValidator { static String? getAddressFromStringPattern(CryptoCurrency type) { switch (type) { case CryptoCurrency.xmr: + case CryptoCurrency.wow: return '([^0-9a-zA-Z]|^)4[0-9a-zA-Z]{94}([^0-9a-zA-Z]|\$)' '|([^0-9a-zA-Z]|^)8[0-9a-zA-Z]{94}([^0-9a-zA-Z]|\$)' '|([^0-9a-zA-Z]|^)[0-9a-zA-Z]{106}([^0-9a-zA-Z]|\$)'; diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index d0d9a8a26..577238baf 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cake_wallet/themes/theme_list.dart'; import 'package:cake_wallet/utils/device_info.dart'; +import 'package:cw_core/root_dir.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; @@ -76,7 +77,7 @@ class BackupService { Future _exportBackupV2(String password) async { final zipEncoder = ZipFileEncoder(); - final appDir = await getApplicationDocumentsDirectory(); + final appDir = await getAppDir(); final now = DateTime.now(); final tmpDir = Directory('${appDir.path}/~_BACKUP_TMP'); final archivePath = '${tmpDir.path}/backup_${now.toString()}.zip'; @@ -116,7 +117,7 @@ class BackupService { } Future _importBackupV1(Uint8List data, String password, {required String nonce}) async { - final appDir = await getApplicationDocumentsDirectory(); + final appDir = await getAppDir(); final decryptedData = await _decryptV1(data, password, nonce); final zip = ZipDecoder().decodeBytes(decryptedData); @@ -139,7 +140,7 @@ class BackupService { } Future _importBackupV2(Uint8List data, String password) async { - final appDir = await getApplicationDocumentsDirectory(); + final appDir = await getAppDir(); final decryptedData = await _decryptV2(data, password); final zip = ZipDecoder().decodeBytes(decryptedData); @@ -172,7 +173,7 @@ class BackupService { } Future> _reloadHiveWalletInfoBox() async { - final appDir = await getApplicationDocumentsDirectory(); + final appDir = await getAppDir(); await CakeHive.close(); CakeHive.init(appDir.path); @@ -184,7 +185,7 @@ class BackupService { } Future _importPreferencesDump() async { - final appDir = await getApplicationDocumentsDirectory(); + final appDir = await getAppDir(); final preferencesFile = File('${appDir.path}/~_preferences_dump'); if (!preferencesFile.existsSync()) { @@ -361,7 +362,7 @@ class BackupService { Future _importKeychainDumpV1(String password, {required String nonce, String keychainSalt = secrets.backupKeychainSalt}) async { - final appDir = await getApplicationDocumentsDirectory(); + final appDir = await getAppDir(); final keychainDumpFile = File('${appDir.path}/~_keychain_dump'); final decryptedKeychainDumpFileData = await _decryptV1(keychainDumpFile.readAsBytesSync(), '$keychainSalt$password', nonce); @@ -387,7 +388,7 @@ class BackupService { Future _importKeychainDumpV2(String password, {String keychainSalt = secrets.backupKeychainSalt}) async { - final appDir = await getApplicationDocumentsDirectory(); + final appDir = await getAppDir(); final keychainDumpFile = File('${appDir.path}/~_keychain_dump'); final decryptedKeychainDumpFileData = await _decryptV2(keychainDumpFile.readAsBytesSync(), '$keychainSalt$password'); diff --git a/lib/core/seed_validator.dart b/lib/core/seed_validator.dart index 9fb839ea2..2d2a0c6ee 100644 --- a/lib/core/seed_validator.dart +++ b/lib/core/seed_validator.dart @@ -8,6 +8,7 @@ import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/solana/solana.dart'; import 'package:cake_wallet/tron/tron.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cake_wallet/utils/language_list.dart'; import 'package:cw_core/wallet_type.dart'; @@ -43,7 +44,9 @@ class SeedValidator extends Validator { return solana!.getSolanaWordList(language); case WalletType.tron: return tron!.getTronWordList(language); - default: + case WalletType.wownero: + return wownero!.getWowneroWordList(language); + case WalletType.none: return []; } } diff --git a/lib/core/wallet_creation_service.dart b/lib/core/wallet_creation_service.dart index 1fa50a6be..2f3acb6c9 100644 --- a/lib/core/wallet_creation_service.dart +++ b/lib/core/wallet_creation_service.dart @@ -81,6 +81,7 @@ class WalletCreationService { case WalletType.tron: return true; case WalletType.monero: + case WalletType.wownero: case WalletType.none: case WalletType.bitcoin: case WalletType.litecoin: diff --git a/lib/di.dart b/lib/di.dart index 2c09d8cbc..9136260e5 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -215,6 +215,7 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; import 'package:cake_wallet/view_model/wallet_new_vm.dart'; import 'package:cake_wallet/view_model/wallet_restore_view_model.dart'; import 'package:cake_wallet/view_model/wallet_seed_view_model.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/receive_page_option.dart'; import 'package:cw_core/wallet_info.dart'; @@ -900,6 +901,7 @@ Future setup({ return bitcoinCash! .createBitcoinCashWalletService(_walletInfoSource, _unspentCoinsInfoSource); case WalletType.nano: + case WalletType.banano: return nano!.createNanoWalletService(_walletInfoSource); case WalletType.polygon: return polygon!.createPolygonWalletService(_walletInfoSource); @@ -907,7 +909,9 @@ Future setup({ return solana!.createSolanaWalletService(_walletInfoSource); case WalletType.tron: return tron!.createTronWalletService(_walletInfoSource); - default: + case WalletType.wownero: + return wownero!.createWowneroWalletService(_walletInfoSource, _unspentCoinsInfoSource); + case WalletType.none: throw Exception('Unexpected token: ${param1.toString()} for generating of WalletService'); } }); diff --git a/lib/entities/background_tasks.dart b/lib/entities/background_tasks.dart index 5db42381e..a373ca0ad 100644 --- a/lib/entities/background_tasks.dart +++ b/lib/entities/background_tasks.dart @@ -31,8 +31,6 @@ void callbackDispatcher() { final walletLoadingService = getIt.get(); - final node = getIt.get().getCurrentNode(WalletType.monero); - final typeRaw = getIt.get().getInt(PreferencesKey.currentWalletType); WalletBase? wallet; @@ -42,23 +40,25 @@ void callbackDispatcher() { final List moneroWallets = getIt .get() .wallets - .where((element) => element.type == WalletType.monero) + .where((element) => [WalletType.monero, WalletType.wownero].contains(element.type)) .toList(); for (int i = 0; i < moneroWallets.length; i++) { - wallet = await walletLoadingService.load(WalletType.monero, moneroWallets[i].name); - + wallet = + await walletLoadingService.load(moneroWallets[i].type, moneroWallets[i].name); + final node = getIt.get().getCurrentNode(moneroWallets[i].type); await wallet.connectToNode(node: node); await wallet.startSync(); } } else { /// if the user chose to sync only active wallet /// if the current wallet is monero; sync it only - if (typeRaw == WalletType.monero.index) { + if (typeRaw == WalletType.monero.index || typeRaw == WalletType.wownero.index) { final name = getIt.get().getString(PreferencesKey.currentWalletName); - wallet = await walletLoadingService.load(WalletType.monero, name!); + wallet = await walletLoadingService.load(WalletType.values[typeRaw!], name!); + final node = getIt.get().getCurrentNode(WalletType.values[typeRaw]); await wallet.connectToNode(node: node); await wallet.startSync(); @@ -66,7 +66,7 @@ void callbackDispatcher() { } if (wallet?.syncStatus.progress() == null) { - return Future.error("No Monero wallet found"); + return Future.error("No Monero/Wownero wallet found"); } for (int i = 0;; i++) { diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index 891d53f59..71a971a9a 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -5,8 +5,8 @@ import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; +import 'package:cw_core/root_dir.dart'; import 'package:hive/hive.dart'; -import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cw_core/wallet_type.dart'; @@ -39,6 +39,7 @@ const nanoDefaultPowNodeUri = 'rpc.nano.to'; const solanaDefaultNodeUri = 'rpc.ankr.com'; const tronDefaultNodeUri = 'trx.nownodes.io'; const newCakeWalletBitcoinUri = 'btc-electrum.cakewallet.com:50002'; +const wowneroDefaultNodeUri = 'node3.monerodevs.org:34568'; Future defaultSettingsMigration( {required int version, @@ -231,7 +232,8 @@ Future defaultSettingsMigration( await _switchElectRsNode(nodes, sharedPreferences); break; case 36: - await changeTronCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes); + await addWowneroNodeList(nodes: nodes); + await changeWowneroCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes); break; case 37: await replaceTronDefaultNode(sharedPreferences: sharedPreferences, nodes: nodes); @@ -305,7 +307,7 @@ Future _updateMoneroPriority(SharedPreferences sharedPreferences) async { Future _validateWalletInfoBoxData(Box walletInfoSource) async { try { - final root = await getApplicationDocumentsDirectory(); + final root = await getAppDir(); for (var type in WalletType.values) { if (type == WalletType.none) { @@ -498,6 +500,32 @@ Node? getTronDefaultNode({required Box nodes}) { nodes.values.firstWhereOrNull((node) => node.type == WalletType.tron); } +Node getWowneroDefaultNode({required Box nodes}) { + final timeZone = DateTime.now().timeZoneOffset.inHours; + var nodeUri = ''; + + if (timeZone >= 1) { + // Eurasia + nodeUri = 'node2.monerodevs.org.lol:34568'; + } else if (timeZone <= -4) { + // America + nodeUri = 'node3.monerodevs.org:34568'; + } + + if (nodeUri == '') { + return nodes.values.where((element) => element.type == WalletType.wownero).first; + } + + try { + return nodes.values.firstWhere( + (Node node) => node.uriRaw == nodeUri, + orElse: () => nodes.values.where((element) => element.type == WalletType.wownero).first, + ); + } catch (_) { + return nodes.values.where((element) => element.type == WalletType.wownero).first; + } +} + Future insecureStorageMigration({ required SharedPreferences sharedPreferences, required SecureStorage secureStorage, @@ -898,6 +926,7 @@ Future checkCurrentNodes( sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey); final currentSolanaNodeId = sharedPreferences.getInt(PreferencesKey.currentSolanaNodeIdKey); final currentTronNodeId = sharedPreferences.getInt(PreferencesKey.currentTronNodeIdKey); + final currentWowneroNodeId = sharedPreferences.getInt(PreferencesKey.currentWowneroNodeIdKey); final currentMoneroNode = nodeSource.values.firstWhereOrNull((node) => node.key == currentMoneroNodeId); final currentBitcoinElectrumServer = @@ -920,6 +949,8 @@ Future checkCurrentNodes( nodeSource.values.firstWhereOrNull((node) => node.key == currentSolanaNodeId); final currentTronNodeServer = nodeSource.values.firstWhereOrNull((node) => node.key == currentTronNodeId); + final currentWowneroNodeServer = + nodeSource.values.firstWhereOrNull((node) => node.key == currentWowneroNodeId); if (currentMoneroNode == null) { final newCakeWalletNode = Node(uri: newCakeWalletMoneroUri, type: WalletType.monero); await nodeSource.add(newCakeWalletNode); @@ -997,6 +1028,12 @@ Future checkCurrentNodes( await nodeSource.add(node); await sharedPreferences.setInt(PreferencesKey.currentTronNodeIdKey, node.key as int); } + + if (currentWowneroNodeServer == null) { + final node = Node(uri: wowneroDefaultNodeUri, type: WalletType.wownero); + await nodeSource.add(node); + await sharedPreferences.setInt(PreferencesKey.currentWowneroNodeIdKey, node.key as int); + } } Future resetBitcoinElectrumServer( @@ -1063,6 +1100,23 @@ Future changeEthereumCurrentNodeToDefault( await sharedPreferences.setInt(PreferencesKey.currentEthereumNodeIdKey, nodeId); } +Future addWowneroNodeList({required Box nodes}) async { + final nodeList = await loadDefaultWowneroNodes(); + for (var node in nodeList) { + if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) { + await nodes.add(node); + } + } +} + +Future changeWowneroCurrentNodeToDefault( + {required SharedPreferences sharedPreferences, required Box nodes}) async { + final node = getWowneroDefaultNode(nodes: nodes); + final nodeId = node?.key as int? ?? 0; + + await sharedPreferences.setInt(PreferencesKey.currentWowneroNodeIdKey, nodeId); +} + Future addNanoNodeList({required Box nodes}) async { final nodeList = await loadDefaultNanoNodes(); for (var node in nodeList) { diff --git a/lib/entities/language_service.dart b/lib/entities/language_service.dart index cfb850889..23d27dd38 100644 --- a/lib/entities/language_service.dart +++ b/lib/entities/language_service.dart @@ -63,6 +63,8 @@ class LanguageService { static final list = {}; + static const defaultLocale = 'en'; + static void loadLocaleList() { supportedLocales.forEach((key, value) { if (locales.contains(key)) { @@ -72,9 +74,16 @@ class LanguageService { } static Future localeDetection() async { - var locale = await Devicelocale.currentLocale ?? ''; - locale = Intl.shortLocale(locale); + try { + var locale = await Devicelocale.currentLocale ?? ''; + locale = Intl.shortLocale(locale); - return list.keys.contains(locale) ? locale : 'en'; + if (list.keys.contains(locale)) { + return locale; + } + return LanguageService.defaultLocale; + } catch(_) { + return LanguageService.defaultLocale; + } } } diff --git a/lib/entities/node_list.dart b/lib/entities/node_list.dart index c1211d2fe..85e37a7bc 100644 --- a/lib/entities/node_list.dart +++ b/lib/entities/node_list.dart @@ -183,6 +183,23 @@ Future> loadDefaultTronNodes() async { return nodes; } +Future> loadDefaultWowneroNodes() async { + final nodesRaw = await rootBundle.loadString('assets/wownero_node_list.yml'); + final loadedNodes = loadYaml(nodesRaw) as YamlList; + final nodes = []; + + for (final raw in loadedNodes) { + if (raw is Map) { + final node = Node.fromMap(Map.from(raw)); + + node.type = WalletType.wownero; + nodes.add(node); + } + } + + return nodes; +} + Future resetToDefault(Box nodeSource) async { final moneroNodes = await loadDefaultNodes(); final bitcoinElectrumServerList = await loadBitcoinElectrumServerList(); diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index fdcd54c9c..e1ee0ada3 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -15,6 +15,7 @@ class PreferencesKey { static const currentBitcoinCashNodeIdKey = 'current_node_id_bch'; static const currentSolanaNodeIdKey = 'current_node_id_sol'; static const currentTronNodeIdKey = 'current_node_id_trx'; + static const currentWowneroNodeIdKey = 'current_node_id_wow'; static const currentTransactionPriorityKeyLegacy = 'current_fee_priority'; static const currentBalanceDisplayModeKey = 'current_balance_display_mode'; static const shouldSaveRecipientAddressKey = 'save_recipient_address'; @@ -43,6 +44,7 @@ class PreferencesKey { static const ethereumTransactionPriority = 'current_fee_priority_ethereum'; static const polygonTransactionPriority = 'current_fee_priority_polygon'; static const bitcoinCashTransactionPriority = 'current_fee_priority_bitcoin_cash'; + static const wowneroTransactionPriority = 'current_fee_priority_wownero'; static const customBitcoinFeeRate = 'custom_electrum_fee_rate'; static const silentPaymentsCardDisplay = 'silentPaymentsCardDisplay'; static const silentPaymentsAlwaysScan = 'silentPaymentsAlwaysScan'; diff --git a/lib/entities/priority_for_wallet_type.dart b/lib/entities/priority_for_wallet_type.dart index 0151c8115..534287494 100644 --- a/lib/entities/priority_for_wallet_type.dart +++ b/lib/entities/priority_for_wallet_type.dart @@ -4,6 +4,7 @@ import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/haven/haven.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/polygon/polygon.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/wallet_type.dart'; @@ -11,6 +12,8 @@ List priorityForWalletType(WalletType type) { switch (type) { case WalletType.monero: return monero!.getTransactionPriorities(); + case WalletType.wownero: + return wownero!.getTransactionPriorities(); case WalletType.bitcoin: return bitcoin!.getTransactionPriorities(); case WalletType.litecoin: diff --git a/lib/entities/provider_types.dart b/lib/entities/provider_types.dart index 37a492987..da7bae4c1 100644 --- a/lib/entities/provider_types.dart +++ b/lib/entities/provider_types.dart @@ -51,6 +51,7 @@ class ProvidersHelper { switch (walletType) { case WalletType.nano: case WalletType.banano: + case WalletType.wownero: return [ProviderType.askEachTime, ProviderType.onramper]; case WalletType.monero: return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.dfx]; @@ -114,6 +115,7 @@ class ProvidersHelper { case WalletType.banano: case WalletType.none: case WalletType.haven: + case WalletType.wownero: return []; } } diff --git a/lib/entities/seed_type.dart b/lib/entities/seed_type.dart index ab9965528..bc2f6cff7 100644 --- a/lib/entities/seed_type.dart +++ b/lib/entities/seed_type.dart @@ -10,6 +10,7 @@ class SeedType extends EnumerableItem with Serializable { static const legacy = SeedType(raw: 0, title: 'Legacy (25 words)'); static const polyseed = SeedType(raw: 1, title: 'Polyseed (16 words)'); + static const wowneroSeed = SeedType(raw: 2, title: 'Wownero (14 words)'); static SeedType deserialize({required int raw}) { switch (raw) { @@ -17,6 +18,8 @@ class SeedType extends EnumerableItem with Serializable { return legacy; case 1: return polyseed; + case 2: + return wowneroSeed; default: throw Exception('Unexpected token: $raw for SeedType deserialize'); } @@ -29,6 +32,8 @@ class SeedType extends EnumerableItem with Serializable { return S.current.seedtype_legacy; case SeedType.polyseed: return S.current.seedtype_polyseed; + case SeedType.wowneroSeed: + return S.current.seedtype_wownero; default: return ''; } diff --git a/lib/main.dart b/lib/main.dart index fbe77bd31..8539ac803 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,45 +1,45 @@ import 'dart:async'; import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart'; +import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/core/auth_service.dart'; +import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/entities/contact.dart'; +import 'package:cake_wallet/entities/default_settings_migration.dart'; +import 'package:cake_wallet/entities/get_encryption_key.dart'; import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cake_wallet/entities/language_service.dart'; -import 'package:cake_wallet/buy/order.dart'; +import 'package:cake_wallet/entities/template.dart'; +import 'package:cake_wallet/entities/transaction_description.dart'; +import 'package:cake_wallet/exchange/exchange_template.dart'; +import 'package:cake_wallet/exchange/trade.dart'; +import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/locales/locale.dart'; +import 'package:cake_wallet/monero/monero.dart'; +import 'package:cake_wallet/reactions/bootstrap.dart'; +import 'package:cake_wallet/router.dart' as Router; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/root/root.dart'; +import 'package:cake_wallet/store/app_store.dart'; +import 'package:cake_wallet/store/authentication_store.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/exception_handler.dart'; import 'package:cake_wallet/view_model/link_view_model.dart'; -import 'package:cw_core/address_info.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; +import 'package:cw_core/address_info.dart'; +import 'package:cw_core/cake_hive.dart'; import 'package:cw_core/hive_type_ids.dart'; +import 'package:cw_core/node.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:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:hive/hive.dart'; -import 'package:cake_wallet/di.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:cake_wallet/themes/theme_base.dart'; -import 'package:cake_wallet/router.dart' as Router; -import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/reactions/bootstrap.dart'; -import 'package:cake_wallet/store/app_store.dart'; -import 'package:cake_wallet/store/authentication_store.dart'; -import 'package:cake_wallet/entities/transaction_description.dart'; -import 'package:cake_wallet/entities/get_encryption_key.dart'; -import 'package:cake_wallet/entities/contact.dart'; -import 'package:cw_core/node.dart'; -import 'package:cw_core/wallet_info.dart'; -import 'package:cake_wallet/entities/default_settings_migration.dart'; -import 'package:cw_core/wallet_type.dart'; -import 'package:cake_wallet/entities/template.dart'; -import 'package:cake_wallet/exchange/trade.dart'; -import 'package:cake_wallet/exchange/exchange_template.dart'; -import 'package:cake_wallet/src/screens/root/root.dart'; -import 'package:cw_core/unspent_coins_info.dart'; -import 'package:cake_wallet/monero/monero.dart'; -import 'package:cw_core/cake_hive.dart'; +import 'package:hive/hive.dart'; +import 'package:cw_core/root_dir.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:cw_core/window_size.dart'; final navigatorKey = GlobalKey(); @@ -103,7 +103,8 @@ Future main() async { } Future initializeAppConfigs() async { - final appDir = await getApplicationDocumentsDirectory(); + setRootDirFromEnv(); + final appDir = await getAppDir(); CakeHive.init(appDir.path); if (!CakeHive.isAdapterRegistered(Contact.typeId)) { diff --git a/lib/monero/cw_monero.dart b/lib/monero/cw_monero.dart index b4d85089a..c1384a3df 100644 --- a/lib/monero/cw_monero.dart +++ b/lib/monero/cw_monero.dart @@ -341,4 +341,9 @@ class CWMonero extends Monero { final moneroWallet = wallet as MoneroWallet; await moneroWallet.updateUnspent(); } + + @override + Future getCurrentHeight() async { + return monero_wallet_api.getCurrentHeight(); + } } diff --git a/lib/reactions/on_current_wallet_change.dart b/lib/reactions/on_current_wallet_change.dart index a6ce2bae9..6f1ba1d8c 100644 --- a/lib/reactions/on_current_wallet_change.dart +++ b/lib/reactions/on_current_wallet_change.dart @@ -71,6 +71,7 @@ void startCurrentWalletChangeReaction( .setInt(PreferencesKey.currentWalletType, serializeToInt(wallet.type)); if (wallet.type == WalletType.monero || + wallet.type == WalletType.wownero || wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin || wallet.type == WalletType.bitcoinCash) { diff --git a/lib/src/screens/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index bec10435e..7a2055930 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -7,6 +7,7 @@ import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sideba import 'package:cake_wallet/src/screens/dashboard/pages/cake_features_page.dart'; import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/bottom_sheet_listener.dart'; import 'package:cake_wallet/src/widgets/gradient_background.dart'; +import 'package:cake_wallet/src/widgets/haven_wallet_removal_popup.dart'; import 'package:cake_wallet/src/widgets/services_updates_widget.dart'; import 'package:cake_wallet/src/widgets/vulnerable_seeds_popup.dart'; import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; @@ -351,10 +352,12 @@ class _DashboardPageView extends BasePage { _showVulnerableSeedsPopup(context); + _showHavenPopup(context); + var needToPresentYat = false; var isInactive = false; - _onInactiveSub = rootKey.currentState!.isInactive.listen( + _onInactiveSub = rootKey.currentState?.isInactive.listen( (inactive) { isInactive = inactive; @@ -428,4 +431,22 @@ class _DashboardPageView extends BasePage { ); } } + + void _showHavenPopup(BuildContext context) async { + final List havenWalletList = await dashboardViewModel.checkForHavenWallets(); + + if (havenWalletList.isNotEmpty) { + Future.delayed( + Duration(seconds: 1), + () { + showPopUp( + context: context, + builder: (BuildContext context) { + return HavenWalletRemovalPopup(havenWalletList); + }, + ); + }, + ); + } + } } diff --git a/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart b/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart index 663675849..46e63af01 100644 --- a/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart +++ b/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart @@ -40,6 +40,7 @@ class _DesktopWalletSelectionDropDownState extends State Image.asset( diff --git a/lib/src/screens/dashboard/widgets/menu_widget.dart b/lib/src/screens/dashboard/widgets/menu_widget.dart index 7eda20bff..78d8abc95 100644 --- a/lib/src/screens/dashboard/widgets/menu_widget.dart +++ b/lib/src/screens/dashboard/widgets/menu_widget.dart @@ -36,7 +36,8 @@ class MenuWidgetState extends State { this.bitcoinCashIcon = Image.asset('assets/images/bch_icon.png'), this.polygonIcon = Image.asset('assets/images/matic_icon.png'), this.solanaIcon = Image.asset('assets/images/sol_icon.png'), - this.tronIcon = Image.asset('assets/images/trx_icon.png'); + this.tronIcon = Image.asset('assets/images/trx_icon.png'), + this.wowneroIcon = Image.asset('assets/images/wownero_icon.png'); final largeScreen = 731; @@ -60,6 +61,7 @@ class MenuWidgetState extends State { Image polygonIcon; Image solanaIcon; Image tronIcon; + Image wowneroIcon; @override void initState() { @@ -236,6 +238,8 @@ class MenuWidgetState extends State { return solanaIcon; case WalletType.tron: return tronIcon; + case WalletType.wownero: + return wowneroIcon; default: throw Exception('No icon for ${type.toString()}'); } diff --git a/lib/src/screens/new_wallet/new_wallet_page.dart b/lib/src/screens/new_wallet/new_wallet_page.dart index 8d46827eb..306c41479 100644 --- a/lib/src/screens/new_wallet/new_wallet_page.dart +++ b/lib/src/screens/new_wallet/new_wallet_page.dart @@ -82,7 +82,7 @@ class _WalletNameFormState extends State { void initState() { _stateReaction ??= reaction((_) => _walletNewVM.state, (ExecutionState state) async { if (state is ExecutedSuccessfullyState) { - Navigator.of(navigatorKey.currentContext!) + Navigator.of(navigatorKey.currentContext ?? context) .pushNamed(Routes.preSeedPage, arguments: _walletNewVM.seedPhraseWordsLength); } diff --git a/lib/src/screens/rescan/rescan_page.dart b/lib/src/screens/rescan/rescan_page.dart index c59ae4ad0..4b2327c43 100644 --- a/lib/src/screens/rescan/rescan_page.dart +++ b/lib/src/screens/rescan/rescan_page.dart @@ -33,6 +33,7 @@ class RescanPage extends BasePage { doSingleScan: _rescanViewModel.doSingleScan, toggleSingleScan: () => _rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan, + walletType: _rescanViewModel.wallet.type, )), Observer( builder: (_) => LoadingPrimaryButton( diff --git a/lib/src/screens/restore/wallet_restore_from_keys_form.dart b/lib/src/screens/restore/wallet_restore_from_keys_form.dart index 588fd7187..f8336a2e8 100644 --- a/lib/src/screens/restore/wallet_restore_from_keys_form.dart +++ b/lib/src/screens/restore/wallet_restore_from_keys_form.dart @@ -171,6 +171,7 @@ class WalletRestoreFromKeysFromState extends State { hasDatePicker: widget.walletRestoreViewModel.type != WalletType.haven, onHeightChange: (_) => null, onHeightOrDateEntered: widget.onHeightOrDateEntered, + walletType: widget.walletRestoreViewModel.type, ), ], ); diff --git a/lib/src/screens/restore/wallet_restore_from_seed_form.dart b/lib/src/screens/restore/wallet_restore_from_seed_form.dart index 288862ce7..1f22af0cb 100644 --- a/lib/src/screens/restore/wallet_restore_from_seed_form.dart +++ b/lib/src/screens/restore/wallet_restore_from_seed_form.dart @@ -83,12 +83,17 @@ class WalletRestoreFromSeedFormState extends State { } void onSeedChange(String seed) { - if (widget.type == WalletType.monero && Polyseed.isValidSeed(seed)) { + if ((widget.type == WalletType.monero || widget.type == WalletType.wownero) && + Polyseed.isValidSeed(seed)) { final lang = PolyseedLang.getByPhrase(seed); _changeSeedType(SeedType.polyseed); _changeLanguage(lang.nameEnglish); } + if (widget.type == WalletType.wownero && seed.split(" ").length == 14) { + _changeSeedType(SeedType.wowneroSeed); + _changeLanguage("English"); + } widget.onSeedChange?.call(seed); } @@ -142,14 +147,18 @@ class WalletRestoreFromSeedFormState extends State { language: language, type: widget.type, onSeedChange: onSeedChange), - if (widget.type == WalletType.monero) + if (widget.type == WalletType.monero || widget.type == WalletType.wownero) GestureDetector( onTap: () async { await showPopUp( context: context, builder: (_) => Picker( - items: SeedType.all, - selectedAtIndex: isPolyseed ? 1 : 0, + items: _getItems(), + selectedAtIndex: isPolyseed + ? 1 + : seedTypeController.value.text.contains("14") + ? 2 + : 0, mainAxisAlignment: MainAxisAlignment.start, onItemSelected: _changeSeedType, isSeparated: false, @@ -168,7 +177,7 @@ class WalletRestoreFromSeedFormState extends State { ), ), ), - if (widget.displayLanguageSelector) + if (!seedTypeController.value.text.contains("14") && widget.displayLanguageSelector) GestureDetector( onTap: () async { await showPopUp( @@ -192,12 +201,14 @@ class WalletRestoreFromSeedFormState extends State { ), ), ), - if (!isPolyseed && widget.displayBlockHeightSelector) + if ((!isPolyseed) && widget.displayBlockHeightSelector) BlockchainHeightWidget( - focusNode: widget.blockHeightFocusNode, - key: blockchainHeightKey, - onHeightOrDateEntered: widget.onHeightOrDateEntered, - hasDatePicker: widget.type == WalletType.monero), + focusNode: widget.blockHeightFocusNode, + key: blockchainHeightKey, + onHeightOrDateEntered: widget.onHeightOrDateEntered, + hasDatePicker: widget.type == WalletType.monero || widget.type == WalletType.wownero, + walletType: widget.type, + ), if (widget.displayPassphrase) ...[ const SizedBox(height: 10), BaseTextFormField( @@ -209,7 +220,9 @@ class WalletRestoreFromSeedFormState extends State { ])); } - bool get isPolyseed => widget.seedTypeViewModel.moneroSeedType == SeedType.polyseed; + bool get isPolyseed => + widget.seedTypeViewModel.moneroSeedType == SeedType.polyseed && + (widget.type == WalletType.monero || widget.type == WalletType.wownero); Widget get expandIcon => Container( padding: EdgeInsets.all(18), @@ -223,7 +236,11 @@ class WalletRestoreFromSeedFormState extends State { ); void _changeLanguage(String language) { - final setLang = isPolyseed ? "POLYSEED_$language" : language; + final setLang = isPolyseed + ? "POLYSEED_$language" + : seedTypeController.value.text.contains("14") + ? "WOWSEED_" + language + : language; setState(() { this.language = setLang; seedWidgetStateKey.currentState!.changeSeedLanguage(setLang); @@ -244,4 +261,15 @@ class WalletRestoreFromSeedFormState extends State { void _setSeedType(SeedType item) { seedTypeController.text = item.toString(); } + + List _getItems() { + switch (widget.type) { + case WalletType.monero: + return [SeedType.legacy, SeedType.polyseed]; + case WalletType.wownero: + return [SeedType.legacy, SeedType.polyseed, SeedType.wowneroSeed]; + default: + return [SeedType.legacy]; + } + } } diff --git a/lib/src/screens/restore/wallet_restore_page.dart b/lib/src/screens/restore/wallet_restore_page.dart index 8e6ee0983..a9bd52b26 100644 --- a/lib/src/screens/restore/wallet_restore_page.dart +++ b/lib/src/screens/restore/wallet_restore_page.dart @@ -20,7 +20,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:keyboard_actions/keyboard_actions.dart'; import 'package:mobx/mobx.dart'; -import 'package:polyseed/polyseed.dart'; import 'package:smooth_page_indicator/smooth_page_indicator.dart'; class WalletRestorePage extends BasePage { @@ -48,8 +47,7 @@ class WalletRestorePage extends BasePage { } }, onSeedChange: (String seed) { - final isPolyseed = - walletRestoreViewModel.type == WalletType.monero && Polyseed.isValidSeed(seed); + final isPolyseed = walletRestoreViewModel.isPolyseed(seed); _validateOnChange(isPolyseed: isPolyseed); }, onLanguageChange: (String language) { @@ -103,11 +101,11 @@ class WalletRestorePage extends BasePage { @override Function(BuildContext)? get pushToNextWidget => (context) { - FocusScopeNode currentFocus = FocusScope.of(context); - if (!currentFocus.hasPrimaryFocus) { - currentFocus.focusedChild?.unfocus(); - } - }; + FocusScopeNode currentFocus = FocusScope.of(context); + if (!currentFocus.hasPrimaryFocus) { + currentFocus.focusedChild?.unfocus(); + } + }; @override Widget body(BuildContext context) { @@ -253,12 +251,14 @@ class WalletRestorePage extends BasePage { bool _isValidSeed() { final seedPhrase = walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.text; - if (walletRestoreViewModel.type == WalletType.monero && Polyseed.isValidSeed(seedPhrase)) - return true; + if (walletRestoreViewModel.isPolyseed(seedPhrase)) return true; final seedWords = seedPhrase.split(' '); + if (seedWords.length == 14 && walletRestoreViewModel.type == WalletType.wownero) return true; + if ((walletRestoreViewModel.type == WalletType.monero || + walletRestoreViewModel.type == WalletType.wownero || walletRestoreViewModel.type == WalletType.haven) && seedWords.length != WalletRestoreViewModelBase.moneroSeedMnemonicLength) { return false; diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index a89d5e66f..2a4841608 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -106,6 +106,7 @@ class WalletListBodyState extends State { final polygonIcon = Image.asset('assets/images/matic_icon.png', height: 24, width: 24); final solanaIcon = Image.asset('assets/images/sol_icon.png', height: 24, width: 24); final tronIcon = Image.asset('assets/images/trx_icon.png', height: 24, width: 24); + final wowneroIcon = Image.asset('assets/images/wownero_icon.png', height: 24, width: 24); final scrollController = ScrollController(); final double tileHeight = 60; Flushbar? _progressBar; @@ -319,6 +320,7 @@ class WalletListBodyState extends State { case WalletType.bitcoinCash: return bitcoinCashIcon; case WalletType.nano: + case WalletType.banano: return nanoIcon; case WalletType.polygon: return polygonIcon; @@ -326,7 +328,9 @@ class WalletListBodyState extends State { return solanaIcon; case WalletType.tron: return tronIcon; - default: + case WalletType.wownero: + return wowneroIcon; + case WalletType.none: return nonWalletTypeIcon; } } diff --git a/lib/src/widgets/blockchain_height_widget.dart b/lib/src/widgets/blockchain_height_widget.dart index d85680cc8..4023e66ad 100644 --- a/lib/src/widgets/blockchain_height_widget.dart +++ b/lib/src/widgets/blockchain_height_widget.dart @@ -2,6 +2,8 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/src/widgets/standard_switch.dart'; import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; import 'package:cake_wallet/utils/date_picker.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; +import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -18,6 +20,7 @@ class BlockchainHeightWidget extends StatefulWidget { this.isSilentPaymentsScan = false, this.toggleSingleScan, this.doSingleScan = false, + required this.walletType, }) : super(key: key); final Function(int)? onHeightChange; @@ -27,6 +30,7 @@ class BlockchainHeightWidget extends StatefulWidget { final bool isSilentPaymentsScan; final bool doSingleScan; final Function()? toggleSingleScan; + final WalletType walletType; @override State createState() => BlockchainHeightState(); @@ -160,7 +164,13 @@ class BlockchainHeightState extends State { if (widget.isSilentPaymentsScan) { height = bitcoin!.getHeightByDate(date: date); } else { - height = monero!.getHeightByDate(date: date); + if (widget.walletType == WalletType.monero) { + height = monero!.getHeightByDate(date: date); + } else { + assert(widget.walletType == WalletType.wownero, + "unknown currency in BlockchainHeightWidget"); + height = wownero!.getHeightByDate(date: date); + } } setState(() { dateController.text = DateFormat('yyyy-MM-dd').format(date); diff --git a/lib/src/widgets/haven_wallet_removal_popup.dart b/lib/src/widgets/haven_wallet_removal_popup.dart new file mode 100644 index 000000000..05a38c41e --- /dev/null +++ b/lib/src/widgets/haven_wallet_removal_popup.dart @@ -0,0 +1,91 @@ +import 'package:cake_wallet/src/widgets/alert_background.dart'; +import 'package:cake_wallet/src/widgets/alert_close_button.dart'; +import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; +import 'package:flutter/material.dart'; + +class HavenWalletRemovalPopup extends StatelessWidget { + final List affectedWalletNames; + + const HavenWalletRemovalPopup(this.affectedWalletNames, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Stack( + alignment: Alignment.center, + children: [ + AlertBackground( + child: AlertDialog( + insetPadding: EdgeInsets.only(left: 16, right: 16, bottom: 48), + elevation: 0.0, + contentPadding: EdgeInsets.zero, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(30))), + content: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + gradient: LinearGradient(colors: [ + Theme.of(context).extension()!.firstGradientBackgroundColor, + Theme.of(context) + .extension()! + .secondGradientBackgroundColor, + ], begin: Alignment.centerLeft, end: Alignment.centerRight)), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24.0), + child: Stack( + children: [ + SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.only(top: 16.0), + child: Container( + alignment: Alignment.bottomCenter, + child: DefaultTextStyle( + style: TextStyle( + decoration: TextDecoration.none, + fontSize: 24.0, + fontWeight: FontWeight.bold, + fontFamily: 'Lato', + color: Theme.of(context).extension()!.textColor, + ), + child: Text("Emergency Notice"), + ), + ), + ), + ), + SingleChildScrollView( + child: Padding( + padding: EdgeInsets.only(top: 48, bottom: 16), + child: Container( + width: double.maxFinite, + child: Column( + children: [ + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height * 0.7, + ), + child: Text( + "It looks like you have Haven wallets in your list. Haven is getting removed in next release of Cake Wallet, and you currently have Haven in the following wallets:\n\n[${affectedWalletNames.join(", ")}]\n\nPlease move your funds to other wallet, as you will lose access to your Haven funds in next update.\n\nFor assistance, please use the in-app support or email support@cakewallet.com", + style: TextStyle( + decoration: TextDecoration.none, + fontSize: 16.0, + fontFamily: 'Lato', + color: Theme.of(context) + .extension()! + .textColor, + ), + ), + ) + ], + ), + ), + ), + ), + ], + ), + ), + ), + ), + ), + AlertCloseButton(bottom: 30) + ], + ); + } +} diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 05af3f3b1..8e16adbff 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -20,6 +20,7 @@ import 'package:cake_wallet/view_model/settings/sync_mode.dart'; import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/wallet_type_utils.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/themes/theme_list.dart'; @@ -27,7 +28,7 @@ import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; -import 'package:package_info/package_info.dart'; +import 'package:cake_wallet/utils/package_info.dart'; import 'package:cake_wallet/di.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -111,6 +112,7 @@ abstract class SettingsStoreBase with Store { required this.silentPaymentsAlwaysScan, TransactionPriority? initialBitcoinTransactionPriority, TransactionPriority? initialMoneroTransactionPriority, + TransactionPriority? initialWowneroTransactionPriority, TransactionPriority? initialHavenTransactionPriority, TransactionPriority? initialLitecoinTransactionPriority, TransactionPriority? initialEthereumTransactionPriority, @@ -168,6 +170,10 @@ abstract class SettingsStoreBase with Store { priority[WalletType.monero] = initialMoneroTransactionPriority; } + if (initialWowneroTransactionPriority != null) { + priority[WalletType.wownero] = initialWowneroTransactionPriority; + } + if (initialBitcoinTransactionPriority != null) { priority[WalletType.bitcoin] = initialBitcoinTransactionPriority; } @@ -249,6 +255,7 @@ abstract class SettingsStoreBase with Store { final String? key; switch (change.key) { case WalletType.monero: + case WalletType.wownero: key = PreferencesKey.moneroTransactionPriority; break; case WalletType.bitcoin: @@ -789,6 +796,7 @@ abstract class SettingsStoreBase with Store { TransactionPriority? ethereumTransactionPriority; TransactionPriority? polygonTransactionPriority; TransactionPriority? bitcoinCashTransactionPriority; + TransactionPriority? wowneroTransactionPriority; if (sharedPreferences.getInt(PreferencesKey.havenTransactionPriority) != null) { havenTransactionPriority = monero?.deserializeMoneroTransactionPriority( @@ -810,6 +818,10 @@ abstract class SettingsStoreBase with Store { bitcoinCashTransactionPriority = bitcoinCash?.deserializeBitcoinCashTransactionPriority( sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority)!); } + if (sharedPreferences.getInt(PreferencesKey.wowneroTransactionPriority) != null) { + wowneroTransactionPriority = wownero?.deserializeWowneroTransactionPriority( + raw: sharedPreferences.getInt(PreferencesKey.wowneroTransactionPriority)!); + } moneroTransactionPriority ??= monero?.getDefaultTransactionPriority(); bitcoinTransactionPriority ??= bitcoin?.getMediumTransactionPriority(); @@ -817,6 +829,7 @@ abstract class SettingsStoreBase with Store { litecoinTransactionPriority ??= bitcoin?.getLitecoinTransactionPriorityMedium(); ethereumTransactionPriority ??= ethereum?.getDefaultTransactionPriority(); bitcoinCashTransactionPriority ??= bitcoinCash?.getDefaultTransactionPriority(); + wowneroTransactionPriority ??= wownero?.getDefaultTransactionPriority(); polygonTransactionPriority ??= polygon?.getDefaultTransactionPriority(); final currentBalanceDisplayMode = BalanceDisplayMode.deserialize( @@ -902,6 +915,7 @@ abstract class SettingsStoreBase with Store { final nanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoPowNodeIdKey); final solanaNodeId = sharedPreferences.getInt(PreferencesKey.currentSolanaNodeIdKey); final tronNodeId = sharedPreferences.getInt(PreferencesKey.currentTronNodeIdKey); + final wowneroNodeId = sharedPreferences.getInt(PreferencesKey.currentWowneroNodeIdKey); final moneroNode = nodeSource.get(nodeId); final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId); final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId); @@ -913,6 +927,7 @@ abstract class SettingsStoreBase with Store { final nanoPowNode = powNodeSource.get(nanoPowNodeId); final solanaNode = nodeSource.get(solanaNodeId); final tronNode = nodeSource.get(tronNodeId); + final wowneroNode = nodeSource.get(wowneroNodeId); final packageInfo = await PackageInfo.fromPlatform(); final deviceName = await _getDeviceName() ?? ''; final shouldShowYatPopup = sharedPreferences.getBool(PreferencesKey.shouldShowYatPopup) ?? true; @@ -979,6 +994,10 @@ abstract class SettingsStoreBase with Store { nodes[WalletType.tron] = tronNode; } + if (wowneroNode != null) { + nodes[WalletType.wownero] = wowneroNode; + } + final savedSyncMode = SyncMode.all.firstWhere((element) { return element.type.index == (sharedPreferences.getInt(PreferencesKey.syncModeKey) ?? 0); }); @@ -1127,6 +1146,7 @@ abstract class SettingsStoreBase with Store { silentPaymentsCardDisplay: silentPaymentsCardDisplay, silentPaymentsAlwaysScan: silentPaymentsAlwaysScan, initialMoneroTransactionPriority: moneroTransactionPriority, + initialWowneroTransactionPriority: wowneroTransactionPriority, initialBitcoinTransactionPriority: bitcoinTransactionPriority, initialHavenTransactionPriority: havenTransactionPriority, initialLitecoinTransactionPriority: litecoinTransactionPriority, @@ -1164,6 +1184,10 @@ abstract class SettingsStoreBase with Store { raw: sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority)!) ?? priority[WalletType.monero]!; + priority[WalletType.wownero] = wownero?.deserializeWowneroTransactionPriority( + raw: sharedPreferences.getInt(PreferencesKey.wowneroTransactionPriority)!) ?? + priority[WalletType.wownero]!; + if (bitcoin != null && sharedPreferences.getInt(PreferencesKey.bitcoinTransactionPriority) != null) { priority[WalletType.bitcoin] = bitcoin!.deserializeBitcoinTransactionPriority( @@ -1282,6 +1306,7 @@ abstract class SettingsStoreBase with Store { final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey); final solanaNodeId = sharedPreferences.getInt(PreferencesKey.currentSolanaNodeIdKey); final tronNodeId = sharedPreferences.getInt(PreferencesKey.currentTronNodeIdKey); + final wowneroNodeId = sharedPreferences.getInt(PreferencesKey.currentWowneroNodeIdKey); final moneroNode = nodeSource.get(nodeId); final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId); final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId); @@ -1292,6 +1317,7 @@ abstract class SettingsStoreBase with Store { final nanoNode = nodeSource.get(nanoNodeId); final solanaNode = nodeSource.get(solanaNodeId); final tronNode = nodeSource.get(tronNodeId); + final wowneroNode = nodeSource.get(wowneroNodeId); if (moneroNode != null) { nodes[WalletType.monero] = moneroNode; } @@ -1332,6 +1358,10 @@ abstract class SettingsStoreBase with Store { nodes[WalletType.tron] = tronNode; } + if (wowneroNode != null) { + nodes[WalletType.wownero] = wowneroNode; + } + // MIGRATED: useTOTP2FA = await SecureKey.getBool( @@ -1465,6 +1495,9 @@ abstract class SettingsStoreBase with Store { case WalletType.tron: await _sharedPreferences.setInt(PreferencesKey.currentTronNodeIdKey, node.key as int); break; + case WalletType.wownero: + await _sharedPreferences.setInt(PreferencesKey.currentWowneroNodeIdKey, node.key as int); + break; default: break; } diff --git a/lib/utils/distribution_info.dart b/lib/utils/distribution_info.dart index 859c507a3..5a2cb8e9d 100644 --- a/lib/utils/distribution_info.dart +++ b/lib/utils/distribution_info.dart @@ -1,5 +1,5 @@ import 'dart:io'; -import 'package:package_info/package_info.dart'; +import 'package:cake_wallet/utils/package_info.dart'; enum DistributionType { googleplay, github, appstore, fdroid } diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index 5a07ab0f0..b19b1bb7e 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -5,12 +5,12 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/main.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cw_core/root_dir.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mailer/flutter_mailer.dart'; -import 'package:package_info/package_info.dart'; -import 'package:path_provider/path_provider.dart'; +import 'package:cake_wallet/utils/package_info.dart'; import 'package:shared_preferences/shared_preferences.dart'; class ExceptionHandler { @@ -20,7 +20,7 @@ class ExceptionHandler { static void _saveException(String? error, StackTrace? stackTrace, {String? library}) async { if (_file == null) { - final appDocDir = await getApplicationDocumentsDirectory(); + final appDocDir = await getAppDir(); _file = File('${appDocDir.path}/error.txt'); } @@ -53,7 +53,7 @@ class ExceptionHandler { static void _sendExceptionFile() async { try { if (_file == null) { - final appDocDir = await getApplicationDocumentsDirectory(); + final appDocDir = await getAppDir(); _file = File('${appDocDir.path}/error.txt'); } diff --git a/lib/utils/package_info.dart b/lib/utils/package_info.dart new file mode 100644 index 000000000..8b911f887 --- /dev/null +++ b/lib/utils/package_info.dart @@ -0,0 +1,54 @@ +import 'dart:io'; +import 'package:package_info/package_info.dart' as __package_info__; + +abstract class _EnvKeys { + static const kWinAppName = 'CW_WIN_APP_NAME'; + static const kWinAppPackageName = 'CW_WIN_APP_PACKAGE_NAME'; + static const kWinAppVersion = 'CW_WIN_APP_VERSION'; + static const kWinAppBuildNumber = 'CW_WIN_APP_BUILD_NUMBER'; +} + +class PackageInfo { + static Future fromPlatform() async { + if (Platform.isWindows) { + return _windowsPackageInfo; + } + + final packageInfo = await __package_info__.PackageInfo.fromPlatform(); + return PackageInfo._( + appName: packageInfo.appName, + packageName: packageInfo.packageName, + version: packageInfo.version, + buildNumber: packageInfo.buildNumber); + } + + static const _defaultCWAppName = 'Cake Wallet'; + static const _defaultCWAppPackageName = 'com.cakewallet.cake_wallet'; + static const _defaultCWAppVersion = '1.0.0'; + static const _defaultCWAppBuildNumber = '1'; + + static const _windowsPackageInfo = PackageInfo._( + appName: const String + .fromEnvironment(_EnvKeys.kWinAppName, + defaultValue: _defaultCWAppName), + packageName: const String + .fromEnvironment(_EnvKeys.kWinAppPackageName, + defaultValue: _defaultCWAppPackageName), + version: const String + .fromEnvironment(_EnvKeys.kWinAppVersion, + defaultValue: _defaultCWAppVersion), + buildNumber: const String + .fromEnvironment(_EnvKeys.kWinAppBuildNumber, + defaultValue: _defaultCWAppBuildNumber)); + + final String appName; + final String packageName; + final String version; + final String buildNumber; + + const PackageInfo._({ + required this.appName, + required this.packageName, + required this.version, + required this.buildNumber}); +} diff --git a/lib/view_model/advanced_privacy_settings_view_model.dart b/lib/view_model/advanced_privacy_settings_view_model.dart index c87e097c3..73308f15a 100644 --- a/lib/view_model/advanced_privacy_settings_view_model.dart +++ b/lib/view_model/advanced_privacy_settings_view_model.dart @@ -41,6 +41,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { case WalletType.tron: return true; case WalletType.monero: + case WalletType.wownero: case WalletType.none: case WalletType.bitcoin: case WalletType.litecoin: @@ -51,7 +52,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { } } - bool get hasSeedTypeOption => type == WalletType.monero; + bool get hasSeedTypeOption => type == WalletType.monero || type == WalletType.wownero; @computed bool get addCustomNode => _addCustomNode; diff --git a/lib/view_model/backup_view_model.dart b/lib/view_model/backup_view_model.dart index 1b9b5e4ff..bbd147e2b 100644 --- a/lib/view_model/backup_view_model.dart +++ b/lib/view_model/backup_view_model.dart @@ -4,6 +4,8 @@ import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:cake_wallet/store/secret_store.dart'; +import 'package:cw_core/root_dir.dart'; +import 'package:flutter/foundation.dart'; import 'package:mobx/mobx.dart'; import 'package:intl/intl.dart'; import 'package:cake_wallet/wallet_type_utils.dart'; @@ -72,7 +74,7 @@ abstract class BackupViewModelBase with Store { } Future saveBackupFileLocally(BackupExportFile backup) async { - final appDir = await getApplicationDocumentsDirectory(); + final appDir = await getAppDir(); final path = '${appDir.path}/${backup.name}'; final backupFile = File(path); await backupFile.writeAsBytes(backup.content); @@ -80,7 +82,7 @@ abstract class BackupViewModelBase with Store { } Future removeBackupFileLocally(BackupExportFile backup) async { - final appDir = await getApplicationDocumentsDirectory(); + final appDir = await getAppDir(); final path = '${appDir.path}/${backup.name}'; final backupFile = File(path); await backupFile.delete(); diff --git a/lib/view_model/dashboard/balance_view_model.dart b/lib/view_model/dashboard/balance_view_model.dart index 5ae532bb6..c8acb9c2c 100644 --- a/lib/view_model/dashboard/balance_view_model.dart +++ b/lib/view_model/dashboard/balance_view_model.dart @@ -125,6 +125,7 @@ abstract class BalanceViewModelBase with Store { String get availableBalanceLabel { switch (wallet.type) { case WalletType.monero: + case WalletType.wownero: case WalletType.haven: case WalletType.ethereum: case WalletType.polygon: @@ -142,6 +143,7 @@ abstract class BalanceViewModelBase with Store { String get additionalBalanceLabel { switch (wallet.type) { case WalletType.monero: + case WalletType.wownero: case WalletType.haven: case WalletType.ethereum: case WalletType.polygon: diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index b59dd1592..8f22e5be1 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -303,6 +303,7 @@ abstract class DashboardViewModelBase with Store { bool get hasRescan => wallet.type == WalletType.bitcoin || wallet.type == WalletType.monero || + wallet.type == WalletType.wownero || wallet.type == WalletType.haven; @computed @@ -532,6 +533,11 @@ abstract class DashboardViewModelBase with Store { @action void setSyncAll(bool value) => settingsStore.currentSyncAll = value; + Future> checkForHavenWallets() async { + final walletInfoSource = await CakeHive.openBox(WalletInfo.boxName); + return walletInfoSource.values.where((element) => element.type == WalletType.haven).map((e) => e.name).toList(); + } + Future> checkAffectedWallets() async { try { // await load file diff --git a/lib/view_model/dashboard/transaction_list_item.dart b/lib/view_model/dashboard/transaction_list_item.dart index fb5348a29..176b4e58d 100644 --- a/lib/view_model/dashboard/transaction_list_item.dart +++ b/lib/view_model/dashboard/transaction_list_item.dart @@ -7,6 +7,7 @@ import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/solana/solana.dart'; import 'package:cake_wallet/tron/tron.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/transaction_info.dart'; @@ -55,8 +56,14 @@ class TransactionListItem extends ActionListItem with Keyable { } String get formattedPendingStatus { - if (transaction.confirmations >= 0 && transaction.confirmations < 10) { - return ' (${transaction.confirmations}/10)'; + if (balanceViewModel.wallet.type == WalletType.monero || balanceViewModel.wallet.type == WalletType.haven) { + if (transaction.confirmations >= 0 && transaction.confirmations < 10) { + return ' (${transaction.confirmations}/10)'; + } + } else if (balanceViewModel.wallet.type == WalletType.wownero) { + if (transaction.confirmations >= 0 && transaction.confirmations < 3) { + return ' (${transaction.confirmations}/3)'; + } } return ''; } @@ -64,6 +71,7 @@ class TransactionListItem extends ActionListItem with Keyable { String get formattedStatus { if (transaction.direction == TransactionDirection.incoming) { if (balanceViewModel.wallet.type == WalletType.monero || + balanceViewModel.wallet.type == WalletType.wownero || balanceViewModel.wallet.type == WalletType.haven) { return formattedPendingStatus; } @@ -95,7 +103,7 @@ class TransactionListItem extends ActionListItem with Keyable { } catch (e) { return null; } - + return null; } @@ -108,6 +116,11 @@ class TransactionListItem extends ActionListItem with Keyable { cryptoAmount: monero!.formatterMoneroAmountToDouble(amount: transaction.amount), price: price); break; + case WalletType.wownero: + amount = calculateFiatAmountRaw( + cryptoAmount: wownero!.formatterWowneroAmountToDouble(amount: transaction.amount), + price: price); + break; case WalletType.bitcoin: case WalletType.litecoin: case WalletType.bitcoinCash: diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 5e0443bf8..b4711068c 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -287,6 +287,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with bool get isLowFee { switch (wallet.type) { case WalletType.monero: + case WalletType.wownero: case WalletType.haven: return transactionPriority == monero!.getMoneroTransactionPrioritySlow(); case WalletType.bitcoin: @@ -670,6 +671,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with depositCurrency = CryptoCurrency.nano; receiveCurrency = CryptoCurrency.xmr; break; + case WalletType.banano: + depositCurrency = CryptoCurrency.banano; + receiveCurrency = CryptoCurrency.xmr; + break; case WalletType.polygon: depositCurrency = CryptoCurrency.maticpoly; receiveCurrency = CryptoCurrency.xmr; @@ -682,7 +687,11 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with depositCurrency = CryptoCurrency.trx; receiveCurrency = CryptoCurrency.xmr; break; - default: + case WalletType.wownero: + depositCurrency = CryptoCurrency.wow; + receiveCurrency = CryptoCurrency.xmr; + break; + case WalletType.none: break; } } @@ -755,6 +764,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with switch (wallet.type) { case WalletType.monero: case WalletType.haven: + case WalletType.wownero: _settingsStore.priority[wallet.type] = monero!.getMoneroTransactionPriorityAutomatic(); break; case WalletType.bitcoin: diff --git a/lib/view_model/node_list/node_create_or_edit_view_model.dart b/lib/view_model/node_list/node_create_or_edit_view_model.dart index 000c9bdea..850e248f2 100644 --- a/lib/view_model/node_list/node_create_or_edit_view_model.dart +++ b/lib/view_model/node_list/node_create_or_edit_view_model.dart @@ -80,6 +80,7 @@ abstract class NodeCreateOrEditViewModelBase with Store { return true; case WalletType.none: case WalletType.monero: + case WalletType.wownero: case WalletType.haven: case WalletType.litecoin: case WalletType.bitcoinCash: diff --git a/lib/view_model/node_list/node_list_view_model.dart b/lib/view_model/node_list/node_list_view_model.dart index a7fe9c6ca..ea1dd574e 100644 --- a/lib/view_model/node_list/node_list_view_model.dart +++ b/lib/view_model/node_list/node_list_view_model.dart @@ -85,6 +85,9 @@ abstract class NodeListViewModelBase with Store { case WalletType.tron: node = getTronDefaultNode(nodes: _nodeSource)!; break; + case WalletType.wownero: + node = getWowneroDefaultNode(nodes: _nodeSource); + break; default: throw Exception('Unexpected wallet type: ${_appStore.wallet!.type}'); } diff --git a/lib/view_model/restore/restore_from_qr_vm.dart b/lib/view_model/restore/restore_from_qr_vm.dart index 1e9aea2c2..f5938911b 100644 --- a/lib/view_model/restore/restore_from_qr_vm.dart +++ b/lib/view_model/restore/restore_from_qr_vm.dart @@ -7,6 +7,7 @@ import 'package:cake_wallet/solana/solana.dart'; import 'package:cake_wallet/tron/tron.dart'; import 'package:cake_wallet/view_model/restore/restore_mode.dart'; import 'package:cake_wallet/view_model/restore/restore_wallet.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/monero/monero.dart'; @@ -48,7 +49,7 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store @observable String address; - bool get hasRestorationHeight => type == WalletType.monero; + bool get hasRestorationHeight => type == WalletType.monero || type == WalletType.wownero; @override WalletCredentials getCredentialsFromRestoredWallet( @@ -74,6 +75,15 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store viewKey: restoreWallet.viewKey ?? '', spendKey: restoreWallet.spendKey ?? '', height: restoreWallet.height ?? 0); + case WalletType.wownero: + return wownero!.createWowneroRestoreWalletFromKeysCredentials( + name: name, + password: password, + language: 'English', + address: restoreWallet.address ?? '', + viewKey: restoreWallet.viewKey ?? '', + spendKey: restoreWallet.spendKey ?? '', + height: restoreWallet.height ?? 0); case WalletType.bitcoin: case WalletType.litecoin: return bitcoin!.createBitcoinRestoreWalletFromWIFCredentials( @@ -137,6 +147,13 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store case WalletType.tron: return tron!.createTronRestoreWalletFromSeedCredentials( name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password); + case WalletType.wownero: + return wownero!.createWowneroRestoreWalletFromSeedCredentials( + name: name, + height: restoreWallet.height ?? 0, + mnemonic: restoreWallet.mnemonicSeed ?? '', + password: password, + ); default: throw Exception('Unexpected type: ${type.toString()}'); } diff --git a/lib/view_model/restore/wallet_restore_from_qr_code.dart b/lib/view_model/restore/wallet_restore_from_qr_code.dart index 09b5c9d96..335b1a006 100644 --- a/lib/view_model/restore/wallet_restore_from_qr_code.dart +++ b/lib/view_model/restore/wallet_restore_from_qr_code.dart @@ -36,6 +36,9 @@ class WalletRestoreFromQRCode { 'tron': WalletType.tron, 'tron-wallet': WalletType.tron, 'tron_wallet': WalletType.tron, + 'wownero': WalletType.wownero, + 'wownero-wallet': WalletType.wownero, + 'wownero_wallet': WalletType.wownero, }; static bool _containsAssetSpecifier(String code) => _extractWalletType(code) != null; @@ -57,7 +60,9 @@ class WalletRestoreFromQRCode { RegExp _getPattern(int wordCount) => RegExp(r'(?<=\W|^)((?:\w+\s+){' + (wordCount - 1).toString() + r'}\w+)(?=\W|$)'); - List patternCounts = walletType == WalletType.monero ? [25, 16, 14, 13] : [24, 18, 12]; + List patternCounts = walletType == WalletType.monero || walletType == WalletType.wownero + ? [25, 16, 14, 13] + : [24, 18, 12]; for (final count in patternCounts) { final pattern = _getPattern(count); @@ -132,7 +137,8 @@ class WalletRestoreFromQRCode { final seedValue = credentials['seed'] as String; final words = SeedValidator.getWordList(type: type, language: 'english'); - if (type == WalletType.monero && Polyseed.isValidSeed(seedValue)) { + if ((type == WalletType.monero || type == WalletType.wownero) && + Polyseed.isValidSeed(seedValue)) { return WalletRestoreMode.seed; } diff --git a/lib/view_model/send/output.dart b/lib/view_model/send/output.dart index d6f2589c1..892841a60 100644 --- a/lib/view_model/send/output.dart +++ b/lib/view_model/send/output.dart @@ -9,6 +9,7 @@ import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/solana/solana.dart'; import 'package:cake_wallet/src/screens/send/widgets/extract_address_from_parsed.dart'; import 'package:cake_wallet/tron/tron.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; @@ -100,6 +101,9 @@ abstract class OutputBase with Store { case WalletType.polygon: _amount = polygon!.formatterPolygonParseAmount(_cryptoAmount); break; + case WalletType.wownero: + _amount = wownero!.formatterWowneroParseAmount(amount: _cryptoAmount); + break; default: break; } @@ -154,6 +158,10 @@ abstract class OutputBase with Store { return monero!.formatterMoneroAmountToDouble(amount: fee); } + if (_wallet.type == WalletType.wownero) { + return wownero!.formatterWowneroAmountToDouble(amount: fee); + } + if (_wallet.type == WalletType.haven) { return haven!.formatterMoneroAmountToDouble(amount: fee); } @@ -287,6 +295,9 @@ abstract class OutputBase with Store { case WalletType.tron: maximumFractionDigits = 12; break; + case WalletType.wownero: + maximumFractionDigits = 11; + break; default: break; } diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index a00cfe0cc..a1997e81d 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -16,6 +16,7 @@ import 'package:cake_wallet/tron/tron.dart'; import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart'; import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cw_core/exceptions.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cake_wallet/view_model/send/output.dart'; @@ -244,6 +245,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin || wallet.type == WalletType.monero || + wallet.type == WalletType.wownero || wallet.type == WalletType.bitcoinCash; @computed @@ -478,6 +480,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor return monero! .createMoneroTransactionCreationCredentials(outputs: outputs, priority: priority!); + case WalletType.wownero: + return wownero! + .createWowneroTransactionCreationCredentials(outputs: outputs, priority: priority!); + case WalletType.haven: return haven!.createHavenTransactionCreationCredentials( outputs: outputs, priority: priority!, assetType: selectedCryptoCurrency.title); diff --git a/lib/view_model/settings/other_settings_view_model.dart b/lib/view_model/settings/other_settings_view_model.dart index 7e751a920..bd04755fa 100644 --- a/lib/view_model/settings/other_settings_view_model.dart +++ b/lib/view_model/settings/other_settings_view_model.dart @@ -1,8 +1,11 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; -import 'package:cake_wallet/entities/provider_types.dart'; import 'package:cake_wallet/entities/priority_for_wallet_type.dart'; +import 'package:cake_wallet/entities/provider_types.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/utils/package_info.dart'; +// import 'package:package_info/package_info.dart'; +import 'package:collection/collection.dart'; import 'package:cw_core/balance.dart'; import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/transaction_info.dart'; @@ -10,19 +13,18 @@ import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:mobx/mobx.dart'; -import 'package:package_info/package_info.dart'; -import 'package:collection/collection.dart'; part 'other_settings_view_model.g.dart'; -class OtherSettingsViewModel = OtherSettingsViewModelBase with _$OtherSettingsViewModel; +class OtherSettingsViewModel = OtherSettingsViewModelBase + with _$OtherSettingsViewModel; abstract class OtherSettingsViewModelBase with Store { OtherSettingsViewModelBase(this._settingsStore, this._wallet) : walletType = _wallet.type, currentVersion = '' { - PackageInfo.fromPlatform() - .then((PackageInfo packageInfo) => currentVersion = packageInfo.version); + PackageInfo.fromPlatform().then( + (PackageInfo packageInfo) => currentVersion = packageInfo.version); final priority = _settingsStore.priority[_wallet.type]; final priorities = priorityForWalletType(_wallet.type); @@ -33,7 +35,8 @@ abstract class OtherSettingsViewModelBase with Store { } final WalletType walletType; - final WalletBase, TransactionInfo> _wallet; + final WalletBase, + TransactionInfo> _wallet; @observable String currentVersion; @@ -61,10 +64,12 @@ abstract class OtherSettingsViewModelBase with Store { _wallet.type == WalletType.tron); @computed - bool get isEnabledBuyAction => !_settingsStore.disableBuy && _wallet.type != WalletType.haven; + bool get isEnabledBuyAction => + !_settingsStore.disableBuy && _wallet.type != WalletType.haven; @computed - bool get isEnabledSellAction => !_settingsStore.disableSell && _wallet.type != WalletType.haven; + bool get isEnabledSellAction => + !_settingsStore.disableSell && _wallet.type != WalletType.haven; List get availableBuyProvidersTypes { return ProvidersHelper.getAvailableBuyProviderTypes(walletType); @@ -74,12 +79,12 @@ abstract class OtherSettingsViewModelBase with Store { ProvidersHelper.getAvailableSellProviderTypes(walletType); ProviderType get buyProviderType => - _settingsStore.defaultBuyProviders[walletType] ?? ProviderType.askEachTime; + _settingsStore.defaultBuyProviders[walletType] ?? + ProviderType.askEachTime; ProviderType get sellProviderType => - _settingsStore.defaultSellProviders[walletType] ?? ProviderType.askEachTime; - - + _settingsStore.defaultSellProviders[walletType] ?? + ProviderType.askEachTime; String getDisplayPriority(dynamic priority) { final _priority = priority as TransactionPriority; @@ -101,7 +106,8 @@ abstract class OtherSettingsViewModelBase with Store { _wallet.type == WalletType.litecoin || _wallet.type == WalletType.bitcoinCash) { final rate = bitcoin!.getFeeRate(_wallet, _priority); - return bitcoin!.bitcoinTransactionPriorityWithLabel(_priority, rate, customRate: customValue); + return bitcoin!.bitcoinTransactionPriorityWithLabel(_priority, rate, + customRate: customValue); } return priority.toString(); @@ -124,7 +130,8 @@ abstract class OtherSettingsViewModelBase with Store { void onDisplayPrioritySelected(TransactionPriority priority) => _settingsStore.priority[walletType] = priority; - void onDisplayBitcoinPrioritySelected(TransactionPriority priority, double customValue) { + void onDisplayBitcoinPrioritySelected( + TransactionPriority priority, double customValue) { if (_wallet.type == WalletType.bitcoin) { _settingsStore.customBitcoinFeeRate = customValue.round(); } @@ -132,12 +139,13 @@ abstract class OtherSettingsViewModelBase with Store { } @computed - double get customBitcoinFeeRate => _settingsStore.customBitcoinFeeRate.toDouble(); + double get customBitcoinFeeRate => + _settingsStore.customBitcoinFeeRate.toDouble(); int? get customPriorityItemIndex { final priorities = priorityForWalletType(walletType); - final customItem = priorities - .firstWhereOrNull((element) => element == bitcoin!.getBitcoinTransactionPriorityCustom()); + final customItem = priorities.firstWhereOrNull( + (element) => element == bitcoin!.getBitcoinTransactionPriorityCustom()); return customItem != null ? priorities.indexOf(customItem) : null; } diff --git a/lib/view_model/settings/privacy_settings_view_model.dart b/lib/view_model/settings/privacy_settings_view_model.dart index 9f0ffa14c..90511af8e 100644 --- a/lib/view_model/settings/privacy_settings_view_model.dart +++ b/lib/view_model/settings/privacy_settings_view_model.dart @@ -41,6 +41,7 @@ abstract class PrivacySettingsViewModelBase with Store { bool get isAutoGenerateSubaddressesVisible => _wallet.type == WalletType.monero || + _wallet.type == WalletType.wownero || _wallet.type == WalletType.bitcoin || _wallet.type == WalletType.litecoin || _wallet.type == WalletType.bitcoinCash; diff --git a/lib/view_model/transaction_details_view_model.dart b/lib/view_model/transaction_details_view_model.dart index 526ff0335..5b7a1a8db 100644 --- a/lib/view_model/transaction_details_view_model.dart +++ b/lib/view_model/transaction_details_view_model.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/tron/tron.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/wallet_type.dart'; @@ -75,6 +76,9 @@ abstract class TransactionDetailsViewModelBase with Store { case WalletType.tron: _addTronListItems(tx, dateFormat); break; + case WalletType.wownero: + _addWowneroListItems(tx, dateFormat); + break; default: break; } @@ -166,7 +170,9 @@ abstract class TransactionDetailsViewModelBase with Store { return 'https://solscan.io/tx/${txId}'; case WalletType.tron: return 'https://tronscan.org/#/transaction/${txId}'; - default: + case WalletType.wownero: + return 'https://explore.wownero.com/tx/${txId}'; + case WalletType.none: return ''; } } @@ -194,7 +200,9 @@ abstract class TransactionDetailsViewModelBase with Store { return S.current.view_transaction_on + 'solscan.io'; case WalletType.tron: return S.current.view_transaction_on + 'tronscan.org'; - default: + case WalletType.wownero: + return S.current.view_transaction_on + 'Wownero.com'; + case WalletType.none: return ''; } } @@ -439,4 +447,44 @@ abstract class TransactionDetailsViewModelBase with Store { String get pendingTransactionFeeFiatAmountFormatted => sendViewModel.isFiatDisabled ? '' : sendViewModel.pendingTransactionFeeFiatAmount + ' ' + sendViewModel.fiat.title; + + void _addWowneroListItems(TransactionInfo tx, DateFormat dateFormat) { + final key = tx.additionalInfo['key'] as String?; + final accountIndex = tx.additionalInfo['accountIndex'] as int; + final addressIndex = tx.additionalInfo['addressIndex'] as int; + final feeFormatted = tx.feeFormatted(); + final _items = [ + StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id), + StandartListItem( + title: S.current.transaction_details_date, value: dateFormat.format(tx.date)), + StandartListItem(title: S.current.transaction_details_height, value: '${tx.height}'), + StandartListItem(title: S.current.transaction_details_amount, value: tx.amountFormatted()), + if (feeFormatted != null) + StandartListItem(title: S.current.transaction_details_fee, value: feeFormatted), + if (key?.isNotEmpty ?? false) StandartListItem(title: S.current.transaction_key, value: key!), + ]; + + if (tx.direction == TransactionDirection.incoming) { + try { + final address = wownero!.getTransactionAddress(wallet, accountIndex, addressIndex); + final label = wownero!.getSubaddressLabel(wallet, accountIndex, addressIndex); + + if (address.isNotEmpty) { + isRecipientAddressShown = true; + _items.add(StandartListItem( + title: S.current.transaction_details_recipient_address, + value: address, + )); + } + + if (label.isNotEmpty) { + _items.add(StandartListItem(title: S.current.address_label, value: label)); + } + } catch (e) { + print(e.toString()); + } + } + + items.addAll(_items); + } } diff --git a/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart b/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart index 2a4383d38..e2d8469f1 100644 --- a/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart +++ b/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart @@ -2,6 +2,7 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/utils/exception_handler.dart'; import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/unspent_transaction_output.dart'; import 'package:cw_core/wallet_base.dart'; @@ -62,6 +63,8 @@ abstract class UnspentCoinsListViewModelBase with Store { String formatAmountToString(int fullBalance) { if (wallet.type == WalletType.monero) return monero!.formatterMoneroAmountToString(amount: fullBalance); + if (wallet.type == WalletType.wownero) + return wownero!.formatterWowneroAmountToString(amount: fullBalance); if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type)) return bitcoin!.formatterBitcoinAmountToString(amount: fullBalance); return ''; @@ -71,6 +74,9 @@ abstract class UnspentCoinsListViewModelBase with Store { if (wallet.type == WalletType.monero) { await monero!.updateUnspents(wallet); } + if (wallet.type == WalletType.wownero) { + await wownero!.updateUnspents(wallet); + } if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type)) { await bitcoin!.updateUnspents(wallet); } @@ -80,6 +86,7 @@ abstract class UnspentCoinsListViewModelBase with Store { List _getUnspents() { if (wallet.type == WalletType.monero) return monero!.getUnspents(wallet); + if (wallet.type == WalletType.wownero) return wownero!.getUnspents(wallet); if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type)) return bitcoin!.getUnspents(wallet); return List.empty(); diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart index 6b59c9033..dd7f02407 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart @@ -17,6 +17,7 @@ import 'package:cake_wallet/utils/list_item.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cw_core/amount_converter.dart'; import 'package:cw_core/currency.dart'; import 'package:cw_core/wallet_type.dart'; @@ -191,6 +192,22 @@ class TronURI extends PaymentURI { } } +class WowneroURI extends PaymentURI { + WowneroURI({required String amount, required String address}) + : super(amount: amount, address: address); + + @override + String toString() { + var base = 'wownero:' + address; + + if (amount.isNotEmpty) { + base += '?tx_amount=${amount.replaceAll(',', '.')}'; + } + + return base; + } +} + abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store { WalletAddressListViewModelBase({ required AppStore appStore, @@ -293,6 +310,10 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo return TronURI(amount: amount, address: address.address); } + if (wallet.type == WalletType.wownero) { + return WowneroURI(amount: amount, address: address.address); + } + throw Exception('Unexpected type: ${type.toString()}'); } @@ -409,6 +430,20 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress)); } + if (wallet.type == WalletType.wownero) { + final primaryAddress = wownero!.getSubaddressList(wallet).subaddresses.first; + final addressItems = wownero!.getSubaddressList(wallet).subaddresses.map((subaddress) { + final isPrimary = subaddress == primaryAddress; + + return WalletAddressListItem( + id: subaddress.id, + isPrimary: isPrimary, + name: subaddress.label, + address: subaddress.address); + }); + addressList.addAll(addressItems); + } + if (searchText.isNotEmpty) { return ObservableList.of(addressList.where((item) { if (item is WalletAddressListItem) { diff --git a/lib/view_model/wallet_creation_vm.dart b/lib/view_model/wallet_creation_vm.dart index 841a88e7e..36661ac7e 100644 --- a/lib/view_model/wallet_creation_vm.dart +++ b/lib/view_model/wallet_creation_vm.dart @@ -13,6 +13,7 @@ import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/entities/generate_name.dart'; +import 'package:polyseed/polyseed.dart'; part 'wallet_creation_vm.g.dart'; @@ -42,6 +43,10 @@ abstract class WalletCreationVMBase with Store { final Box _walletInfoSource; final AppStore _appStore; + bool isPolyseed(String seed) => + (type == WalletType.monero || type == WalletType.wownero) && + (Polyseed.isValidSeed(seed) || (seed.split(" ").length == 14)); + bool nameExists(String name) => walletCreationService.exists(name); bool typeExists(WalletType type) => walletCreationService.typeExists(type); @@ -86,7 +91,9 @@ abstract class WalletCreationVMBase with Store { getIt.get().registerSyncTask(); _appStore.authenticationStore.allowed(); state = ExecutedSuccessfullyState(); - } catch (e) { + } catch (e, s) { + print("@@@@@@@@"); + print(s); state = FailureState(e.toString()); } } diff --git a/lib/view_model/wallet_keys_view_model.dart b/lib/view_model/wallet_keys_view_model.dart index 060770273..5102ba8eb 100644 --- a/lib/view_model/wallet_keys_view_model.dart +++ b/lib/view_model/wallet_keys_view_model.dart @@ -4,11 +4,13 @@ import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart'; import 'package:cake_wallet/store/app_store.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_type.dart'; -import 'package:cw_monero/api/wallet.dart' as monero_wallet; +import 'package:cw_monero/monero_wallet.dart'; +import 'package:cw_wownero/wownero_wallet.dart'; import 'package:mobx/mobx.dart'; import 'package:polyseed/polyseed.dart'; @@ -32,13 +34,17 @@ abstract class WalletKeysViewModelBase with Store { _populateItems(); }); - if (_appStore.wallet!.type == WalletType.monero || _appStore.wallet!.type == WalletType.haven) { + if (_appStore.wallet!.type == WalletType.monero || + _appStore.wallet!.type == WalletType.haven || + _appStore.wallet!.type == WalletType.wownero) { final accountTransactions = _getWalletTransactions(_appStore.wallet!); if (accountTransactions.isNotEmpty) { - final incomingAccountTransactions = - accountTransactions.where((tx) => tx.direction == TransactionDirection.incoming); + final incomingAccountTransactions = accountTransactions + .where((tx) => tx.direction == TransactionDirection.incoming); if (incomingAccountTransactions.isNotEmpty) { - incomingAccountTransactions.toList().sort((a, b) => a.date.compareTo(b.date)); + incomingAccountTransactions + .toList() + .sort((a, b) => a.date.compareTo(b.date)); _restoreHeightByTransactions = _getRestoreHeightByTransactions( _appStore.wallet!.type, incomingAccountTransactions.first.date); } @@ -64,29 +70,38 @@ abstract class WalletKeysViewModelBase with Store { items.addAll([ if (keys['publicSpendKey'] != null) - StandartListItem(title: S.current.spend_key_public, value: keys['publicSpendKey']!), + StandartListItem( + title: S.current.spend_key_public, + value: keys['publicSpendKey']!), if (keys['privateSpendKey'] != null) - StandartListItem(title: S.current.spend_key_private, value: keys['privateSpendKey']!), + StandartListItem( + title: S.current.spend_key_private, + value: keys['privateSpendKey']!), if (keys['publicViewKey'] != null) - StandartListItem(title: S.current.view_key_public, value: keys['publicViewKey']!), + StandartListItem( + title: S.current.view_key_public, value: keys['publicViewKey']!), if (keys['privateViewKey'] != null) - StandartListItem(title: S.current.view_key_private, value: keys['privateViewKey']!), - StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!), + StandartListItem( + title: S.current.view_key_private, + value: keys['privateViewKey']!), + StandartListItem( + title: S.current.wallet_seed, value: _appStore.wallet!.seed!), ]); - if (_appStore.wallet?.seed != null && Polyseed.isValidSeed(_appStore.wallet!.seed!)) { + if (_appStore.wallet?.seed != null && + Polyseed.isValidSeed(_appStore.wallet!.seed!)) { final lang = PolyseedLang.getByPhrase(_appStore.wallet!.seed!); - final legacyLang = _getLegacySeedLang(lang); - final legacySeed = - Polyseed.decode(_appStore.wallet!.seed!, lang, PolyseedCoin.POLYSEED_MONERO) - .toLegacySeed(legacyLang); - items.add(StandartListItem(title: S.current.wallet_seed_legacy, value: legacySeed)); + items.add(StandartListItem( + title: S.current.wallet_seed_legacy, + value: (_appStore.wallet as MoneroWalletBase) + .seedLegacy(lang.nameEnglish))); } final restoreHeight = monero!.getRestoreHeight(_appStore.wallet!); if (restoreHeight != null) { items.add(StandartListItem( - title: S.current.wallet_recovery_height, value: restoreHeight.toString())); + title: S.current.wallet_recovery_height, + value: restoreHeight.toString())); } } @@ -95,17 +110,58 @@ abstract class WalletKeysViewModelBase with Store { items.addAll([ if (keys['publicSpendKey'] != null) - StandartListItem(title: S.current.spend_key_public, value: keys['publicSpendKey']!), + StandartListItem( + title: S.current.spend_key_public, + value: keys['publicSpendKey']!), if (keys['privateSpendKey'] != null) - StandartListItem(title: S.current.spend_key_private, value: keys['privateSpendKey']!), + StandartListItem( + title: S.current.spend_key_private, + value: keys['privateSpendKey']!), if (keys['publicViewKey'] != null) - StandartListItem(title: S.current.view_key_public, value: keys['publicViewKey']!), + StandartListItem( + title: S.current.view_key_public, value: keys['publicViewKey']!), if (keys['privateViewKey'] != null) - StandartListItem(title: S.current.view_key_private, value: keys['privateViewKey']!), - StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!), + StandartListItem( + title: S.current.view_key_private, + value: keys['privateViewKey']!), + StandartListItem( + title: S.current.wallet_seed, value: _appStore.wallet!.seed!), ]); } + if (_appStore.wallet!.type == WalletType.wownero) { + final keys = wownero!.getKeys(_appStore.wallet!); + + items.addAll([ + if (keys['publicSpendKey'] != null) + StandartListItem( + title: S.current.spend_key_public, + value: keys['publicSpendKey']!), + if (keys['privateSpendKey'] != null) + StandartListItem( + title: S.current.spend_key_private, + value: keys['privateSpendKey']!), + if (keys['publicViewKey'] != null) + StandartListItem( + title: S.current.view_key_public, value: keys['publicViewKey']!), + if (keys['privateViewKey'] != null) + StandartListItem( + title: S.current.view_key_private, + value: keys['privateViewKey']!), + StandartListItem( + title: S.current.wallet_seed, value: _appStore.wallet!.seed!), + ]); + + if (_appStore.wallet?.seed != null && + Polyseed.isValidSeed(_appStore.wallet!.seed!)) { + final lang = PolyseedLang.getByPhrase(_appStore.wallet!.seed!); + items.add(StandartListItem( + title: S.current.wallet_seed_legacy, + value: (_appStore.wallet as WowneroWalletBase) + .seedLegacy(lang.nameEnglish))); + } + } + if (_appStore.wallet!.type == WalletType.bitcoin || _appStore.wallet!.type == WalletType.litecoin || _appStore.wallet!.type == WalletType.bitcoinCash) { @@ -118,7 +174,8 @@ abstract class WalletKeysViewModelBase with Store { // StandartListItem(title: S.current.private_key, value: keys['privateKey']!), // if (keys['publicKey'] != null) // StandartListItem(title: S.current.public_key, value: keys['publicKey']!), - StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!), + StandartListItem( + title: S.current.wallet_seed, value: _appStore.wallet!.seed!), ]); } @@ -127,24 +184,32 @@ abstract class WalletKeysViewModelBase with Store { _appStore.wallet!.type == WalletType.tron) { items.addAll([ if (_appStore.wallet!.privateKey != null) - StandartListItem(title: S.current.private_key, value: _appStore.wallet!.privateKey!), + StandartListItem( + title: S.current.private_key, + value: _appStore.wallet!.privateKey!), if (_appStore.wallet!.seed != null) - StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!), + StandartListItem( + title: S.current.wallet_seed, value: _appStore.wallet!.seed!), ]); } - bool nanoBased = - _appStore.wallet!.type == WalletType.nano || _appStore.wallet!.type == WalletType.banano; + bool nanoBased = _appStore.wallet!.type == WalletType.nano || + _appStore.wallet!.type == WalletType.banano; if (nanoBased) { // we always have the hex version of the seed and private key: items.addAll([ if (_appStore.wallet!.seed != null) - StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!), + StandartListItem( + title: S.current.wallet_seed, value: _appStore.wallet!.seed!), if (_appStore.wallet!.hexSeed != null) - StandartListItem(title: S.current.seed_hex_form, value: _appStore.wallet!.hexSeed!), + StandartListItem( + title: S.current.seed_hex_form, + value: _appStore.wallet!.hexSeed!), if (_appStore.wallet!.privateKey != null) - StandartListItem(title: S.current.private_key, value: _appStore.wallet!.privateKey!), + StandartListItem( + title: S.current.private_key, + value: _appStore.wallet!.privateKey!), ]); } } @@ -154,7 +219,10 @@ abstract class WalletKeysViewModelBase with Store { return await haven!.getCurrentHeight(); } if (_appStore.wallet!.type == WalletType.monero) { - return monero_wallet.getCurrentHeight(); + return await monero!.getCurrentHeight(); + } + if (_appStore.wallet!.type == WalletType.wownero) { + return await wownero!.getCurrentHeight(); } return null; } @@ -183,8 +251,10 @@ abstract class WalletKeysViewModelBase with Store { return 'solana-wallet'; case WalletType.tron: return 'tron-wallet'; + case WalletType.wownero: + return 'wownero-wallet'; default: - throw Exception('Unexpected wallet type: ${_appStore.wallet!.toString()}'); + throw Exception('Unexpected wallet type: ${_appStore.wallet!.type.toString()}'); } } @@ -205,7 +275,8 @@ abstract class WalletKeysViewModelBase with Store { if (_appStore.wallet!.seed != null) 'seed': _appStore.wallet!.seed!, if (_appStore.wallet!.seed == null && _appStore.wallet!.hexSeed != null) 'hexSeed': _appStore.wallet!.hexSeed!, - if (_appStore.wallet!.seed == null && _appStore.wallet!.privateKey != null) + if (_appStore.wallet!.seed == null && + _appStore.wallet!.privateKey != null) 'private_key': _appStore.wallet!.privateKey!, if (restoreHeightResult != null) ...{'height': restoreHeightResult} }; @@ -221,6 +292,12 @@ abstract class WalletKeysViewModelBase with Store { return monero!.getTransactionHistory(wallet).transactions.values.toList(); } else if (wallet.type == WalletType.haven) { return haven!.getTransactionHistory(wallet).transactions.values.toList(); + } else if (wallet.type == WalletType.wownero) { + return wownero! + .getTransactionHistory(wallet) + .transactions + .values + .toList(); } return []; } @@ -230,11 +307,14 @@ abstract class WalletKeysViewModelBase with Store { return monero!.getHeightByDate(date: date); } else if (type == WalletType.haven) { return haven!.getHeightByDate(date: date); + } else if (type == WalletType.wownero) { + return wownero!.getHeightByDate(date: date); } return 0; } - String getRoundedRestoreHeight(int height) => ((height / 1000).floor() * 1000).toString(); + String getRoundedRestoreHeight(int height) => + ((height / 1000).floor() * 1000).toString(); LegacySeedLang _getLegacySeedLang(PolyseedLang lang) { switch (lang.nameEnglish) { diff --git a/lib/view_model/wallet_new_vm.dart b/lib/view_model/wallet_new_vm.dart index e19efabc5..4729a38b2 100644 --- a/lib/view_model/wallet_new_vm.dart +++ b/lib/view_model/wallet_new_vm.dart @@ -2,6 +2,7 @@ import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; import 'package:cake_wallet/solana/solana.dart'; import 'package:cake_wallet/tron/tron.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/monero/monero.dart'; @@ -35,11 +36,13 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store { @observable String selectedMnemonicLanguage; - bool get hasLanguageSelector => type == WalletType.monero || type == WalletType.haven; + bool get hasLanguageSelector => + type == WalletType.monero || type == WalletType.haven || type == WalletType.wownero; int get seedPhraseWordsLength { switch (type) { case WalletType.monero: + case WalletType.wownero: if (advancedPrivacySettingsViewModel.isPolySeed) { return 16; } @@ -55,7 +58,7 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store { } } - bool get hasSeedType => type == WalletType.monero; + bool get hasSeedType => type == WalletType.monero || type == WalletType.wownero; @override WalletCredentials getCredentials(dynamic _options) { @@ -76,6 +79,7 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store { case WalletType.bitcoinCash: return bitcoinCash!.createBitcoinCashNewWalletCredentials(name: name); case WalletType.nano: + case WalletType.banano: return nano!.createNanoNewWalletCredentials(name: name); case WalletType.polygon: return polygon!.createPolygonNewWalletCredentials(name: name); @@ -83,7 +87,10 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store { return solana!.createSolanaNewWalletCredentials(name: name); case WalletType.tron: return tron!.createTronNewWalletCredentials(name: name); - default: + case WalletType.wownero: + return wownero!.createWowneroNewWalletCredentials( + name: name, language: options!.first as String, isPolyseed: options.last as bool); + case WalletType.none: throw Exception('Unexpected type: ${type.toString()}'); } } diff --git a/lib/view_model/wallet_restore_view_model.dart b/lib/view_model/wallet_restore_view_model.dart index e19a83bc3..25a555b44 100644 --- a/lib/view_model/wallet_restore_view_model.dart +++ b/lib/view_model/wallet_restore_view_model.dart @@ -2,7 +2,7 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; -import 'package:cw_core/nano_account_info_response.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/solana/solana.dart'; @@ -29,8 +29,10 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { WalletRestoreViewModelBase(AppStore appStore, WalletCreationService walletCreationService, Box walletInfoSource, {required WalletType type}) - : hasSeedLanguageSelector = type == WalletType.monero || type == WalletType.haven, - hasBlockchainHeightLanguageSelector = type == WalletType.monero || type == WalletType.haven, + : hasSeedLanguageSelector = + type == WalletType.monero || type == WalletType.haven || type == WalletType.wownero, + hasBlockchainHeightLanguageSelector = + type == WalletType.monero || type == WalletType.haven || type == WalletType.wownero, hasRestoreFromPrivateKey = type == WalletType.ethereum || type == WalletType.polygon || type == WalletType.nano || @@ -42,18 +44,22 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true) { switch (type) { case WalletType.monero: - case WalletType.haven: - case WalletType.ethereum: - case WalletType.polygon: availableModes = WalletRestoreMode.values; break; case WalletType.nano: case WalletType.banano: case WalletType.solana: case WalletType.tron: + case WalletType.wownero: + case WalletType.haven: + case WalletType.ethereum: + case WalletType.polygon: availableModes = [WalletRestoreMode.seed, WalletRestoreMode.keys]; break; - default: + case WalletType.bitcoin: + case WalletType.litecoin: + case WalletType.bitcoinCash: + case WalletType.none: availableModes = [WalletRestoreMode.seed]; break; } @@ -112,6 +118,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { return bitcoinCash!.createBitcoinCashRestoreWalletFromSeedCredentials( name: name, mnemonic: seed, password: password); case WalletType.nano: + case WalletType.banano: return nano!.createNanoRestoreWalletFromSeedCredentials( name: name, mnemonic: seed, @@ -136,7 +143,14 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { mnemonic: seed, password: password, ); - default: + case WalletType.wownero: + return wownero!.createWowneroRestoreWalletFromSeedCredentials( + name: name, + mnemonic: seed, + password: password, + height: height, + ); + case WalletType.none: break; } } @@ -200,6 +214,16 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { password: password, privateKey: options['private_key'] as String, ); + case WalletType.wownero: + return wownero!.createWowneroRestoreWalletFromKeysCredentials( + name: name, + height: height, + spendKey: spendKey!, + viewKey: viewKey!, + address: address!, + password: password, + language: 'English', + ); default: break; } diff --git a/lib/wallet_type_utils.dart b/lib/wallet_type_utils.dart index 5ed78dc64..459ca992b 100644 --- a/lib/wallet_type_utils.dart +++ b/lib/wallet_type_utils.dart @@ -16,6 +16,10 @@ bool get isSingleCoin { return availableWalletTypes.length == 1; } +bool get hasMonero { + return availableWalletTypes.contains(WalletType.monero); +} + String get approximatedAppName { if (isMoneroOnly) { return 'Monero.com'; @@ -26,4 +30,4 @@ String get approximatedAppName { } return 'Cake Wallet'; -} \ No newline at end of file +} diff --git a/lib/wownero/cw_wownero.dart b/lib/wownero/cw_wownero.dart new file mode 100644 index 000000000..eccb0f126 --- /dev/null +++ b/lib/wownero/cw_wownero.dart @@ -0,0 +1,347 @@ +part of 'wownero.dart'; + +class CWWowneroAccountList extends WowneroAccountList { + CWWowneroAccountList(this._wallet); + + final Object _wallet; + + @override + @computed + ObservableList get accounts { + final wowneroWallet = _wallet as WowneroWallet; + final accounts = wowneroWallet.walletAddresses.accountList.accounts + .map((acc) => Account(id: acc.id, label: acc.label, balance: acc.balance)) + .toList(); + return ObservableList.of(accounts); + } + + @override + void update(Object wallet) { + final wowneroWallet = wallet as WowneroWallet; + wowneroWallet.walletAddresses.accountList.update(); + } + + @override + void refresh(Object wallet) { + final wowneroWallet = wallet as WowneroWallet; + wowneroWallet.walletAddresses.accountList.refresh(); + } + + @override + List getAll(Object wallet) { + final wowneroWallet = wallet as WowneroWallet; + return wowneroWallet.walletAddresses.accountList + .getAll() + .map((acc) => Account(id: acc.id, label: acc.label, balance: acc.balance)) + .toList(); + } + + @override + Future addAccount(Object wallet, {required String label}) async { + final wowneroWallet = wallet as WowneroWallet; + await wowneroWallet.walletAddresses.accountList.addAccount(label: label); + } + + @override + Future setLabelAccount(Object wallet, + {required int accountIndex, required String label}) async { + final wowneroWallet = wallet as WowneroWallet; + await wowneroWallet.walletAddresses.accountList + .setLabelAccount(accountIndex: accountIndex, label: label); + } +} + +class CWWowneroSubaddressList extends WowneroSubaddressList { + CWWowneroSubaddressList(this._wallet); + + final Object _wallet; + + @override + @computed + ObservableList get subaddresses { + final wowneroWallet = _wallet as WowneroWallet; + final subAddresses = wowneroWallet.walletAddresses.subaddressList.subaddresses + .map((sub) => Subaddress(id: sub.id, address: sub.address, label: sub.label)) + .toList(); + return ObservableList.of(subAddresses); + } + + @override + void update(Object wallet, {required int accountIndex}) { + final wowneroWallet = wallet as WowneroWallet; + wowneroWallet.walletAddresses.subaddressList.update(accountIndex: accountIndex); + } + + @override + void refresh(Object wallet, {required int accountIndex}) { + final wowneroWallet = wallet as WowneroWallet; + wowneroWallet.walletAddresses.subaddressList.refresh(accountIndex: accountIndex); + } + + @override + List getAll(Object wallet) { + final wowneroWallet = wallet as WowneroWallet; + return wowneroWallet.walletAddresses.subaddressList + .getAll() + .map((sub) => Subaddress(id: sub.id, label: sub.label, address: sub.address)) + .toList(); + } + + @override + Future addSubaddress(Object wallet, + {required int accountIndex, required String label}) async { + final wowneroWallet = wallet as WowneroWallet; + await wowneroWallet.walletAddresses.subaddressList + .addSubaddress(accountIndex: accountIndex, label: label); + } + + @override + Future setLabelSubaddress(Object wallet, + {required int accountIndex, required int addressIndex, required String label}) async { + final wowneroWallet = wallet as WowneroWallet; + await wowneroWallet.walletAddresses.subaddressList + .setLabelSubaddress(accountIndex: accountIndex, addressIndex: addressIndex, label: label); + } +} + +class CWWowneroWalletDetails extends WowneroWalletDetails { + CWWowneroWalletDetails(this._wallet); + + final Object _wallet; + + @computed + @override + Account get account { + final wowneroWallet = _wallet as WowneroWallet; + final acc = wowneroWallet.walletAddresses.account; + return Account(id: acc!.id, label: acc.label, balance: acc.balance); + } + + @computed + @override + WowneroBalance get balance { + throw Exception('Unimplemented'); + // return WowneroBalance(); + //return WowneroBalance( + // fullBalance: balance.fullBalance, + // unlockedBalance: balance.unlockedBalance); + } +} + +class CWWownero extends Wownero { + @override + WowneroAccountList getAccountList(Object wallet) => CWWowneroAccountList(wallet); + + @override + WowneroSubaddressList getSubaddressList(Object wallet) => CWWowneroSubaddressList(wallet); + + @override + TransactionHistoryBase getTransactionHistory(Object wallet) { + final wowneroWallet = wallet as WowneroWallet; + return wowneroWallet.transactionHistory; + } + + @override + WowneroWalletDetails getWowneroWalletDetails(Object wallet) => CWWowneroWalletDetails(wallet); + + @override + int getHeightByDate({required DateTime date}) => getWowneroHeightByDate(date: date); + + @override + TransactionPriority getDefaultTransactionPriority() => MoneroTransactionPriority.automatic; + + @override + TransactionPriority getWowneroTransactionPrioritySlow() => MoneroTransactionPriority.slow; + + @override + TransactionPriority getWowneroTransactionPriorityAutomatic() => + MoneroTransactionPriority.automatic; + + @override + TransactionPriority deserializeWowneroTransactionPriority({required int raw}) => + MoneroTransactionPriority.deserialize(raw: raw); + + @override + List getTransactionPriorities() => MoneroTransactionPriority.all; + + @override + List getWowneroWordList(String language) { + if (language.startsWith("POLYSEED_")) { + final lang = language.replaceAll("POLYSEED_", ""); + return PolyseedLang.getByEnglishName(lang).words; + } + if (language.startsWith("WOWSEED_")) { + final lang = language.replaceAll("WOWSEED_", ""); + return PolyseedLang.getByEnglishName(lang).words; + } + switch (language.toLowerCase()) { + case 'english': + return EnglishMnemonics.words; + case 'chinese (simplified)': + return ChineseSimplifiedMnemonics.words; + case 'dutch': + return DutchMnemonics.words; + case 'german': + return GermanMnemonics.words; + case 'japanese': + return JapaneseMnemonics.words; + case 'portuguese': + return PortugueseMnemonics.words; + case 'russian': + return RussianMnemonics.words; + case 'spanish': + return SpanishMnemonics.words; + case 'french': + return FrenchMnemonics.words; + case 'italian': + return ItalianMnemonics.words; + default: + return EnglishMnemonics.words; + } + } + + @override + WalletCredentials createWowneroRestoreWalletFromKeysCredentials( + {required String name, + required String spendKey, + required String viewKey, + required String address, + required String password, + required String language, + required int height}) => + WowneroRestoreWalletFromKeysCredentials( + name: name, + spendKey: spendKey, + viewKey: viewKey, + address: address, + password: password, + language: language, + height: height); + + @override + WalletCredentials createWowneroRestoreWalletFromSeedCredentials( + {required String name, + required String password, + required int height, + required String mnemonic}) => + WowneroRestoreWalletFromSeedCredentials( + name: name, password: password, height: height, mnemonic: mnemonic); + + @override + WalletCredentials createWowneroNewWalletCredentials( + {required String name, + required String language, + required bool isPolyseed, + String? password}) => + WowneroNewWalletCredentials( + name: name, password: password, language: language, isPolyseed: isPolyseed); + + @override + Map getKeys(Object wallet) { + final wowneroWallet = wallet as WowneroWallet; + final keys = wowneroWallet.keys; + return { + 'privateSpendKey': keys.privateSpendKey, + 'privateViewKey': keys.privateViewKey, + 'publicSpendKey': keys.publicSpendKey, + 'publicViewKey': keys.publicViewKey + }; + } + + @override + Object createWowneroTransactionCreationCredentials( + {required List outputs, required TransactionPriority priority}) => + WowneroTransactionCreationCredentials( + outputs: outputs + .map((out) => OutputInfo( + fiatAmount: out.fiatAmount, + cryptoAmount: out.cryptoAmount, + address: out.address, + note: out.note, + sendAll: out.sendAll, + extractedAddress: out.extractedAddress, + isParsedAddress: out.isParsedAddress, + formattedCryptoAmount: out.formattedCryptoAmount)) + .toList(), + priority: priority as MoneroTransactionPriority); + + @override + Object createWowneroTransactionCreationCredentialsRaw( + {required List outputs, required TransactionPriority priority}) => + WowneroTransactionCreationCredentials( + outputs: outputs, priority: priority as MoneroTransactionPriority); + + @override + String formatterWowneroAmountToString({required int amount}) => + wowneroAmountToString(amount: amount); + + @override + double formatterWowneroAmountToDouble({required int amount}) => + wowneroAmountToDouble(amount: amount); + + @override + int formatterWowneroParseAmount({required String amount}) => wowneroParseAmount(amount: amount); + + @override + Account getCurrentAccount(Object wallet) { + final wowneroWallet = wallet as WowneroWallet; + final acc = wowneroWallet.walletAddresses.account; + return Account(id: acc!.id, label: acc.label, balance: acc.balance); + } + + @override + void setCurrentAccount(Object wallet, int id, String label, String? balance) { + final wowneroWallet = wallet as WowneroWallet; + wowneroWallet.walletAddresses.account = + wownero_account.Account(id: id, label: label, balance: balance); + } + + @override + void onStartup() => wownero_wallet_api.onStartup(); + + @override + int getTransactionInfoAccountId(TransactionInfo tx) { + final wowneroTransactionInfo = tx as WowneroTransactionInfo; + return wowneroTransactionInfo.accountIndex; + } + + @override + WalletService createWowneroWalletService( + Box walletInfoSource, Box unspentCoinSource) => + WowneroWalletService(walletInfoSource, unspentCoinSource); + + @override + String getTransactionAddress(Object wallet, int accountIndex, int addressIndex) { + final wowneroWallet = wallet as WowneroWallet; + return wowneroWallet.getTransactionAddress(accountIndex, addressIndex); + } + + @override + String getSubaddressLabel(Object wallet, int accountIndex, int addressIndex) { + final wowneroWallet = wallet as WowneroWallet; + return wowneroWallet.getSubaddressLabel(accountIndex, addressIndex); + } + + @override + Map pendingTransactionInfo(Object transaction) { + final ptx = transaction as PendingWowneroTransaction; + return {'id': ptx.id, 'hex': ptx.hex, 'key': ptx.txKey}; + } + + @override + List getUnspents(Object wallet) { + final wowneroWallet = wallet as WowneroWallet; + return wowneroWallet.unspentCoins; + } + + @override + Future updateUnspents(Object wallet) async { + final wowneroWallet = wallet as WowneroWallet; + await wowneroWallet.updateUnspent(); + } + + @override + Future getCurrentHeight() async { + return wownero_wallet_api.getCurrentHeight(); + } +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 51f61d9e3..7ef453eb1 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,12 +6,10 @@ import FlutterMacOS import Foundation import connectivity_plus -import cw_monero import device_info_plus import devicelocale import flutter_inappwebview_macos import flutter_local_authentication -import flutter_secure_storage_macos import in_app_review import package_info import package_info_plus @@ -23,12 +21,10 @@ import wakelock_plus func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) - CwMoneroPlugin.register(with: registry.registrar(forPlugin: "CwMoneroPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin")) InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) FlutterLocalAuthenticationPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalAuthenticationPlugin")) - FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin")) FLTPackageInfoPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) diff --git a/macos/Podfile.lock b/macos/Podfile.lock index b71468adc..c2f37a3f3 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -2,23 +2,6 @@ PODS: - connectivity_plus (0.0.1): - FlutterMacOS - ReachabilitySwift - - cw_monero (0.0.1): - - cw_monero/Boost (= 0.0.1) - - cw_monero/Monero (= 0.0.1) - - cw_monero/OpenSSL (= 0.0.1) - - cw_monero/Sodium (= 0.0.1) - - cw_monero/Unbound (= 0.0.1) - - FlutterMacOS - - cw_monero/Boost (0.0.1): - - FlutterMacOS - - cw_monero/Monero (0.0.1): - - FlutterMacOS - - cw_monero/OpenSSL (0.0.1): - - FlutterMacOS - - cw_monero/Sodium (0.0.1): - - FlutterMacOS - - cw_monero/Unbound (0.0.1): - - FlutterMacOS - device_info_plus (0.0.1): - FlutterMacOS - devicelocale (0.0.1): @@ -56,7 +39,6 @@ PODS: DEPENDENCIES: - connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`) - - cw_monero (from `Flutter/ephemeral/.symlinks/plugins/cw_monero/macos`) - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - devicelocale (from `Flutter/ephemeral/.symlinks/plugins/devicelocale/macos`) - flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`) @@ -81,8 +63,6 @@ SPEC REPOS: EXTERNAL SOURCES: connectivity_plus: :path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos - cw_monero: - :path: Flutter/ephemeral/.symlinks/plugins/cw_monero/macos device_info_plus: :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos devicelocale: @@ -116,7 +96,6 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747 - cw_monero: ec03de55a19c4a2b174ea687e0f4202edc716fa4 device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225 flutter_inappwebview_macos: 9600c9df9fdb346aaa8933812009f8d94304203d diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index d14d203c6..0dccffc4f 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -26,8 +26,10 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - 4171CB1F5A4EA2E4DC33F52F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B38D1DBC56DBD386923BC063 /* Pods_Runner.framework */; }; + 83FF6BF911B29965D3589BE7 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD06F8F6E3797AC031038136 /* Pods_Runner.framework */; }; 9F565D5929954F53009A75FB /* secRandom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F565D5729954F53009A75FB /* secRandom.swift */; }; + CE75FC4A2C147EBA00CCC46E /* wownero_libwallet2_api_c.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE75FC492C147EBA00CCC46E /* wownero_libwallet2_api_c.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + CED5DBE42BE59BBF0065028F /* monero_libwallet2_api_c.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = CED5DBE32BE59BBF0065028F /* monero_libwallet2_api_c.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -51,12 +53,22 @@ name = "Bundle Framework"; runOnlyForDeploymentPostprocessing = 0; }; + CED5DBE02BE59B230065028F /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + CE75FC4A2C147EBA00CCC46E /* wownero_libwallet2_api_c.dylib in CopyFiles */, + CED5DBE42BE59BBF0065028F /* monero_libwallet2_api_c.dylib in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 094BF982245FD1012D60A103 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 0C090639294D3AAC00954DC9 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; }; - 2A820A13B0719E9E0CD6686F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 3294B60E86F47055290F9DFC /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; 33CC10ED2044A3C60003C045 /* Cake Wallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Cake Wallet.app"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -72,10 +84,14 @@ 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 9646C67C7114830A5ACFF5DF /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 81434A9891ECC211D4A12B7B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + 981E08F6A19E64F1F4D21DB8 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 9F1E7BFB2BF2D27500C28C9A /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 9F565D5729954F53009A75FB /* secRandom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = secRandom.swift; path = CakeWallet/secRandom.swift; sourceTree = ""; }; - B38D1DBC56DBD386923BC063 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BD06F8F6E3797AC031038136 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CE75FC492C147EBA00CCC46E /* wownero_libwallet2_api_c.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = wownero_libwallet2_api_c.dylib; sourceTree = ""; }; + CED5DBE32BE59BBF0065028F /* monero_libwallet2_api_c.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = monero_libwallet2_api_c.dylib; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -83,7 +99,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4171CB1F5A4EA2E4DC33F52F /* Pods_Runner.framework in Frameworks */, + 83FF6BF911B29965D3589BE7 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -104,6 +120,8 @@ 33CC10E42044A3C60003C045 = { isa = PBXGroup; children = ( + CE75FC492C147EBA00CCC46E /* wownero_libwallet2_api_c.dylib */, + CED5DBE32BE59BBF0065028F /* monero_libwallet2_api_c.dylib */, 9F565D5729954F53009A75FB /* secRandom.swift */, 33FAB671232836740065AC1E /* Runner */, 33CEB47122A05771004F2AC0 /* Flutter */, @@ -146,6 +164,7 @@ 33FAB671232836740065AC1E /* Runner */ = { isa = PBXGroup; children = ( + 9F1E7BFB2BF2D27500C28C9A /* Runner.entitlements */, 33CC10F02044A3C60003C045 /* AppDelegate.swift */, 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, 33E51913231747F40026EE4D /* DebugProfile.entitlements */, @@ -159,9 +178,9 @@ 9B6E7CA3983216A9E173F00F /* Pods */ = { isa = PBXGroup; children = ( - 9646C67C7114830A5ACFF5DF /* Pods-Runner.debug.xcconfig */, - 2A820A13B0719E9E0CD6686F /* Pods-Runner.release.xcconfig */, - 094BF982245FD1012D60A103 /* Pods-Runner.profile.xcconfig */, + 81434A9891ECC211D4A12B7B /* Pods-Runner.debug.xcconfig */, + 3294B60E86F47055290F9DFC /* Pods-Runner.release.xcconfig */, + 981E08F6A19E64F1F4D21DB8 /* Pods-Runner.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -170,7 +189,7 @@ isa = PBXGroup; children = ( 0C090639294D3AAC00954DC9 /* libiconv.tbd */, - B38D1DBC56DBD386923BC063 /* Pods_Runner.framework */, + BD06F8F6E3797AC031038136 /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; @@ -182,13 +201,14 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 93B711AB4B96E7C8C5C5B844 /* [CP] Check Pods Manifest.lock */, + 88D0DCAF0B588F71DC405272 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - 5592D00118C2EA3C5E0B5FDF /* [CP] Embed Pods Frameworks */, + CED5DBE02BE59B230065028F /* CopyFiles */, + 283A25C672FDD605ACF2FDFC /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -257,6 +277,23 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 283A25C672FDD605ACF2FDFC /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -295,24 +332,7 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; - 5592D00118C2EA3C5E0B5FDF /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 93B711AB4B96E7C8C5C5B844 /* [CP] Check Pods Manifest.lock */ = { + 88D0DCAF0B588F71DC405272 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -424,8 +444,9 @@ ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 10; @@ -558,8 +579,9 @@ ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 10; @@ -586,8 +608,9 @@ ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 10; diff --git a/cw_monero/example/macos/Runner/DebugProfile.entitlements b/macos/Runner/RunnerBase.entitlements similarity index 66% rename from cw_monero/example/macos/Runner/DebugProfile.entitlements rename to macos/Runner/RunnerBase.entitlements index dddb8a30c..2003d2c2b 100644 --- a/cw_monero/example/macos/Runner/DebugProfile.entitlements +++ b/macos/Runner/RunnerBase.entitlements @@ -4,9 +4,13 @@ com.apple.security.app-sandbox - com.apple.security.cs.allow-jit + com.apple.security.network.client com.apple.security.network.server + keychain-access-groups + + $(AppIdentifierPrefix)${BUNDLE_ID} + diff --git a/macos/monero_libwallet2_api_c.dylib b/macos/monero_libwallet2_api_c.dylib new file mode 120000 index 000000000..b0ccd724f --- /dev/null +++ b/macos/monero_libwallet2_api_c.dylib @@ -0,0 +1 @@ +../scripts/monero_c/release/monero/host-apple-darwin_libwallet2_api_c.dylib \ No newline at end of file diff --git a/macos/wownero_libwallet2_api_c.dylib b/macos/wownero_libwallet2_api_c.dylib new file mode 120000 index 000000000..6b79a4f03 --- /dev/null +++ b/macos/wownero_libwallet2_api_c.dylib @@ -0,0 +1 @@ +../scripts/monero_c/release/wownero/host-apple-darwin_libwallet2_api_c.dylib \ No newline at end of file diff --git a/model_generator.sh b/model_generator.sh index 24dac675f..58ce9b5d0 100755 --- a/model_generator.sh +++ b/model_generator.sh @@ -7,6 +7,7 @@ cd cw_nano; flutter pub get; flutter packages pub run build_runner build --delet cd cw_bitcoin_cash; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. cd cw_solana; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. cd cw_tron; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. +cd cw_wownero; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. cd cw_polygon; flutter pub get; cd .. cd cw_ethereum; flutter pub get; cd .. flutter packages pub run build_runner build --delete-conflicting-outputs diff --git a/pubspec_base.yaml b/pubspec_base.yaml index e00527d9f..0ce331d57 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -163,6 +163,7 @@ flutter: - assets/polygon_node_list.yml - assets/solana_node_list.yml - assets/tron_node_list.yml + - assets/wownero_node_list.yml - assets/text/ - assets/faq/ - assets/animation/ diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index ac82c9e0f..d39b175dd 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -585,6 +585,7 @@ "seedtype": "Seedtype", "seedtype_legacy": "Legacy (25 words)", "seedtype_polyseed": "Polyseed (16 words)", + "seedtype_wownero": "Wownero (14 words)", "select_backup_file": "Select backup file", "select_buy_provider_notice": "Select a buy provider above. You can skip this screen by setting your default buy provider in app settings.", "select_destination": "Please select destination for the backup file.", diff --git a/run-android.sh b/run-android.sh index bdacef392..880d86b6f 100755 --- a/run-android.sh +++ b/run-android.sh @@ -4,8 +4,7 @@ get_current_branch() { if git rev-parse --git-dir > /dev/null 2>&1; then branch=$(git rev-parse --abbrev-ref HEAD) - branch=${branch//[-]/_} # Replace all dashes with underscores - echo "$branch" + echo "$branch" | tr '-' '_' else echo "Error: Not a git repository." return 1 @@ -16,6 +15,7 @@ get_current_branch() { update_app_properties() { local branch=$1 local file_path="./android/app.properties" + sed -i "s/^id=.*/id=com.cakewallet.$branch/" "$file_path" sed -i "s/^name=.*/name=$branch-Cake Wallet/" "$file_path" } @@ -27,4 +27,4 @@ if [[ $? -eq 0 ]]; then fi # run the app -flutter run \ No newline at end of file +flutter run diff --git a/scripts/android/app_config.sh b/scripts/android/app_config.sh index e2cbd72da..5fe5e503d 100755 --- a/scripts/android/app_config.sh +++ b/scripts/android/app_config.sh @@ -8,5 +8,5 @@ fi ./app_properties.sh ./app_icon.sh ./pubspec_gen.sh -./manifest.sh +./manifest.sh true #force overwrite manifest ./inject_app_details.sh diff --git a/scripts/android/build_all.sh b/scripts/android/build_all.sh index 5093d231d..ec70f02a6 100755 --- a/scripts/android/build_all.sh +++ b/scripts/android/build_all.sh @@ -10,6 +10,6 @@ DIR=$(dirname "$0") case $APP_ANDROID_TYPE in "monero.com") $DIR/build_monero_all.sh ;; "cakewallet") $DIR/build_monero_all.sh - $DIR/build_haven.sh ;; + $DIR/build_haven_all.sh ;; "haven") $DIR/build_haven_all.sh ;; esac diff --git a/scripts/android/build_monero.sh b/scripts/android/build_monero.sh deleted file mode 100755 index fb596e452..000000000 --- a/scripts/android/build_monero.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/sh - -. ./config.sh -MONERO_BRANCH=release-v0.18.3.2-android -MONERO_SRC_DIR=${WORKDIR}/monero - -git clone https://github.com/cake-tech/monero.git ${MONERO_SRC_DIR} --branch ${MONERO_BRANCH} -cd $MONERO_SRC_DIR -git submodule update --init --force - -for arch in "aarch" "aarch64" "i686" "x86_64" -do -FLAGS="" -PREFIX=${WORKDIR}/prefix_${arch} -DEST_LIB_DIR=${PREFIX}/lib/monero -DEST_INCLUDE_DIR=${PREFIX}/include/monero -export CMAKE_INCLUDE_PATH="${PREFIX}/include" -export CMAKE_LIBRARY_PATH="${PREFIX}/lib" -ANDROID_STANDALONE_TOOLCHAIN_PATH="${TOOLCHAIN_BASE_DIR}_${arch}" -PATH="${ANDROID_STANDALONE_TOOLCHAIN_PATH}/bin:${ORIGINAL_PATH}" - -mkdir -p $DEST_LIB_DIR -mkdir -p $DEST_INCLUDE_DIR - -case $arch in - "aarch" ) - CLANG=arm-linux-androideabi-clang - CXXLANG=arm-linux-androideabi-clang++ - BUILD_64=OFF - TAG="android-armv7" - ARCH="armv7-a" - ARCH_ABI="armeabi-v7a" - FLAGS="-D CMAKE_ANDROID_ARM_MODE=ON -D NO_AES=true";; - "aarch64" ) - CLANG=aarch64-linux-androideabi-clang - CXXLANG=aarch64-linux-androideabi-clang++ - BUILD_64=ON - TAG="android-armv8" - ARCH="armv8-a" - ARCH_ABI="arm64-v8a";; - "i686" ) - CLANG=i686-linux-androideabi-clang - CXXLANG=i686-linux-androideabi-clang++ - BUILD_64=OFF - TAG="android-x86" - ARCH="i686" - ARCH_ABI="x86";; - "x86_64" ) - CLANG=x86_64-linux-androideabi-clang - CXXLANG=x86_64-linux-androideabi-clang++ - BUILD_64=ON - TAG="android-x86_64" - ARCH="x86-64" - ARCH_ABI="x86_64";; -esac - -cd $MONERO_SRC_DIR -rm -rf ./build/release -mkdir -p ./build/release -cd ./build/release -CC=${CLANG} CXX=${CXXLANG} cmake -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH=${ARCH} -D STATIC=ON -D BUILD_64=${BUILD_64} -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG=${TAG} -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARCH_ABI=${ARCH_ABI} -D MANUAL_SUBMODULES=1 $FLAGS ../.. - -make wallet_api -j$THREADS -find . -path ./lib -prune -o -name '*.a' -exec cp '{}' lib \; - -cp -r ./lib/* $DEST_LIB_DIR -cp ../../src/wallet/api/wallet2_api.h $DEST_INCLUDE_DIR -done diff --git a/scripts/android/build_monero_all.sh b/scripts/android/build_monero_all.sh index 69ec37b5f..261ebd560 100755 --- a/scripts/android/build_monero_all.sh +++ b/scripts/android/build_monero_all.sh @@ -1,9 +1,57 @@ #!/bin/bash -./build_iconv.sh -./build_boost.sh -./build_openssl.sh -./build_sodium.sh -./build_unbound.sh -./build_zmq.sh -./build_monero.sh +# Usage: env USE_DOCKER= ./build_all.sh + +set -x -e + +cd "$(dirname "$0")" + +NPROC="-j$(nproc)" + +if [[ "x$(uname)" == "xDarwin" ]]; +then + USE_DOCKER="ON" + NPROC="-j1" +fi + +../prepare_moneroc.sh + +if [[ ! "x$RUNNER_OS" == "x" ]]; +then + REMOVE_CACHES=ON +fi + +# NOTE: -j1 is intentional. Otherwise you will run into weird behaviour on macos +if [[ ! "x$USE_DOCKER" == "x" ]]; +then + for COIN in monero wownero; + do + pushd ../monero_c + docker run --platform linux/amd64 -v$HOME/.cache/ccache:/root/.ccache -v$PWD:$PWD -w $PWD --rm -it git.mrcyjanek.net/mrcyjanek/debian:buster bash -c "git config --global --add safe.directory '*'; apt update; apt install -y ccache gcc g++ libtinfo5 gperf; ./build_single.sh ${COIN} x86_64-linux-android $NPROC" + # docker run --platform linux/amd64 -v$PWD:$PWD -w $PWD --rm -it git.mrcyjanek.net/mrcyjanek/debian:buster bash -c "git config --global --add safe.directory '*'; apt update; apt install -y ccache gcc g++ libtinfo5 gperf; ./build_single.sh ${COIN} i686-linux-android $NPROC" + docker run --platform linux/amd64 -v$HOME/.cache/ccache:/root/.ccache -v$PWD:$PWD -w $PWD --rm -it git.mrcyjanek.net/mrcyjanek/debian:buster bash -c "git config --global --add safe.directory '*'; apt update; apt install -y ccache gcc g++ libtinfo5 gperf; ./build_single.sh ${COIN} armv7a-linux-androideabi $NPROC" + docker run --platform linux/amd64 -v$HOME/.cache/ccache:/root/.ccache -v$PWD:$PWD -w $PWD --rm -it git.mrcyjanek.net/mrcyjanek/debian:buster bash -c "git config --global --add safe.directory '*'; apt update; apt install -y ccache gcc g++ libtinfo5 gperf; ./build_single.sh ${COIN} aarch64-linux-android $NPROC" + popd + done +else + for COIN in monero wownero; + do + pushd ../monero_c + env -i ./build_single.sh ${COIN} x86_64-linux-android $NPROC + [[ ! "x$REMOVE_CACHES" == "x" ]] && rm -rf ${COIN}/contrib/depends/x86_64-linux-android + # ./build_single.sh ${COIN} i686-linux-android $NPROC + # [[ ! "x$REMOVE_CACHES" == "x" ]] && rm -rf ${COIN}/contrib/depends/i686-linux-android + env -i ./build_single.sh ${COIN} armv7a-linux-androideabi $NPROC + [[ ! "x$REMOVE_CACHES" == "x" ]] && rm -rf ${COIN}/contrib/depends/armv7a-linux-androideabi + env -i ./build_single.sh ${COIN} aarch64-linux-android $NPROC + [[ ! "x$REMOVE_CACHES" == "x" ]] && rm -rf ${COIN}/contrib/depends/aarch64-linux-android + + popd + unxz -f ../monero_c/release/${COIN}/x86_64-linux-android_libwallet2_api_c.so.xz + + unxz -f ../monero_c/release/${COIN}/armv7a-linux-androideabi_libwallet2_api_c.so.xz + + unxz -f ../monero_c/release/${COIN}/aarch64-linux-android_libwallet2_api_c.so.xz + [[ ! "x$REMOVE_CACHES" == "x" ]] && rm -rf ${COIN}/contrib/depends/{built,sources} + done +fi diff --git a/scripts/android/build_openssl.sh b/scripts/android/build_openssl.sh index aa668e6bf..33788c8b9 100755 --- a/scripts/android/build_openssl.sh +++ b/scripts/android/build_openssl.sh @@ -1,6 +1,6 @@ #!/bin/sh -set -e +set -e -x . ./config.sh OPENSSL_FILENAME=openssl-1.1.1q.tar.gz @@ -26,7 +26,6 @@ do PREFIX=$WORKDIR/prefix_${arch} TOOLCHAIN=${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64 PATH="${TOOLCHAIN}/bin:${ORIGINAL_PATH}" - case $arch in "aarch") X_ARCH="android-arm";; "aarch64") X_ARCH="android-arm64";; diff --git a/scripts/android/build_unbound.sh b/scripts/android/build_unbound.sh index 8786b0f2b..afe848a41 100755 --- a/scripts/android/build_unbound.sh +++ b/scripts/android/build_unbound.sh @@ -14,7 +14,7 @@ PATH="${TOOLCHAIN_BASE_DIR}_${arch}/bin:${ORIGINAL_PATH}" cd $WORKDIR rm -rf $EXPAT_SRC_DIR -git clone https://github.com/libexpat/libexpat.git -b ${EXPAT_VERSION} ${EXPAT_SRC_DIR} +git clone https://github.com/libexpat/libexpat.git --depth=1 -b ${EXPAT_VERSION} ${EXPAT_SRC_DIR} cd $EXPAT_SRC_DIR test `git rev-parse HEAD` = ${EXPAT_HASH} || exit 1 cd $EXPAT_SRC_DIR/expat @@ -49,7 +49,7 @@ PATH="${TOOLCHAIN_BIN_PATH}:${TOOLCHAIN_BASE_DIR}_${arch}/bin:${ORIGINAL_PATH}" echo $PATH cd $WORKDIR rm -rf $UNBOUND_SRC_DIR -git clone https://github.com/NLnetLabs/unbound.git -b ${UNBOUND_VERSION} ${UNBOUND_SRC_DIR} +git clone https://github.com/NLnetLabs/unbound.git --depth=1 -b ${UNBOUND_VERSION} ${UNBOUND_SRC_DIR} cd $UNBOUND_SRC_DIR test `git rev-parse HEAD` = ${UNBOUND_HASH} || exit 1 diff --git a/scripts/android/copy_monero_deps.sh b/scripts/android/copy_monero_deps.sh index d59e9d7f0..ed8181e6b 100755 --- a/scripts/android/copy_monero_deps.sh +++ b/scripts/android/copy_monero_deps.sh @@ -41,5 +41,4 @@ done mkdir -p ${CW_HAVEN_EXTERNAL_DIR}/include mkdir -p ${CW_MONERO_EXTERNAL_DIR}/include -cp $CW_EXRTERNAL_DIR/x86/include/monero/wallet2_api.h ${CW_MONERO_EXTERNAL_DIR}/include cp $CW_EXRTERNAL_DIR/x86/include/haven/wallet2_api.h ${CW_HAVEN_EXTERNAL_DIR}/include diff --git a/scripts/android/init_boost.sh b/scripts/android/init_boost.sh index 13120c910..7579acdd7 100755 --- a/scripts/android/init_boost.sh +++ b/scripts/android/init_boost.sh @@ -17,6 +17,6 @@ echo $BOOST_SHA256 $BOOST_FILE_PATH | sha256sum -c - || exit 1 cd $WORKDIR rm -rf $BOOST_SRC_DIR rm -rf $PREFIX/include/boost -tar -xvf $BOOST_FILE_PATH -C $WORKDIR +tar -xf $BOOST_FILE_PATH -C $WORKDIR cd $BOOST_SRC_DIR -./bootstrap.sh --prefix=${PREFIX} +./bootstrap.sh --prefix=${PREFIX} --with-toolset=gcc diff --git a/scripts/android/inject_app_details.sh b/scripts/android/inject_app_details.sh index 3fa9ef921..2957b91e3 100755 --- a/scripts/android/inject_app_details.sh +++ b/scripts/android/inject_app_details.sh @@ -6,6 +6,7 @@ if [ -z "$APP_ANDROID_TYPE" ]; then fi cd ../.. +set -x sed -i "0,/version:/{s/version:.*/version: ${APP_ANDROID_VERSION}+${APP_ANDROID_BUILD_NUMBER}/}" ./pubspec.yaml sed -i "0,/version:/{s/__APP_PACKAGE__/${APP_ANDROID_PACKAGE}/}" ./android/app/src/main/AndroidManifest.xml sed -i "0,/__APP_SCHEME__/s/__APP_SCHEME__/${APP_ANDROID_SCHEME}/" ./android/app/src/main/AndroidManifest.xml diff --git a/scripts/android/install_ndk.sh b/scripts/android/install_ndk.sh index bee72abad..ea131eb39 100755 --- a/scripts/android/install_ndk.sh +++ b/scripts/android/install_ndk.sh @@ -8,9 +8,9 @@ TOOLCHAIN_x86_DIR=${TOOLCHAIN_DIR}_i686 TOOLCHAIN_x86_64_DIR=${TOOLCHAIN_DIR}_x86_64 ANDROID_NDK_SHA256="3f541adbd0330a9205ba12697f6d04ec90752c53d6b622101a2a8a856e816589" -curl https://dl.google.com/android/repository/android-ndk-r17c-linux-x86_64.zip -o ${ANDROID_NDK_ZIP} -echo $ANDROID_NDK_SHA256 $ANDROID_NDK_ZIP | sha256sum -c || exit 1 -unzip $ANDROID_NDK_ZIP -d $WORKDIR + curl https://dl.google.com/android/repository/android-ndk-r17c-linux-x86_64.zip -o ${ANDROID_NDK_ZIP} + echo $ANDROID_NDK_SHA256 $ANDROID_NDK_ZIP | sha256sum -c || exit 1 + unzip $ANDROID_NDK_ZIP -d $WORKDIR ${ANDROID_NDK_ROOT}/build/tools/make_standalone_toolchain.py --arch arm64 --api $API --install-dir ${TOOLCHAIN_A64_DIR} --stl=libc++ ${ANDROID_NDK_ROOT}/build/tools/make_standalone_toolchain.py --arch arm --api $API --install-dir ${TOOLCHAIN_A32_DIR} --stl=libc++ diff --git a/scripts/android/manifest.sh b/scripts/android/manifest.sh index 95459606d..dab24d5c0 100755 --- a/scripts/android/manifest.sh +++ b/scripts/android/manifest.sh @@ -1,5 +1,10 @@ #!/bin/bash cd ../.. -cp -rf ./android/app/src/main/AndroidManifestBase.xml ./android/app/src/main/AndroidManifest.xml -cd scripts/android \ No newline at end of file + +if [ "$1" = true ]; then + cp -rf ./android/app/src/main/AndroidManifestBase.xml ./android/app/src/main/AndroidManifest.xml +else + cp -n ./android/app/src/main/AndroidManifestBase.xml ./android/app/src/main/AndroidManifest.xml +fi +cd scripts/android diff --git a/scripts/android/pubspec_gen.sh b/scripts/android/pubspec_gen.sh index bc7985506..468f548f3 100755 --- a/scripts/android/pubspec_gen.sh +++ b/scripts/android/pubspec_gen.sh @@ -10,7 +10,10 @@ case $APP_ANDROID_TYPE in CONFIG_ARGS="--monero" ;; $CAKEWALLET) - CONFIG_ARGS="--monero --bitcoin --haven --ethereum --polygon --nano --bitcoinCash --solana --tron" + CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero" + if [ "$CW_WITH_HAVEN" = true ];then + CONFIG_ARGS="$CONFIG_ARGS --haven" + fi ;; $HAVEN) CONFIG_ARGS="--haven" diff --git a/scripts/docker/.gitignore b/scripts/docker/.gitignore index ea1472ec1..c39e9d9f7 100644 --- a/scripts/docker/.gitignore +++ b/scripts/docker/.gitignore @@ -1 +1,2 @@ output/ +cache/ \ No newline at end of file diff --git a/scripts/docker/Dockerfile b/scripts/docker/Dockerfile old mode 100644 new mode 100755 index eef09a323..a352cdc71 --- a/scripts/docker/Dockerfile +++ b/scripts/docker/Dockerfile @@ -4,23 +4,59 @@ LABEL authors="konsti" ENV MONERO_BRANCH=release-v0.18.2.2-android RUN apt-get update && \ echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections && \ - apt-get install -y dialog apt-utils curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake clang + apt-get install -y dialog apt-utils curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake clang bison ccache RUN mkdir /opt/android/ -COPY . /opt/android/cakewallet/ - WORKDIR /opt/android/cakewallet/ +# build_all.sh +# build_boost.sh +# build_haven.sh +# build_haven_all.sh +# build_iconv.sh +# build_monero.sh +# build_openssl.sh +# build_sodium.sh +# build_unbound.sh +# build_zmq.sh +# config.sh +# copy_haven_deps.sh +# copy_monero_deps.sh +# docker-compose.yml +# entrypoint.sh +# finish_boost.sh +# init_boost.sh +# install_ndk.sh +COPY config.sh /opt/android/cakewallet/ +COPY install_ndk.sh /opt/android/cakewallet/ RUN ./install_ndk.sh +COPY build_iconv.sh /opt/android/cakewallet/ RUN ./build_iconv.sh + +COPY build_boost.sh /opt/android/cakewallet/ +COPY init_boost.sh /opt/android/cakewallet/ +COPY finish_boost.sh /opt/android/cakewallet/ RUN ./build_boost.sh + +COPY build_openssl.sh /opt/android/cakewallet/ RUN ./build_openssl.sh + +COPY build_sodium.sh /opt/android/cakewallet/ RUN ./build_sodium.sh + +COPY build_unbound.sh /opt/android/cakewallet/ RUN ./build_unbound.sh + +COPY build_zmq.sh /opt/android/cakewallet/ RUN ./build_zmq.sh +COPY entrypoint.sh /opt/android/cakewallet/ +COPY build_monero.sh /opt/android/cakewallet/ +COPY copy_monero_deps.sh /opt/android/cakewallet/ +COPY build_haven.sh /opt/android/cakewallet/ +COPY copy_haven_deps.sh /opt/android/cakewallet/ ENTRYPOINT ["./entrypoint.sh"] diff --git a/scripts/docker/build_all.sh b/scripts/docker/build_all.sh old mode 100644 new mode 100755 index 0acb7fcde..a4163c3f4 --- a/scripts/docker/build_all.sh +++ b/scripts/docker/build_all.sh @@ -1 +1,17 @@ -#!/bin/sh if [ -z "$APP_ANDROID_TYPE" ]; then echo "Please set APP_ANDROID_TYPE" exit 1 fi DIR=$(dirname "$0") case $APP_ANDROID_TYPE in "monero.com") $DIR/build_monero_all.sh ;; "cakewallet") $DIR/build_monero_all.sh $DIR/build_haven.sh ;; "haven") $DIR/build_haven_all.sh ;; esac \ No newline at end of file +#!/bin/sh + +set -x -e + +if [ -z "$APP_ANDROID_TYPE" ]; then + echo "Please set APP_ANDROID_TYPE" + exit 1 +fi + +DIR=$(dirname "$0") + +case $APP_ANDROID_TYPE in + "monero.com") $DIR/build_monero_all.sh ;; + "cakewallet") $DIR/build_monero_all.sh + $DIR/build_haven.sh ;; + "haven") $DIR/build_haven_all.sh ;; +esac diff --git a/scripts/docker/build_boost.sh b/scripts/docker/build_boost.sh old mode 100644 new mode 100755 index 2c98afab5..97333bbee --- a/scripts/docker/build_boost.sh +++ b/scripts/docker/build_boost.sh @@ -1,5 +1,5 @@ #!/bin/bash - +set -x -e . ./config.sh BOOST_SRC_DIR=$WORKDIR/boost_1_72_0 BOOST_FILENAME=boost_1_72_0.tar.bz2 diff --git a/scripts/docker/build_haven.sh b/scripts/docker/build_haven.sh old mode 100644 new mode 100755 index 7927c5102..1cfb16265 --- a/scripts/docker/build_haven.sh +++ b/scripts/docker/build_haven.sh @@ -1 +1,71 @@ -#!/bin/sh . ./config.sh HAVEN_VERSION=tags/v3.0.7 HAVEN_SRC_DIR=${WORKDIR}/haven git clone https://github.com/haven-protocol-org/haven-main.git ${HAVEN_SRC_DIR} git checkout ${HAVEN_VERSION} cd $HAVEN_SRC_DIR git submodule init git submodule update for arch in "aarch" "aarch64" "i686" "x86_64" do FLAGS="" PREFIX=${WORKDIR}/prefix_${arch} DEST_LIB_DIR=${PREFIX}/lib/haven DEST_INCLUDE_DIR=${PREFIX}/include/haven export CMAKE_INCLUDE_PATH="${PREFIX}/include" export CMAKE_LIBRARY_PATH="${PREFIX}/lib" ANDROID_STANDALONE_TOOLCHAIN_PATH="${TOOLCHAIN_BASE_DIR}_${arch}" PATH="${ANDROID_STANDALONE_TOOLCHAIN_PATH}/bin:${ORIGINAL_PATH}" mkdir -p $DEST_LIB_DIR mkdir -p $DEST_INCLUDE_DIR case $arch in "aarch" ) CLANG=arm-linux-androideabi-clang CXXLANG=arm-linux-androideabi-clang++ BUILD_64=OFF TAG="android-armv7" ARCH="armv7-a" ARCH_ABI="armeabi-v7a" FLAGS="-D CMAKE_ANDROID_ARM_MODE=ON -D NO_AES=true";; "aarch64" ) CLANG=aarch64-linux-androideabi-clang CXXLANG=aarch64-linux-androideabi-clang++ BUILD_64=ON TAG="android-armv8" ARCH="armv8-a" ARCH_ABI="arm64-v8a";; "i686" ) CLANG=i686-linux-androideabi-clang CXXLANG=i686-linux-androideabi-clang++ BUILD_64=OFF TAG="android-x86" ARCH="i686" ARCH_ABI="x86";; "x86_64" ) CLANG=x86_64-linux-androideabi-clang CXXLANG=x86_64-linux-androideabi-clang++ BUILD_64=ON TAG="android-x86_64" ARCH="x86-64" ARCH_ABI="x86_64";; esac cd $HAVEN_SRC_DIR rm -rf ./build/release mkdir -p ./build/release cd ./build/release CC=${CLANG} CXX=${CXXLANG} cmake -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH=${ARCH} -D STATIC=ON -D BUILD_64=${BUILD_64} -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG=${TAG} -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARCH_ABI=${ARCH_ABI} $FLAGS ../.. make wallet_api -j$THREADS find . -path ./lib -prune -o -name '*.a' -exec cp '{}' lib \; cp -r ./lib/* $DEST_LIB_DIR cp ../../src/wallet/api/wallet2_api.h $DEST_INCLUDE_DIR done \ No newline at end of file +#!/bin/sh +set -x -e + +. ./config.sh +HAVEN_VERSION=tags/v3.0.7 +HAVEN_SRC_DIR=${WORKDIR}/haven + +git clone https://github.com/haven-protocol-org/haven-main.git ${HAVEN_SRC_DIR} +cd $HAVEN_SRC_DIR +git checkout ${HAVEN_VERSION} +git submodule init +git submodule update + +for arch in "aarch" "aarch64" "i686" "x86_64" +do +FLAGS="" +PREFIX=${WORKDIR}/prefix_${arch} +DEST_LIB_DIR=${PREFIX}/lib/haven +DEST_INCLUDE_DIR=${PREFIX}/include/haven +export CMAKE_INCLUDE_PATH="${PREFIX}/include" +export CMAKE_LIBRARY_PATH="${PREFIX}/lib" +ANDROID_STANDALONE_TOOLCHAIN_PATH="${TOOLCHAIN_BASE_DIR}_${arch}" +PATH="${ANDROID_STANDALONE_TOOLCHAIN_PATH}/bin:${ORIGINAL_PATH}" + +mkdir -p $DEST_LIB_DIR +mkdir -p $DEST_INCLUDE_DIR + +case $arch in + "aarch" ) + CLANG=arm-linux-androideabi-clang + CXXLANG=arm-linux-androideabi-clang++ + BUILD_64=OFF + TAG="android-armv7" + ARCH="armv7-a" + ARCH_ABI="armeabi-v7a" + FLAGS="-D CMAKE_ANDROID_ARM_MODE=ON -D NO_AES=true";; + "aarch64" ) + CLANG=aarch64-linux-androideabi-clang + CXXLANG=aarch64-linux-androideabi-clang++ + BUILD_64=ON + TAG="android-armv8" + ARCH="armv8-a" + ARCH_ABI="arm64-v8a";; + "i686" ) + CLANG=i686-linux-androideabi-clang + CXXLANG=i686-linux-androideabi-clang++ + BUILD_64=OFF + TAG="android-x86" + ARCH="i686" + ARCH_ABI="x86";; + "x86_64" ) + CLANG=x86_64-linux-androideabi-clang + CXXLANG=x86_64-linux-androideabi-clang++ + BUILD_64=ON + TAG="android-x86_64" + ARCH="x86-64" + ARCH_ABI="x86_64";; +esac + +cd $HAVEN_SRC_DIR +rm -rf ./build/release +mkdir -p ./build/release +cd ./build/release +CC=${CLANG} CXX=${CXXLANG} cmake -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH=${ARCH} -D STATIC=ON -D BUILD_64=${BUILD_64} -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG=${TAG} -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARCH_ABI=${ARCH_ABI} $FLAGS ../.. + +make wallet_api -j$THREADS +find . -path ./lib -prune -o -name '*.a' -exec cp '{}' lib \; + +cp -r ./lib/* $DEST_LIB_DIR +cp ../../src/wallet/api/wallet2_api.h $DEST_INCLUDE_DIR +done diff --git a/scripts/docker/build_haven_all.sh b/scripts/docker/build_haven_all.sh old mode 100644 new mode 100755 index 4b33ad077..ce8eb3f0e --- a/scripts/docker/build_haven_all.sh +++ b/scripts/docker/build_haven_all.sh @@ -1 +1,9 @@ -#!/bin/bash ./build_iconv.sh ./build_boost.sh ./build_openssl.sh ./build_sodium.sh ./build_zmq.sh ./build_haven.sh \ No newline at end of file +#!/bin/bash +set -x -e + +./build_iconv.sh +./build_boost.sh +./build_openssl.sh +./build_sodium.sh +./build_zmq.sh +./build_haven.sh diff --git a/scripts/docker/build_iconv.sh b/scripts/docker/build_iconv.sh old mode 100644 new mode 100755 index 9edac26b3..e55686fec --- a/scripts/docker/build_iconv.sh +++ b/scripts/docker/build_iconv.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -x -e . ./config.sh export ICONV_FILENAME=libiconv-1.16.tar.gz diff --git a/scripts/docker/build_monero.sh b/scripts/docker/build_monero.sh old mode 100644 new mode 100755 index d663f5288..04162f0f8 --- a/scripts/docker/build_monero.sh +++ b/scripts/docker/build_monero.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -x -e . ./config.sh diff --git a/scripts/docker/build_openssl.sh b/scripts/docker/build_openssl.sh old mode 100644 new mode 100755 index 685d0a1be..233e64a7c --- a/scripts/docker/build_openssl.sh +++ b/scripts/docker/build_openssl.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -x -e set -e diff --git a/scripts/docker/build_sodium.sh b/scripts/docker/build_sodium.sh old mode 100644 new mode 100755 index a934d641b..c911814d9 --- a/scripts/docker/build_sodium.sh +++ b/scripts/docker/build_sodium.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -x -e . ./config.sh SODIUM_SRC_DIR=${WORKDIR}/libsodium diff --git a/scripts/docker/build_unbound.sh b/scripts/docker/build_unbound.sh old mode 100644 new mode 100755 index 8786b0f2b..2d1efdea2 --- a/scripts/docker/build_unbound.sh +++ b/scripts/docker/build_unbound.sh @@ -1,7 +1,7 @@ #!/bin/bash +set -x -e . ./config.sh - EXPAT_VERSION=R_2_4_8 EXPAT_HASH="3bab6c09bbe8bf42d84b81563ddbcf4cca4be838" EXPAT_SRC_DIR=$WORKDIR/libexpat diff --git a/scripts/docker/build_zmq.sh b/scripts/docker/build_zmq.sh old mode 100644 new mode 100755 index bbff9e41b..19bb99172 --- a/scripts/docker/build_zmq.sh +++ b/scripts/docker/build_zmq.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -x -e . ./config.sh ZMQ_SRC_DIR=$WORKDIR/libzmq diff --git a/scripts/docker/config.sh b/scripts/docker/config.sh old mode 100644 new mode 100755 index c5067f2c3..a9b691688 --- a/scripts/docker/config.sh +++ b/scripts/docker/config.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -x -e export API=21 export WORKDIR=/opt/android diff --git a/scripts/docker/copy_haven_deps.sh b/scripts/docker/copy_haven_deps.sh old mode 100644 new mode 100755 index d59e9d7f0..cef644701 --- a/scripts/docker/copy_haven_deps.sh +++ b/scripts/docker/copy_haven_deps.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -x WORKDIR=/opt/android CW_DIR=${WORKDIR}/cake_wallet diff --git a/scripts/docker/copy_monero_deps.sh b/scripts/docker/copy_monero_deps.sh old mode 100644 new mode 100755 index e4392186c..1c2394c0d --- a/scripts/docker/copy_monero_deps.sh +++ b/scripts/docker/copy_monero_deps.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -x WORKDIR=/opt/android CW_EXRTERNAL_DIR=${WORKDIR}/output/android diff --git a/scripts/docker/docker-compose.yml b/scripts/docker/docker-compose.yml old mode 100644 new mode 100755 index eaeea0f5b..00f24ce2e --- a/scripts/docker/docker-compose.yml +++ b/scripts/docker/docker-compose.yml @@ -7,3 +7,5 @@ services: MONERO_BRANCH: release-v0.18.2.2-android volumes: - ./output:/opt/android/output + - ./cache/dotcache:/root/.cache + - ./cache/dotccache:/root/.ccache diff --git a/scripts/docker/entrypoint.sh b/scripts/docker/entrypoint.sh old mode 100644 new mode 100755 index e4bdc017c..14f02a1f8 --- a/scripts/docker/entrypoint.sh +++ b/scripts/docker/entrypoint.sh @@ -1,4 +1,11 @@ #!/bin/bash +set -x -e + +ls /opt/android + +rm -rf monero haven ./build_monero.sh +./build_haven.sh ./copy_monero_deps.sh +./copy_haven_deps.sh diff --git a/scripts/docker/finish_boost.sh b/scripts/docker/finish_boost.sh old mode 100644 new mode 100755 index e3f195276..774c65d77 --- a/scripts/docker/finish_boost.sh +++ b/scripts/docker/finish_boost.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -x -e ARCH=$1 PREFIX=$2 diff --git a/scripts/docker/init_boost.sh b/scripts/docker/init_boost.sh old mode 100644 new mode 100755 index ffb7a1416..068647e1f --- a/scripts/docker/init_boost.sh +++ b/scripts/docker/init_boost.sh @@ -1,4 +1,6 @@ #!/bin/bash +set -x -e + ARCH=$1 PREFIX=$2 @@ -17,6 +19,6 @@ echo $BOOST_SHA256 $BOOST_FILE_PATH | sha256sum -c - || exit 1 cd $WORKDIR rm -rf $BOOST_SRC_DIR rm -rf $PREFIX/include/boost -tar -xvf $BOOST_FILE_PATH -C $WORKDIR +tar -xf $BOOST_FILE_PATH -C $WORKDIR cd $BOOST_SRC_DIR -./bootstrap.sh --prefix=${PREFIX} +./bootstrap.sh --prefix=${PREFIX} --with-toolset=gcc diff --git a/scripts/docker/install_ndk.sh b/scripts/docker/install_ndk.sh old mode 100644 new mode 100755 index 5f97751e3..94373954c --- a/scripts/docker/install_ndk.sh +++ b/scripts/docker/install_ndk.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -x -e . ./config.sh TOOLCHAIN_DIR=${WORKDIR}/toolchain diff --git a/scripts/gen_android_manifest.sh b/scripts/gen_android_manifest.sh new file mode 100755 index 000000000..d6f7a130d --- /dev/null +++ b/scripts/gen_android_manifest.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +ANDROID_SCRIPTS_DIR=`pwd`/android +if [ ! -d $ANDROID_SCRIPTS_DIR ]; then + echo "no android scripts directory found at ${ANDROID_SCRIPTS_DIR}" + exit 0 +fi + +cd $ANDROID_SCRIPTS_DIR +./manifest.sh diff --git a/scripts/ios/app_config.sh b/scripts/ios/app_config.sh index ab7fbd422..67375c914 100755 --- a/scripts/ios/app_config.sh +++ b/scripts/ios/app_config.sh @@ -9,8 +9,10 @@ if [ -z "$APP_IOS_TYPE" ]; then echo "Please set APP_IOS_TYPE" exit 1 fi - -cd ../.. # go to root +./gen_framework.sh +cd .. # go to scipts +./gen_android_manifest.sh +cd .. # go to root cp -rf ./ios/Runner/InfoBase.plist ./ios/Runner/Info.plist /usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName ${APP_IOS_NAME}" ./ios/Runner/Info.plist /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier ${APP_IOS_BUNDLE_ID}" ./ios/Runner/Info.plist @@ -20,6 +22,7 @@ cp -rf ./ios/Runner/InfoBase.plist ./ios/Runner/Info.plist /usr/libexec/PlistBuddy -c "Add :CFBundleURLTypes:1:CFBundleURLName string ${APP_IOS_TYPE}" ./ios/Runner/Info.plist /usr/libexec/PlistBuddy -c "Add :CFBundleURLTypes:1:CFBundleURLSchemes array" ./ios/Runner/Info.plist /usr/libexec/PlistBuddy -c "Add :CFBundleURLTypes:1:CFBundleURLSchemes: string ${APP_IOS_TYPE}" ./ios/Runner/Info.plist +sed -i '' "s/\${PRODUCT_BUNDLE_IDENTIFIER}/${APP_IOS_BUNDLE_ID}/g" ./ios/Runner.xcodeproj/project.pbxproj CONFIG_ARGS="" @@ -28,7 +31,10 @@ case $APP_IOS_TYPE in CONFIG_ARGS="--monero" ;; $CAKEWALLET) - CONFIG_ARGS="--monero --bitcoin --haven --ethereum --polygon --nano --bitcoinCash --solana --tron" + CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero" + if [ "$CW_WITH_HAVEN" = true ];then + CONFIG_ARGS="$CONFIG_ARGS --haven" + fi ;; $HAVEN) diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index 974e44bc4..7d52d42bb 100644 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_IOS_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.15.2" -MONERO_COM_BUILD_NUMBER=90 +MONERO_COM_VERSION="1.16.0" +MONERO_COM_BUILD_NUMBER=91 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.18.2" -CAKEWALLET_BUILD_NUMBER=250 +CAKEWALLET_VERSION="4.19.0" +CAKEWALLET_BUILD_NUMBER=251 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" HAVEN_NAME="Haven" diff --git a/scripts/ios/build_monero_all.sh b/scripts/ios/build_monero_all.sh index 2b61f6db0..aec6d86f3 100755 --- a/scripts/ios/build_monero_all.sh +++ b/scripts/ios/build_monero_all.sh @@ -1,10 +1,27 @@ #!/bin/sh . ./config.sh -./install_missing_headers.sh -./build_openssl.sh -./build_boost.sh -./build_sodium.sh -./build_zmq.sh -./build_unbound.sh -./build_monero.sh \ No newline at end of file +# ./install_missing_headers.sh +# ./build_openssl.sh +# ./build_boost.sh +# ./build_sodium.sh +# ./build_zmq.sh +# ./build_unbound.sh + +set -x -e + +cd "$(dirname "$0")" + +NPROC="-j$(sysctl -n hw.logicalcpu)" + +../prepare_moneroc.sh + +for COIN in monero wownero; +do + pushd ../monero_c + ./build_single.sh ${COIN} host-apple-ios $NPROC + popd +done + +unxz -f ../monero_c/release/monero/host-apple-ios_libwallet2_api_c.dylib.xz +unxz -f ../monero_c/release/wownero/host-apple-ios_libwallet2_api_c.dylib.xz diff --git a/scripts/ios/build_openssl.sh b/scripts/ios/build_openssl.sh index 57a59500b..d03054cd3 100755 --- a/scripts/ios/build_openssl.sh +++ b/scripts/ios/build_openssl.sh @@ -12,6 +12,8 @@ git clone $OPEN_SSL_URL $OPEN_SSL_DIR_PATH cd $OPEN_SSL_DIR_PATH ./build-libssl.sh --version=1.1.1q --targets="ios-cross-arm64" --deprecated -mv ${OPEN_SSL_DIR_PATH}/include/* $EXTERNAL_IOS_INCLUDE_DIR +# copy and then remove because mv is not working when there is subdirectories +cp -R ${OPEN_SSL_DIR_PATH}/include/* $EXTERNAL_IOS_INCLUDE_DIR +rm -rf ${OPEN_SSL_DIR_PATH}/include/ mv ${OPEN_SSL_DIR_PATH}/lib/libcrypto-iOS.a ${EXTERNAL_IOS_LIB_DIR}/libcrypto.a mv ${OPEN_SSL_DIR_PATH}/lib/libssl-iOS.a ${EXTERNAL_IOS_LIB_DIR}/libssl.a \ No newline at end of file diff --git a/scripts/ios/gen_framework.sh b/scripts/ios/gen_framework.sh new file mode 100755 index 000000000..950a7afe5 --- /dev/null +++ b/scripts/ios/gen_framework.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# Assume we are in scripts/ios +IOS_DIR="$(pwd)/../../ios" +DYLIB_NAME="monero_libwallet2_api_c.dylib" +DYLIB_LINK_PATH="${IOS_DIR}/${DYLIB_NAME}" +FRWK_DIR="${IOS_DIR}/MoneroWallet.framework" + +if [ ! -f $DYLIB_LINK_PATH ]; then + echo "Dylib is not found by the link: ${DYLIB_LINK_PATH}" + exit 0 +fi + +cd $FRWK_DIR # go to iOS framework dir +lipo -create $DYLIB_LINK_PATH -output MoneroWallet + +echo "Generated ${FRWK_DIR}" +# also generate for wownero +IOS_DIR="$(pwd)/../../ios" +DYLIB_NAME="wownero_libwallet2_api_c.dylib" +DYLIB_LINK_PATH="${IOS_DIR}/${DYLIB_NAME}" +FRWK_DIR="${IOS_DIR}/WowneroWallet.framework" + +if [ ! -f $DYLIB_LINK_PATH ]; then + echo "Dylib is not found by the link: ${DYLIB_LINK_PATH}" + exit 0 +fi + +cd $FRWK_DIR # go to iOS framework dir +lipo -create $DYLIB_LINK_PATH -output WowneroWallet + +echo "Generated ${FRWK_DIR}" \ No newline at end of file diff --git a/scripts/macos/app_config.sh b/scripts/macos/app_config.sh index a1143bb12..b8785a9be 100755 --- a/scripts/macos/app_config.sh +++ b/scripts/macos/app_config.sh @@ -9,7 +9,9 @@ if [ -z "$APP_MACOS_TYPE" ]; then exit 1 fi -cd ../.. # go to root +cd .. # go to scipts +./gen_android_manifest.sh +cd .. # go to root cp -rf ./macos/Runner/InfoBase.plist ./macos/Runner/Info.plist /usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName ${APP_MACOS_NAME}" ./macos/Runner/Info.plist /usr/libexec/PlistBuddy -c "Set :CFBundleName ${APP_MACOS_NAME}" ./macos/Runner/Info.plist @@ -20,9 +22,11 @@ cp -rf ./macos/Runner/InfoBase.plist ./macos/Runner/Info.plist # Fill entitlements Bundle ID cp -rf ./macos/Runner/DebugProfileBase.entitlements ./macos/Runner/DebugProfile.entitlements cp -rf ./macos/Runner/ReleaseBase.entitlements ./macos/Runner/Release.entitlements +cp -rf ./macos/Runner/RunnerBase.entitlements ./macos/Runner/Runner.entitlements cp -rf ./macos/Runner/Configs/AppInfoBase.xcconfig ./macos/Runner/Configs/AppInfo.xcconfig sed -i '' "s/\${BUNDLE_ID}/${APP_MACOS_BUNDLE_ID}/g" ./macos/Runner/DebugProfile.entitlements sed -i '' "s/\${BUNDLE_ID}/${APP_MACOS_BUNDLE_ID}/g" ./macos/Runner/Release.entitlements +sed -i '' "s/\${BUNDLE_ID}/${APP_MACOS_BUNDLE_ID}/g" ./macos/Runner/Runner.entitlements sed -i '' "s/\${PRODUCT_NAME}/${APP_MACOS_NAME}/g" ./macos/Runner/Configs/AppInfo.xcconfig sed -i '' "s/\${PRODUCT_BUNDLE_IDENTIFIER}/${APP_MACOS_BUNDLE_ID}/g" ./macos/Runner/Configs/AppInfo.xcconfig CONFIG_ARGS="" @@ -31,7 +35,7 @@ case $APP_MACOS_TYPE in $MONERO_COM) CONFIG_ARGS="--monero";; $CAKEWALLET) - CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron";; #--haven + CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero";; #--haven esac cp -rf pubspec_description.yaml pubspec.yaml @@ -40,4 +44,4 @@ flutter pub run tool/generate_pubspec.dart flutter pub get flutter packages pub run tool/configure.dart $CONFIG_ARGS cd $DIR -$DIR/app_icon.sh \ No newline at end of file +$DIR/app_icon.sh diff --git a/scripts/macos/build_monero_all.sh b/scripts/macos/build_monero_all.sh index f7e55909b..9f6130066 100755 --- a/scripts/macos/build_monero_all.sh +++ b/scripts/macos/build_monero_all.sh @@ -1,20 +1,59 @@ #!/bin/sh +set -x -e -ARCH=`uname -m` +cd "$(dirname "$0")" -. ./config.sh +NPROC="-j$(sysctl -n hw.logicalcpu)" +MONERO_LIBS="" +WOWNERO_LIBS="" +MONEROC_RELEASE_DIR="../monero_c/release/monero" +WOWNEROC_RELEASE_DIR="../monero_c/release/wownero" -case $ARCH in - arm64) - ./build_openssl_arm64.sh - ./build_boost_arm64.sh;; - x86_64) - ./build_openssl_x86_64.sh - ./build_boost_x86_64.sh;; -esac +../prepare_moneroc.sh -./build_zmq.sh -./build_expat.sh -./build_unbound.sh -./build_sodium.sh -./build_monero.sh \ No newline at end of file +# NOTE: -j1 is intentional. Otherwise you will run into weird behaviour on macos +if [[ ! "x$USE_DOCKER" == "x" ]]; +then + for COIN in monero wownero; + do + pushd ../monero_c + echo "unsupported!" + exit 1 + popd + done +else + if [[ "x$1" == "xuniversal" ]]; then + ARCHS=(arm64 x86_64) + else + ARCHS=$(uname -m) + fi + for COIN in monero wownero; + do + for ARCH in "${ARCHS[@]}"; + do + if [[ "$ARCH" == "arm64" ]]; then + export HOMEBREW_PREFIX=/opt/homebrew + HOST="aarch64-host-apple-darwin" + else + export HOMEBREW_PREFIX=/usr/local + HOST="${ARCH}-host-apple-darwin" + fi + + MONERO_LIBS=" -arch ${ARCH} ${MONEROC_RELEASE_DIR}/${HOST}_libwallet2_api_c.dylib" + WOWNERO_LIBS=" -arch ${ARCH} ${WOWNEROC_RELEASE_DIR}/${HOST}_libwallet2_api_c.dylib" + + if [[ ! $(uname -m) == $ARCH ]]; then + PRC="arch -${ARCH}" + fi + + pushd ../monero_c + $PRC ./build_single.sh ${COIN} ${HOST} $NPROC + unxz -f ./release/${COIN}/${HOST}_libwallet2_api_c.dylib.xz + + popd + done + done +fi + +lipo -create ${MONERO_LIBS} -output "${MONEROC_RELEASE_DIR}/host-apple-darwin_libwallet2_api_c.dylib" +lipo -create ${WOWNERO_LIBS} -output "${WOWNEROC_RELEASE_DIR}/host-apple-darwin_libwallet2_api_c.dylib" diff --git a/scripts/prepare_moneroc.sh b/scripts/prepare_moneroc.sh new file mode 100755 index 000000000..27d839ef4 --- /dev/null +++ b/scripts/prepare_moneroc.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +set -x -e + +cd "$(dirname "$0")" + +if [[ ! -d "monero_c" ]]; +then + git clone https://github.com/mrcyjanek/monero_c --branch rewrite-wip + cd monero_c + git checkout eaa7bdb8be3479418445ddb18bf33d453f64afcf + git reset --hard + git submodule update --init --force --recursive + ./apply_patches.sh monero + ./apply_patches.sh wownero +else + cd monero_c +fi + +if [[ ! -f "monero/.patch-applied" ]]; +then + ./apply_patches.sh monero +fi + +if [[ ! -f "wownero/.patch-applied" ]]; +then + ./apply_patches.sh wownero +fi +cd .. + +echo "monero_c source prepared". diff --git a/scripts/windows/build_all.sh b/scripts/windows/build_all.sh new file mode 100755 index 000000000..e77f6edb5 --- /dev/null +++ b/scripts/windows/build_all.sh @@ -0,0 +1,37 @@ +set -x -e + +cd "$(dirname "$0")" + +if [[ ! "x$(uname)" == "xLinux" ]]; +then + echo "Only Linux hosts can build windows (yes, i know)"; + exit 1 +fi + +../prepare_moneroc.sh + +# export USE_DOCKER="ON" + +pushd ../monero_c + set +e + command -v sudo && export SUDO=sudo + set -e + NPROC="-j$(nproc)" + if [[ ! "x$USE_DOCKER" == "x" ]]; + then + for COIN in monero wownero; + do + $SUDO docker run --platform linux/amd64 -v$HOME/.cache/ccache:/root/.ccache -v$PWD:$PWD -w $PWD --rm -it git.mrcyjanek.net/mrcyjanek/debian:buster bash -c "git config --global --add safe.directory '*'; apt update; apt install -y ccache gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 gperf libtinfo5; ./build_single.sh ${COIN} x86_64-w64-mingw32 $NPROC" + # $SUDO docker run --platform linux/amd64 -v$HOME/.cache/ccache:/root/.ccache -v$PWD:$PWD -w $PWD --rm -it git.mrcyjanek.net/mrcyjanek/debian:buster bash -c "git config --global --add safe.directory '*'; apt update; apt install -y ccache gcc-mingw-w64-i686 g++-mingw-w64-i686 gperf libtinfo5; ./build_single.sh ${COIN} i686-w64-mingw32 $NPROC" + done + else + for COIN in monero wownero; + do + $SUDO ./build_single.sh ${COIN} x86_64-w64-mingw32 $NPROC + # $SUDO ./build_single.sh ${COIN} i686-w64-mingw32 $NPROC + done + fi +popd + +$SUDO unxz -f ../monero_c/release/monero/*.dll.xz +$SUDO unxz -f ../monero_c/release/wownero/*.dll.xz diff --git a/scripts/windows/build_exe_installer.iss b/scripts/windows/build_exe_installer.iss new file mode 100644 index 000000000..4315f7d27 --- /dev/null +++ b/scripts/windows/build_exe_installer.iss @@ -0,0 +1,44 @@ +#define MyAppName "Cake Wallet" +#define MyAppVersion "0.0.1" +#define MyAppPublisher "Cake Labs LLC" +#define MyAppURL "https://cakewallet.com/" +#define MyAppExeName "CakeWallet.exe" + +[Setup] +AppId=com.cakewallet.cakewallet +AppName={#MyAppName} +AppVersion={#MyAppVersion} +;AppVerName={#MyAppName} {#MyAppVersion} +AppPublisher={#MyAppPublisher} +AppPublisherURL={#MyAppURL} +AppSupportURL={#MyAppURL} +AppUpdatesURL={#MyAppURL} +DefaultDirName={autopf}\CakeWallet +DisableProgramGroupPage=yes +LicenseFile=..\..\LICENSE.md +; Uncomment the following line to run in non administrative install mode (install for current user only.) +; PrivilegesRequired=lowest +OutputDir=..\..\ +OutputBaseFilename=cakewallet_setup +SetupIconFile=..\..\windows\runner\resources\app_icon.ico +Compression=lzma +SolidCompression=yes +WizardStyle=modern + +[Languages] +Name: "english"; MessagesFile: "compiler:Default.isl" + +[Tasks] +Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked + +[Files] +Source: "..\..\build\windows\x64\runner\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs +; NOTE: Don't use "Flags: ignoreversion" on any shared system files + +[Icons] +Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" +Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon + +[Run] +Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent + diff --git a/scripts/windows/cakewallet.sh b/scripts/windows/cakewallet.sh new file mode 100755 index 000000000..61e526a73 --- /dev/null +++ b/scripts/windows/cakewallet.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# This (wrapper) script should be run in wsl with installed (windows "side") flutter +# and available cmd.exe in PATH +# Assume that we are in scripts/windows dir +CW_ROOT=`pwd`/../.. +cd $CW_ROOT +cmd.exe /c cakewallet.bat $1 \ No newline at end of file diff --git a/tool/configure.dart b/tool/configure.dart index 133f12e52..fcfd676dc 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -9,6 +9,7 @@ const nanoOutputPath = 'lib/nano/nano.dart'; const polygonOutputPath = 'lib/polygon/polygon.dart'; const solanaOutputPath = 'lib/solana/solana.dart'; const tronOutputPath = 'lib/tron/tron.dart'; +const wowneroOutputPath = 'lib/wownero/wownero.dart'; const walletTypesPath = 'lib/wallet_types.g.dart'; const secureStoragePath = 'lib/core/secure_storage.dart'; const pubspecDefaultPath = 'pubspec_default.yaml'; @@ -26,6 +27,7 @@ Future main(List args) async { final hasPolygon = args.contains('${prefix}polygon'); final hasSolana = args.contains('${prefix}solana'); final hasTron = args.contains('${prefix}tron'); + final hasWownero = args.contains('${prefix}wownero'); final excludeFlutterSecureStorage = args.contains('${prefix}excludeFlutterSecureStorage'); await generateBitcoin(hasBitcoin); @@ -37,6 +39,7 @@ Future main(List args) async { await generatePolygon(hasPolygon); await generateSolana(hasSolana); await generateTron(hasTron); + await generateWownero(hasWownero); // await generateBanano(hasEthereum); await generatePubspec( @@ -51,6 +54,7 @@ Future main(List args) async { hasPolygon: hasPolygon, hasSolana: hasSolana, hasTron: hasTron, + hasWownero: hasWownero, ); await generateWalletTypes( hasMonero: hasMonero, @@ -63,6 +67,7 @@ Future main(List args) async { hasPolygon: hasPolygon, hasSolana: hasSolana, hasTron: hasTron, + hasWownero: hasWownero, ); await injectSecureStorage(!excludeFlutterSecureStorage); } @@ -239,7 +244,6 @@ Future generateMonero(bool hasImplementation) async { const moneroCommonHeaders = """ import 'package:cw_core/unspent_transaction_output.dart'; import 'package:cw_core/unspent_coins_info.dart'; -import 'package:cw_monero/monero_unspent.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; @@ -256,6 +260,7 @@ import 'package:polyseed/polyseed.dart';"""; import 'package:cw_core/get_height_by_date.dart'; import 'package:cw_core/monero_amount_format.dart'; import 'package:cw_core/monero_transaction_priority.dart'; +import 'package:cw_monero/monero_unspent.dart'; import 'package:cw_monero/monero_wallet_service.dart'; import 'package:cw_monero/monero_wallet.dart'; import 'package:cw_monero/monero_transaction_info.dart'; @@ -352,6 +357,8 @@ abstract class Monero { List getUnspents(Object wallet); Future updateUnspents(Object wallet); + Future getCurrentHeight(); + WalletCredentials createMoneroRestoreWalletFromKeysCredentials({ required String name, required String spendKey, @@ -414,6 +421,187 @@ abstract class MoneroAccountList { await outputFile.writeAsString(output); } +Future generateWownero(bool hasImplementation) async { + final outputFile = File(wowneroOutputPath); + const wowneroCommonHeaders = """ +import 'package:cw_core/unspent_transaction_output.dart'; +import 'package:cw_core/unspent_coins_info.dart'; +import 'package:mobx/mobx.dart'; +import 'package:cw_core/wallet_credentials.dart'; +import 'package:cw_core/wallet_info.dart'; +import 'package:cw_core/transaction_priority.dart'; +import 'package:cw_core/transaction_history.dart'; +import 'package:cw_core/transaction_info.dart'; +import 'package:cw_core/balance.dart'; +import 'package:cw_core/output_info.dart'; +import 'package:cake_wallet/view_model/send/output.dart'; +import 'package:cw_core/wallet_service.dart'; +import 'package:hive/hive.dart'; +import 'package:polyseed/polyseed.dart';"""; + const wowneroCWHeaders = """ +import 'package:cw_core/get_height_by_date.dart'; +import 'package:cw_core/wownero_amount_format.dart'; +import 'package:cw_core/monero_transaction_priority.dart'; +import 'package:cw_wownero/wownero_unspent.dart'; +import 'package:cw_wownero/wownero_wallet_service.dart'; +import 'package:cw_wownero/wownero_wallet.dart'; +import 'package:cw_wownero/wownero_transaction_info.dart'; +import 'package:cw_wownero/wownero_transaction_creation_credentials.dart'; +import 'package:cw_core/account.dart' as wownero_account; +import 'package:cw_wownero/api/wallet.dart' as wownero_wallet_api; +import 'package:cw_wownero/mnemonics/english.dart'; +import 'package:cw_wownero/mnemonics/chinese_simplified.dart'; +import 'package:cw_wownero/mnemonics/dutch.dart'; +import 'package:cw_wownero/mnemonics/german.dart'; +import 'package:cw_wownero/mnemonics/japanese.dart'; +import 'package:cw_wownero/mnemonics/russian.dart'; +import 'package:cw_wownero/mnemonics/spanish.dart'; +import 'package:cw_wownero/mnemonics/portuguese.dart'; +import 'package:cw_wownero/mnemonics/french.dart'; +import 'package:cw_wownero/mnemonics/italian.dart'; +import 'package:cw_wownero/pending_wownero_transaction.dart'; +"""; + const wowneroCwPart = "part 'cw_wownero.dart';"; + const wowneroContent = """ +class Account { + Account({required this.id, required this.label, this.balance}); + final int id; + final String label; + final String? balance; +} + +class Subaddress { + Subaddress({ + required this.id, + required this.label, + required this.address}); + final int id; + final String label; + final String address; +} + +class WowneroBalance extends Balance { + WowneroBalance({required this.fullBalance, required this.unlockedBalance}) + : formattedFullBalance = wownero!.formatterWowneroAmountToString(amount: fullBalance), + formattedUnlockedBalance = + wownero!.formatterWowneroAmountToString(amount: unlockedBalance), + super(unlockedBalance, fullBalance); + + WowneroBalance.fromString( + {required this.formattedFullBalance, + required this.formattedUnlockedBalance}) + : fullBalance = wownero!.formatterWowneroParseAmount(amount: formattedFullBalance), + unlockedBalance = wownero!.formatterWowneroParseAmount(amount: formattedUnlockedBalance), + super(wownero!.formatterWowneroParseAmount(amount: formattedUnlockedBalance), + wownero!.formatterWowneroParseAmount(amount: formattedFullBalance)); + + final int fullBalance; + final int unlockedBalance; + final String formattedFullBalance; + final String formattedUnlockedBalance; + + @override + String get formattedAvailableBalance => formattedUnlockedBalance; + + @override + String get formattedAdditionalBalance => formattedFullBalance; +} + +abstract class WowneroWalletDetails { + @observable + late Account account; + + @observable + late WowneroBalance balance; +} + +abstract class Wownero { + WowneroAccountList getAccountList(Object wallet); + + WowneroSubaddressList getSubaddressList(Object wallet); + + TransactionHistoryBase getTransactionHistory(Object wallet); + + WowneroWalletDetails getWowneroWalletDetails(Object wallet); + + String getTransactionAddress(Object wallet, int accountIndex, int addressIndex); + + String getSubaddressLabel(Object wallet, int accountIndex, int addressIndex); + + int getHeightByDate({required DateTime date}); + TransactionPriority getDefaultTransactionPriority(); + TransactionPriority getWowneroTransactionPrioritySlow(); + TransactionPriority getWowneroTransactionPriorityAutomatic(); + TransactionPriority deserializeWowneroTransactionPriority({required int raw}); + List getTransactionPriorities(); + List getWowneroWordList(String language); + + List getUnspents(Object wallet); + Future updateUnspents(Object wallet); + + Future getCurrentHeight(); + + WalletCredentials createWowneroRestoreWalletFromKeysCredentials({ + required String name, + required String spendKey, + required String viewKey, + required String address, + required String password, + required String language, + required int height}); + WalletCredentials createWowneroRestoreWalletFromSeedCredentials({required String name, required String password, required int height, required String mnemonic}); + WalletCredentials createWowneroNewWalletCredentials({required String name, required String language, required bool isPolyseed, String password}); + Map getKeys(Object wallet); + Object createWowneroTransactionCreationCredentials({required List outputs, required TransactionPriority priority}); + Object createWowneroTransactionCreationCredentialsRaw({required List outputs, required TransactionPriority priority}); + String formatterWowneroAmountToString({required int amount}); + double formatterWowneroAmountToDouble({required int amount}); + int formatterWowneroParseAmount({required String amount}); + Account getCurrentAccount(Object wallet); + void setCurrentAccount(Object wallet, int id, String label, String? balance); + void onStartup(); + int getTransactionInfoAccountId(TransactionInfo tx); + WalletService createWowneroWalletService(Box walletInfoSource, Box unspentCoinSource); + Map pendingTransactionInfo(Object transaction); +} + +abstract class WowneroSubaddressList { + ObservableList get subaddresses; + void update(Object wallet, {required int accountIndex}); + void refresh(Object wallet, {required int accountIndex}); + List getAll(Object wallet); + Future addSubaddress(Object wallet, {required int accountIndex, required String label}); + Future setLabelSubaddress(Object wallet, + {required int accountIndex, required int addressIndex, required String label}); +} + +abstract class WowneroAccountList { + ObservableList get accounts; + void update(Object wallet); + void refresh(Object wallet); + List getAll(Object wallet); + Future addAccount(Object wallet, {required String label}); + Future setLabelAccount(Object wallet, {required int accountIndex, required String label}); +} + """; + + const wowneroEmptyDefinition = 'Wownero? wownero;\n'; + const wowneroCWDefinition = 'Wownero? wownero = CWWownero();\n'; + + final output = '$wowneroCommonHeaders\n' + + (hasImplementation ? '$wowneroCWHeaders\n' : '\n') + + (hasImplementation ? '$wowneroCwPart\n\n' : '\n') + + (hasImplementation ? wowneroCWDefinition : wowneroEmptyDefinition) + + '\n' + + wowneroContent; + + if (outputFile.existsSync()) { + await outputFile.delete(); + } + + await outputFile.writeAsString(output); +} + Future generateHaven(bool hasImplementation) async { final outputFile = File(havenOutputPath); const havenCommonHeaders = """ @@ -1154,18 +1342,20 @@ abstract class Tron { await outputFile.writeAsString(output); } -Future generatePubspec( - {required bool hasMonero, - required bool hasBitcoin, - required bool hasHaven, - required bool hasEthereum, - required bool hasNano, - required bool hasBanano, - required bool hasBitcoinCash, - required bool hasFlutterSecureStorage, - required bool hasPolygon, - required bool hasSolana, - required bool hasTron}) async { +Future generatePubspec({ + required bool hasMonero, + required bool hasBitcoin, + required bool hasHaven, + required bool hasEthereum, + required bool hasNano, + required bool hasBanano, + required bool hasBitcoinCash, + required bool hasFlutterSecureStorage, + required bool hasPolygon, + required bool hasSolana, + required bool hasTron, + required bool hasWownero, +}) async { const cwCore = """ cw_core: path: ./cw_core @@ -1191,8 +1381,8 @@ Future generatePubspec( git: url: https://github.com/cake-tech/flutter_secure_storage.git path: flutter_secure_storage - ref: cake-8.0.0 - version: 8.0.0 + ref: cake-8.1.0 + version: 8.1.0 """; const cwEthereum = """ cw_ethereum: @@ -1226,15 +1416,21 @@ Future generatePubspec( cw_tron: path: ./cw_tron """; + const cwWownero = """ + cw_wownero: + path: ./cw_wownero + """; final inputFile = File(pubspecOutputPath); final inputText = await inputFile.readAsString(); final inputLines = inputText.split('\n'); - final dependenciesIndex = - inputLines.indexWhere((line) => line.toLowerCase().contains('dependencies:')); + final dependenciesIndex = inputLines.indexWhere((line) => Platform.isWindows + // On Windows it could contains `\r` (Carriage Return). It could be fixed in newer dart versions. + ? line.toLowerCase() == 'dependencies:\r' || line.toLowerCase() == 'dependencies:' + : line.toLowerCase() == 'dependencies:'); var output = cwCore; if (hasMonero) { - output += '\n$cwMonero\n$cwSharedExternal'; + output += '\n$cwMonero'; } if (hasBitcoin) { @@ -1269,10 +1465,8 @@ Future generatePubspec( output += '\n$cwTron'; } - if (hasHaven && !hasMonero) { + if (hasHaven) { output += '\n$cwSharedExternal\n$cwHaven'; - } else if (hasHaven) { - output += '\n$cwHaven'; } if (hasFlutterSecureStorage) { @@ -1283,6 +1477,10 @@ Future generatePubspec( output += '\n$cwEVM'; } + if (hasWownero) { + output += '\n$cwWownero'; + } + final outputLines = output.split('\n'); inputLines.insertAll(dependenciesIndex + 1, outputLines); final outputContent = inputLines.join('\n'); @@ -1295,17 +1493,19 @@ Future generatePubspec( await outputFile.writeAsString(outputContent); } -Future generateWalletTypes( - {required bool hasMonero, - required bool hasBitcoin, - required bool hasHaven, - required bool hasEthereum, - required bool hasNano, - required bool hasBanano, - required bool hasBitcoinCash, - required bool hasPolygon, - required bool hasSolana, - required bool hasTron}) async { +Future generateWalletTypes({ + required bool hasMonero, + required bool hasBitcoin, + required bool hasHaven, + required bool hasEthereum, + required bool hasNano, + required bool hasBanano, + required bool hasBitcoinCash, + required bool hasPolygon, + required bool hasSolana, + required bool hasTron, + required bool hasWownero, +}) async { final walletTypesFile = File(walletTypesPath); if (walletTypesFile.existsSync()) { @@ -1356,6 +1556,10 @@ Future generateWalletTypes( outputContent += '\tWalletType.banano,\n'; } + if (hasWownero) { + outputContent += '\tWalletType.wownero,\n'; + } + if (hasHaven) { outputContent += '\tWalletType.haven,\n'; } diff --git a/tool/import_secrets_config.dart b/tool/import_secrets_config.dart index 72a019636..42379021f 100644 --- a/tool/import_secrets_config.dart +++ b/tool/import_secrets_config.dart @@ -16,6 +16,7 @@ const tronOutputPath = 'cw_tron/lib/.secrets.g.dart'; const nanoConfigPath = 'tool/.nano-secrets-config.json'; const nanoOutputPath = 'cw_nano/lib/.secrets.g.dart'; + Future main(List args) async => importSecretsConfig(); Future importSecretsConfig() async { diff --git a/windows/.gitignore b/windows/.gitignore new file mode 100644 index 000000000..d492d0d98 --- /dev/null +++ b/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt new file mode 100644 index 000000000..7d1c03451 --- /dev/null +++ b/windows/CMakeLists.txt @@ -0,0 +1,123 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(cake_wallet LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "CakeWallet") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../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}/../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}/../scripts/monero_c/release/monero/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}/../scripts/monero_c/release/monero/x86_64-w64-mingw32_libpolyseed.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "libpolyseed.dll" + COMPONENT Runtime) + +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../scripts/monero_c/release/monero/x86_64-w64-mingw32_libssp-0.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "libssp-0.dll" + COMPONENT Runtime) + +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../scripts/monero_c/release/monero/x86_64-w64-mingw32_libstdc++-6.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "libstdc++-6.dll" + COMPONENT Runtime) + +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../scripts/monero_c/release/monero/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}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000..903f4899d --- /dev/null +++ b/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..323f53c9f --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,26 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include +#include +#include +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + ConnectivityPlusWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); + FlutterLocalAuthenticationPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterLocalAuthenticationPluginCApi")); + FlutterSecureStorageWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); +} diff --git a/windows/flutter/generated_plugin_registrant.h b/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..dc139d85a --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000..d6d9b0a49 --- /dev/null +++ b/windows/flutter/generated_plugins.cmake @@ -0,0 +1,29 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + connectivity_plus + flutter_local_authentication + flutter_secure_storage_windows + permission_handler_windows + url_launcher_windows +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST + sp_scanner +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/windows/runner/CMakeLists.txt b/windows/runner/CMakeLists.txt new file mode 100644 index 000000000..394917c05 --- /dev/null +++ b/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc new file mode 100644 index 000000000..0a899f86e --- /dev/null +++ b/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.cakewallet.cake_wallet" "\0" + VALUE "FileDescription", "Cake Wallet" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "Cake Wallet" "\0" + VALUE "LegalCopyright", "Copyright (C) 2023 Cake Wallet. All rights reserved." "\0" + VALUE "OriginalFilename", "Cake Wallet.exe" "\0" + VALUE "ProductName", "Cake Wallet" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp new file mode 100644 index 000000000..955ee3038 --- /dev/null +++ b/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/windows/runner/flutter_window.h b/windows/runner/flutter_window.h new file mode 100644 index 000000000..6da0652f0 --- /dev/null +++ b/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp new file mode 100644 index 000000000..a7eecbf9c --- /dev/null +++ b/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"Cake Wallet", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/windows/runner/resource.h b/windows/runner/resource.h new file mode 100644 index 000000000..66a65d1e4 --- /dev/null +++ b/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico new file mode 100644 index 000000000..242fb9cb5 Binary files /dev/null and b/windows/runner/resources/app_icon.ico differ diff --git a/windows/runner/runner.exe.manifest b/windows/runner/runner.exe.manifest new file mode 100644 index 000000000..a42ea7687 --- /dev/null +++ b/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/windows/runner/utils.cpp b/windows/runner/utils.cpp new file mode 100644 index 000000000..b2b08734d --- /dev/null +++ b/windows/runner/utils.cpp @@ -0,0 +1,65 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length <= 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/windows/runner/utils.h b/windows/runner/utils.h new file mode 100644 index 000000000..3879d5475 --- /dev/null +++ b/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/windows/runner/win32_window.cpp b/windows/runner/win32_window.cpp new file mode 100644 index 000000000..60608d0fe --- /dev/null +++ b/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/windows/runner/win32_window.h b/windows/runner/win32_window.h new file mode 100644 index 000000000..e901dde68 --- /dev/null +++ b/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_