From cf466f43f6ed150faa20b10c3b0b24e6ae39bae8 Mon Sep 17 00:00:00 2001 From: 0xskydb Date: Tue, 10 Dec 2024 00:30:34 +0000 Subject: [PATCH 1/4] initial commit for salvium support --- .github/workflows/cache_dependencies.yml | 4 +- .github/workflows/pr_test_build_android.yml | 19 +- .github/workflows/pr_test_build_linux.yml | 2 +- .gitignore | 5 + README.md | 104 +- analysis_options.yaml | 46 +- .../com/cakewallet/salvium/Application.java | 11 + .../com/cakewallet/salvium/MainActivity.java | 83 + assets/salvium_node_list.yml | 3 + cw_core/lib/crypto_currency.dart | 3 + cw_core/lib/currency_for_wallet_type.dart | 4 + cw_core/lib/get_height_by_date.dart | 22 + cw_core/lib/node.dart | 2 + cw_core/lib/wallet_info.dart | 2 +- cw_core/lib/wallet_type.dart | 14 + cw_salvium/.gitignore | 7 + cw_salvium/.metadata | 10 + cw_salvium/CHANGELOG.md | 3 + cw_salvium/LICENSE | 1 + cw_salvium/README.md | 14 + cw_salvium/android/.gitignore | 8 + cw_salvium/android/CMakeLists.txt | 220 +++ cw_salvium/android/build.gradle | 56 + cw_salvium/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + cw_salvium/android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 3 + .../cakewallet/cw_salvium/CwSalviumPlugin.kt | 36 + cw_salvium/ios/.gitignore | 37 + cw_salvium/ios/Assets/.gitkeep | 0 cw_salvium/ios/Classes/CwSalviumPlugin.h | 4 + cw_salvium/ios/Classes/CwSalviumPlugin.m | 15 + .../ios/Classes/SwiftCwSalviumPlugin.swift | 14 + cw_salvium/ios/Classes/salvium_api.cpp | 942 ++++++++++ cw_salvium/ios/cw_salvium.podspec | 50 + cw_salvium/lib/api/account_list.dart | 81 + cw_salvium/lib/api/asset_types.dart | 23 + cw_salvium/lib/api/balance_list.dart | 58 + .../lib/api/convert_utf8_to_string.dart | 8 + cw_salvium/lib/api/cw_salvium.dart | 14 + .../connection_to_node_exception.dart | 5 + .../creation_transaction_exception.dart | 8 + .../exceptions/setup_wallet_exception.dart | 10 + .../exceptions/wallet_creation_exception.dart | 8 + .../exceptions/wallet_opening_exception.dart | 8 + .../wallet_restore_from_keys_exception.dart | 10 + .../wallet_restore_from_seed_exception.dart | 5 + cw_salvium/lib/api/monero_output.dart | 8 + cw_salvium/lib/api/salvium_api.dart | 6 + cw_salvium/lib/api/signatures.dart | 144 ++ cw_salvium/lib/api/structs/account_row.dart | 12 + .../lib/api/structs/pending_transaction.dart | 27 + .../lib/api/structs/salvium_balance_row.dart | 12 + cw_salvium/lib/api/structs/salvium_rate.dart | 12 + .../lib/api/structs/subaddress_row.dart | 15 + .../lib/api/structs/transaction_info_row.dart | 44 + cw_salvium/lib/api/structs/ut8_box.dart | 8 + cw_salvium/lib/api/subaddress_list.dart | 97 + cw_salvium/lib/api/transaction_history.dart | 246 +++ cw_salvium/lib/api/types.dart | 142 ++ cw_salvium/lib/api/wallet.dart | 376 ++++ cw_salvium/lib/api/wallet_manager.dart | 248 +++ .../lib/mnemonics/chinese_simplified.dart | 1630 +++++++++++++++++ cw_salvium/lib/mnemonics/dutch.dart | 1630 +++++++++++++++++ cw_salvium/lib/mnemonics/english.dart | 1630 +++++++++++++++++ cw_salvium/lib/mnemonics/french.dart | 1630 +++++++++++++++++ cw_salvium/lib/mnemonics/german.dart | 1630 +++++++++++++++++ cw_salvium/lib/mnemonics/italian.dart | 1630 +++++++++++++++++ cw_salvium/lib/mnemonics/japanese.dart | 1630 +++++++++++++++++ cw_salvium/lib/mnemonics/portuguese.dart | 1630 +++++++++++++++++ cw_salvium/lib/mnemonics/russian.dart | 1630 +++++++++++++++++ cw_salvium/lib/mnemonics/spanish.dart | 1630 +++++++++++++++++ .../lib/pending_salvium_transaction.dart | 56 + cw_salvium/lib/salvium_account_list.dart | 84 + cw_salvium/lib/salvium_balance.dart | 34 + cw_salvium/lib/salvium_subaddress_list.dart | 86 + ...vium_transaction_creation_credentials.dart | 13 + ...alvium_transaction_creation_exception.dart | 8 + .../lib/salvium_transaction_history.dart | 27 + cw_salvium/lib/salvium_transaction_info.dart | 73 + cw_salvium/lib/salvium_wallet.dart | 429 +++++ cw_salvium/lib/salvium_wallet_addresses.dart | 95 + cw_salvium/lib/salvium_wallet_service.dart | 258 +++ cw_salvium/lib/update_salvium_rate.dart | 15 + cw_salvium/pubspec.lock | 773 ++++++++ cw_salvium/pubspec.yaml | 81 + cw_shared_external/README.md | 5 +- .../ios/cw_shared_external.podspec | 4 +- cw_shared_external/pubspec.yaml | 4 +- lib/core/seed_validator.dart | 3 + lib/core/wallet_creation_service.dart | 1 + lib/di.dart | 7 +- lib/entities/default_settings_migration.dart | 52 +- lib/entities/ens_record.dart | 2 + lib/entities/node_list.dart | 18 + lib/entities/parse_address_from_domain.dart | 2 +- lib/entities/preferences_key.dart | 2 + lib/entities/priority_for_wallet_type.dart | 3 + lib/entities/provider_types.dart | 2 + lib/entities/update_salvium_rate.dart | 26 + lib/reactions/bip39_wallet_utils.dart | 1 + lib/reactions/fiat_rate_update.dart | 3 + lib/reactions/on_current_wallet_change.dart | 5 + .../on_wallet_sync_status_change.dart | 5 + lib/salvium/cw_salvium.dart | 331 ++++ lib/src/screens/dashboard/dashboard_page.dart | 21 + .../desktop_wallet_selection_dropdown.dart | 3 + .../dashboard/pages/cake_features_page.dart | 1 + .../dashboard/widgets/menu_widget.dart | 4 + .../new_wallet/new_wallet_type_page.dart | 2 +- .../screens/receive/widgets/address_list.dart | 3 +- .../screens/restore/wallet_restore_page.dart | 5 +- .../screens/wallet_list/wallet_list_page.dart | 1 + .../welcome/create_pin_welcome_page.dart | 8 + .../widgets/salvium_wallet_removal_popup.dart | 91 + lib/store/settings_store.dart | 35 + .../advanced_privacy_settings_view_model.dart | 1 + .../contact_list/contact_list_view_model.dart | 6 +- .../dashboard/balance_view_model.dart | 2 + .../dashboard/dashboard_view_model.dart | 16 +- .../dashboard/receive_option_view_model.dart | 3 + lib/view_model/dashboard/sign_view_model.dart | 1 + .../dashboard/transaction_list_item.dart | 14 + .../exchange/exchange_view_model.dart | 7 + ...ero_account_edit_or_create_view_model.dart | 34 +- .../monero_account_list_view_model.dart | 16 + .../node_create_or_edit_view_model.dart | 3 +- .../node_list/node_list_view_model.dart | 3 + lib/view_model/send/output.dart | 11 + .../send/send_template_view_model.dart | 1 + lib/view_model/send/send_view_model.dart | 8 +- .../transaction_details_view_model.dart | 38 + ...let_address_edit_or_create_view_model.dart | 13 + .../wallet_address_list_view_model.dart | 43 +- lib/view_model/wallet_keys_view_model.dart | 52 + lib/view_model/wallet_new_vm.dart | 6 +- lib/view_model/wallet_restore_view_model.dart | 20 +- lib/wallet_type_utils.dart | 9 + model_generator.sh | 1 + pubspec_base.yaml | 17 +- scripts/android/app_env.sh | 16 +- scripts/android/app_icon.sh | 5 + scripts/android/build_all.sh | 2 + scripts/android/build_salvium.sh | 70 + scripts/android/build_salvium_all.sh | 8 + scripts/android/copy_monero_deps.sh | 3 + scripts/android/pubspec_gen.sh | 2 +- scripts/docker/Dockerfile | 5 + scripts/docker/build_all.sh | 2 + scripts/docker/build_salvium.sh | 71 + scripts/docker/build_salvium_all.sh | 9 + scripts/docker/copy_salvium_deps.sh | 46 + scripts/docker/entrypoint.sh | 4 +- scripts/ios/app_config.sh | 2 +- scripts/ios/app_env.sh | 14 +- scripts/ios/app_icon.sh | 4 + scripts/ios/build_all.sh | 1 + scripts/ios/build_salvium.sh | 64 + scripts/ios/build_salvium_all.sh | 9 + scripts/ios/setup.sh | 14 +- scripts/macos/app_config.sh | 2 +- scripts/macos/build_salvium.sh | 50 + scripts/macos/setup.sh | 21 +- tool/configure.dart | 184 ++ 164 files changed, 23174 insertions(+), 129 deletions(-) create mode 100644 android/app/src/main/java/com/cakewallet/salvium/Application.java create mode 100644 android/app/src/main/java/com/cakewallet/salvium/MainActivity.java create mode 100644 assets/salvium_node_list.yml create mode 100644 cw_salvium/.gitignore create mode 100644 cw_salvium/.metadata create mode 100644 cw_salvium/CHANGELOG.md create mode 100644 cw_salvium/LICENSE create mode 100644 cw_salvium/README.md create mode 100644 cw_salvium/android/.gitignore create mode 100644 cw_salvium/android/CMakeLists.txt create mode 100644 cw_salvium/android/build.gradle create mode 100644 cw_salvium/android/gradle.properties create mode 100644 cw_salvium/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 cw_salvium/android/settings.gradle create mode 100644 cw_salvium/android/src/main/AndroidManifest.xml create mode 100644 cw_salvium/android/src/main/kotlin/com/cakewallet/cw_salvium/CwSalviumPlugin.kt create mode 100644 cw_salvium/ios/.gitignore create mode 100644 cw_salvium/ios/Assets/.gitkeep create mode 100644 cw_salvium/ios/Classes/CwSalviumPlugin.h create mode 100644 cw_salvium/ios/Classes/CwSalviumPlugin.m create mode 100644 cw_salvium/ios/Classes/SwiftCwSalviumPlugin.swift create mode 100644 cw_salvium/ios/Classes/salvium_api.cpp create mode 100644 cw_salvium/ios/cw_salvium.podspec create mode 100644 cw_salvium/lib/api/account_list.dart create mode 100644 cw_salvium/lib/api/asset_types.dart create mode 100644 cw_salvium/lib/api/balance_list.dart create mode 100644 cw_salvium/lib/api/convert_utf8_to_string.dart create mode 100644 cw_salvium/lib/api/cw_salvium.dart create mode 100644 cw_salvium/lib/api/exceptions/connection_to_node_exception.dart create mode 100644 cw_salvium/lib/api/exceptions/creation_transaction_exception.dart create mode 100644 cw_salvium/lib/api/exceptions/setup_wallet_exception.dart create mode 100644 cw_salvium/lib/api/exceptions/wallet_creation_exception.dart create mode 100644 cw_salvium/lib/api/exceptions/wallet_opening_exception.dart create mode 100644 cw_salvium/lib/api/exceptions/wallet_restore_from_keys_exception.dart create mode 100644 cw_salvium/lib/api/exceptions/wallet_restore_from_seed_exception.dart create mode 100644 cw_salvium/lib/api/monero_output.dart create mode 100644 cw_salvium/lib/api/salvium_api.dart create mode 100644 cw_salvium/lib/api/signatures.dart create mode 100644 cw_salvium/lib/api/structs/account_row.dart create mode 100644 cw_salvium/lib/api/structs/pending_transaction.dart create mode 100644 cw_salvium/lib/api/structs/salvium_balance_row.dart create mode 100644 cw_salvium/lib/api/structs/salvium_rate.dart create mode 100644 cw_salvium/lib/api/structs/subaddress_row.dart create mode 100644 cw_salvium/lib/api/structs/transaction_info_row.dart create mode 100644 cw_salvium/lib/api/structs/ut8_box.dart create mode 100644 cw_salvium/lib/api/subaddress_list.dart create mode 100644 cw_salvium/lib/api/transaction_history.dart create mode 100644 cw_salvium/lib/api/types.dart create mode 100644 cw_salvium/lib/api/wallet.dart create mode 100644 cw_salvium/lib/api/wallet_manager.dart create mode 100644 cw_salvium/lib/mnemonics/chinese_simplified.dart create mode 100644 cw_salvium/lib/mnemonics/dutch.dart create mode 100644 cw_salvium/lib/mnemonics/english.dart create mode 100644 cw_salvium/lib/mnemonics/french.dart create mode 100644 cw_salvium/lib/mnemonics/german.dart create mode 100644 cw_salvium/lib/mnemonics/italian.dart create mode 100644 cw_salvium/lib/mnemonics/japanese.dart create mode 100644 cw_salvium/lib/mnemonics/portuguese.dart create mode 100644 cw_salvium/lib/mnemonics/russian.dart create mode 100644 cw_salvium/lib/mnemonics/spanish.dart create mode 100644 cw_salvium/lib/pending_salvium_transaction.dart create mode 100644 cw_salvium/lib/salvium_account_list.dart create mode 100644 cw_salvium/lib/salvium_balance.dart create mode 100644 cw_salvium/lib/salvium_subaddress_list.dart create mode 100644 cw_salvium/lib/salvium_transaction_creation_credentials.dart create mode 100644 cw_salvium/lib/salvium_transaction_creation_exception.dart create mode 100644 cw_salvium/lib/salvium_transaction_history.dart create mode 100644 cw_salvium/lib/salvium_transaction_info.dart create mode 100644 cw_salvium/lib/salvium_wallet.dart create mode 100644 cw_salvium/lib/salvium_wallet_addresses.dart create mode 100644 cw_salvium/lib/salvium_wallet_service.dart create mode 100644 cw_salvium/lib/update_salvium_rate.dart create mode 100644 cw_salvium/pubspec.lock create mode 100644 cw_salvium/pubspec.yaml create mode 100644 lib/entities/update_salvium_rate.dart create mode 100644 lib/salvium/cw_salvium.dart create mode 100644 lib/src/widgets/salvium_wallet_removal_popup.dart create mode 100644 scripts/android/build_salvium.sh create mode 100644 scripts/android/build_salvium_all.sh create mode 100644 scripts/docker/build_salvium.sh create mode 100644 scripts/docker/build_salvium_all.sh create mode 100644 scripts/docker/copy_salvium_deps.sh create mode 100644 scripts/ios/build_salvium.sh create mode 100644 scripts/ios/build_salvium_all.sh create mode 100644 scripts/macos/build_salvium.sh diff --git a/.github/workflows/cache_dependencies.yml b/.github/workflows/cache_dependencies.yml index 938027e81f..da52af600b 100644 --- a/.github/workflows/cache_dependencies.yml +++ b/.github/workflows/cache_dependencies.yml @@ -3,11 +3,10 @@ name: Cache Dependencies on: workflow_dispatch: push: - branches: [ main ] + branches: [main] jobs: test: - runs-on: ubuntu-20.04 steps: @@ -60,6 +59,7 @@ jobs: with: path: | /opt/android/cake_wallet/cw_haven/android/.cxx + /opt/android/cake_wallet/cw_salvium/android/.cxx /opt/android/cake_wallet/scripts/monero_c/release key: ${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh' ,'**/cache_dependencies.yml') }} - if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }} diff --git a/.github/workflows/pr_test_build_android.yml b/.github/workflows/pr_test_build_android.yml index 9df11f7abd..b06b272d63 100644 --- a/.github/workflows/pr_test_build_android.yml +++ b/.github/workflows/pr_test_build_android.yml @@ -81,6 +81,7 @@ jobs: with: path: | /opt/android/cake_wallet/cw_haven/android/.cxx + /opt/android/cake_wallet/cw_salvium/android/.cxx /opt/android/cake_wallet/scripts/monero_c/release key: ${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh' ,'**/cache_dependencies.yml') }} @@ -96,7 +97,6 @@ jobs: cd /opt/android/cake_wallet flutter pub get - - name: Install go and gomobile run: | # install go > 1.23: @@ -115,14 +115,14 @@ jobs: cd /opt/android/cake_wallet/scripts/android/ ./build_mwebd.sh --dont-install -# - name: Cache Keystore -# id: cache-keystore -# uses: actions/cache@v3 -# with: -# path: /opt/android/cake_wallet/android/app/key.jks -# key: $STORE_PASS -# -# - if: ${{ steps.cache-keystore.outputs.cache-hit != 'true' }} + # - name: Cache Keystore + # id: cache-keystore + # uses: actions/cache@v3 + # with: + # path: /opt/android/cake_wallet/android/app/key.jks + # key: $STORE_PASS + # + # - if: ${{ steps.cache-keystore.outputs.cache-hit != 'true' }} - name: Generate KeyStore run: | cd /opt/android/cake_wallet/android/app @@ -288,4 +288,3 @@ jobs: title: "${{ env.BRANCH_NAME }}.apk" filename: ${{ env.BRANCH_NAME }}.apk initial_comment: ${{ github.event.head_commit.message }} - diff --git a/.github/workflows/pr_test_build_linux.yml b/.github/workflows/pr_test_build_linux.yml index c37b115821..33166d2617 100644 --- a/.github/workflows/pr_test_build_linux.yml +++ b/.github/workflows/pr_test_build_linux.yml @@ -74,6 +74,7 @@ jobs: with: path: | /opt/android/cake_wallet/cw_haven/android/.cxx + /opt/android/cake_wallet/cw_salvium/android/.cxx /opt/android/cake_wallet/scripts/monero_c/release key: linux_${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh' ,'**/cache_dependencies.yml') }} @@ -200,7 +201,6 @@ jobs: uses: kittaakos/upload-artifact-as-is@v0 with: path: /opt/android/cake_wallet/build/linux/x64/release/${{env.BRANCH_NAME}}.zip - # Just as an artifact would be enough # - name: Send Test APK # continue-on-error: true diff --git a/.gitignore b/.gitignore index 970241189f..c87b01d2eb 100644 --- a/.gitignore +++ b/.gitignore @@ -126,10 +126,15 @@ cw_shared_external/ios/External/ cw_haven/ios/External/ cw_haven/android/.externalNativeBuild/ cw_haven/android/.cxx/ +# cw_salvium/** +cw_salvium/ios/External/ +cw_salvium/android/.externalNativeBuild/ +cw_salvium/android/.cxx/ lib/bitcoin/bitcoin.dart lib/monero/monero.dart lib/haven/haven.dart +lib/salvium/salvium.dart lib/ethereum/ethereum.dart lib/bitcoin_cash/bitcoin_cash.dart lib/nano/nano.dart diff --git a/README.md b/README.md index ea8f346246..1b5f11ec6f 100644 --- a/README.md +++ b/README.md @@ -21,83 +21,89 @@ [Cake Wallet](https://cakewallet.com) is an open-source, non-custodial, and private multi-currency crypto wallet for Android, iOS, macOS, and Linux. Cake Wallet includes support for several cryptocurrencies, including: -* Monero (XMR) -* Bitcoin (BTC) -* Ethereum (ETH) -* Litecoin (LTC) -* Bitcoin Cash (BCH) -* Polygon (Pol) -* Solana (SOL) -* Nano (XNO) -* Haven (XHV) + +- Monero (XMR) +- Bitcoin (BTC) +- Ethereum (ETH) +- Litecoin (LTC) +- Bitcoin Cash (BCH) +- Polygon (Pol) +- Solana (SOL) +- Nano (XNO) +- Haven (XHV) +- Salvium (XRM) ## Features ### App-Wide Features -* Completely noncustodial. *Your keys, your coins.* -* Built-in exchange for dozens of pairs -* Easily pay cryptocurrency invoices with fixed rate exchanges -* Buy cryptocurrency (BTC/LTC/XMR/ETH) with credit/debit/bank -* Sell cryptocurrency by bank transfer -* Scan QR codes for easy cryptocurrency transfers -* Create several wallets -* Select your own custom nodes/servers -* Address book -* Backup to an external location or iCloud -* Send to OpenAlias, Unstoppable Domains, Yats, and FIO Crypto Handles -* Set desired network fee level -* Store local transaction notes -* Extremely simple user experience -* Convenient exchange and sending templates for recurring payments -* Create donation links and invoices in the receive screen -* Robust privacy settings (eg: Tor-only connections) -* Robust security settings (eg: Cake 2FA) +- Completely noncustodial. _Your keys, your coins._ +- Built-in exchange for dozens of pairs +- Easily pay cryptocurrency invoices with fixed rate exchanges +- Buy cryptocurrency (BTC/LTC/XMR/ETH) with credit/debit/bank +- Sell cryptocurrency by bank transfer +- Scan QR codes for easy cryptocurrency transfers +- Create several wallets +- Select your own custom nodes/servers +- Address book +- Backup to an external location or iCloud +- Send to OpenAlias, Unstoppable Domains, Yats, and FIO Crypto Handles +- Set desired network fee level +- Store local transaction notes +- Extremely simple user experience +- Convenient exchange and sending templates for recurring payments +- Create donation links and invoices in the receive screen +- Robust privacy settings (eg: Tor-only connections) +- Robust security settings (eg: Cake 2FA) ### Monero Specific Features -* The Monero view key is retained on the device for maximum privacy -* Full support for Monero subaddresses and accounts -* Specify restore height for faster syncing -* Specify multiple recipients for batch sending -* Optionally set Monero nodes as trusted for faster syncing -* Specify a proxy for Monero nodes, compatible with Tor and i2p +- The Monero view key is retained on the device for maximum privacy +- Full support for Monero subaddresses and accounts +- Specify restore height for faster syncing +- Specify multiple recipients for batch sending +- Optionally set Monero nodes as trusted for faster syncing +- Specify a proxy for Monero nodes, compatible with Tor and i2p ### Bitcoin Specific Features -* Bitcoin coin control (specify specific outputs to spend) -* Automatically generate new addresses -* Specify multiple recipients for batch sending +- Bitcoin coin control (specify specific outputs to spend) +- Automatically generate new addresses +- Specify multiple recipients for batch sending ### Ethereum Specific Features -* Store ETH and all ERc-20 tokens -* Add custom tokens by contract address -* Enable or disable Etherscan for transaction history +- Store ETH and all ERc-20 tokens +- Add custom tokens by contract address +- Enable or disable Etherscan for transaction history ### Litecoin Specific Features -* Litecoin coin control (specify specific outputs to spend) -* Automatically generate new addresses -* Specify multiple recipients for batch sending +- Litecoin coin control (specify specific outputs to spend) +- Automatically generate new addresses +- Specify multiple recipients for batch sending ### Haven Specific Features -* Send, receive, and store XHV and all xAssets like xUSD, xEUR, xAG, etc. +- Send, receive, and store XHV and all xAssets like xUSD, xEUR, xAG, etc. + +### Salvium Specific Features + +- Add Salvium specfic features here # Monero.com by Cake Wallet for Android and iOS ## Open Source Monero-Only Wallet -*Exchanging to/from other assets is also supported.* +_Exchanging to/from other assets is also supported._ ## Links -* Website: https://monero.com -* App Store (iOS): https://apps.apple.com/app/id1601990386 -* Google Play: https://play.google.com/store/apps/details?id=com.monero.app -* F-Droid: https://fdroid.cakelabs.com -* APK: https://github.com/cake-tech/cake_wallet/releases +- Website: https://monero.com +- App Store (iOS): https://apps.apple.com/app/id1601990386 +- Google Play: https://play.google.com/store/apps/details?id=com.monero.app +- F-Droid: https://fdroid.cakelabs.com +- APK: https://github.com/cake-tech/cake_wallet/releases ### APK Verification diff --git a/analysis_options.yaml b/analysis_options.yaml index be68a4f26d..830f09f821 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,27 +1,29 @@ include: package:lints/recommended.yaml - analyzer: - exclude: [ - build/**, - lib/**.g.dart, - cw_core/lib/**.g.dart, - cw_haven/lib/**.g.dart, - cw_monero/lib/**.g.dart, - lib/generated/*.dart, - cw_monero/ios/External/**, - cw_shared_external/**, - shared_external/**, - lib/bitcoin/cw_bitcoin.dart, - lib/bitcoin_cash/cw_bitcoin_cash.dart, - lib/ethereum/cw_ethereum.dart, - lib/haven/cw_haven.dart, - lib/monero/cw_monero.dart, - lib/nano/cw_nano.dart, - lib/polygon/cw_polygon.dart, - lib/solana/cw_solana.dart, - lib/tron/cw_tron.dart, - lib/wownero/cw_wownero.dart, + exclude: + [ + build/**, + lib/**.g.dart, + cw_core/lib/**.g.dart, + cw_haven/lib/**.g.dart, + cw_salvium/lib/**.g.dart, + cw_monero/lib/**.g.dart, + lib/generated/*.dart, + cw_monero/ios/External/**, + cw_shared_external/**, + shared_external/**, + lib/bitcoin/cw_bitcoin.dart, + lib/bitcoin_cash/cw_bitcoin_cash.dart, + lib/ethereum/cw_ethereum.dart, + lib/haven/cw_haven.dart, + lib/salvium/cw_salvium.dart, + lib/monero/cw_monero.dart, + lib/nano/cw_nano.dart, + lib/polygon/cw_polygon.dart, + lib/solana/cw_solana.dart, + lib/tron/cw_tron.dart, + lib/wownero/cw_wownero.dart, ] language: strict-casts: true @@ -32,8 +34,6 @@ linter: - cancel_subscriptions - always_declare_return_types - prefer_final_fields - - # analyzer: # strong-mode: # implicit-casts: false diff --git a/android/app/src/main/java/com/cakewallet/salvium/Application.java b/android/app/src/main/java/com/cakewallet/salvium/Application.java new file mode 100644 index 0000000000..cc75a58284 --- /dev/null +++ b/android/app/src/main/java/com/cakewallet/salvium/Application.java @@ -0,0 +1,11 @@ +package com.cakewallet.salvium; + +import io.flutter.app.FlutterApplication; +import io.flutter.plugin.common.PluginRegistry; +import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback; +import io.flutter.plugins.GeneratedPluginRegistrant; + +public class Application extends FlutterApplication implements PluginRegistrantCallback { + @Override + public void registerWith(PluginRegistry registry) {} +} \ No newline at end of file diff --git a/android/app/src/main/java/com/cakewallet/salvium/MainActivity.java b/android/app/src/main/java/com/cakewallet/salvium/MainActivity.java new file mode 100644 index 0000000000..dd1e06b3b5 --- /dev/null +++ b/android/app/src/main/java/com/cakewallet/salvium/MainActivity.java @@ -0,0 +1,83 @@ +package com.cakewallet.salvium; + +import androidx.annotation.NonNull; + +import io.flutter.embedding.android.FlutterFragmentActivity; +import io.flutter.embedding.engine.FlutterEngine; +import io.flutter.plugins.GeneratedPluginRegistrant; + +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; + +import android.os.AsyncTask; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.view.WindowManager; +import android.content.Intent; +import android.net.Uri; +import android.os.PowerManager; +import android.provider.Settings; + +import java.security.SecureRandom; + +public class MainActivity extends FlutterFragmentActivity { + final String UTILS_CHANNEL = "com.cake_wallet/native_utils"; + + @Override + public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { + GeneratedPluginRegistrant.registerWith(flutterEngine); + + MethodChannel utilsChannel = + new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), + UTILS_CHANNEL); + + utilsChannel.setMethodCallHandler(this::handle); + } + + private void handle(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { + Handler handler = new Handler(Looper.getMainLooper()); + + try { + switch (call.method) { + case "sec_random": + int count = call.argument("count"); + SecureRandom random = new SecureRandom(); + byte bytes[] = new byte[count]; + random.nextBytes(bytes); + handler.post(() -> result.success(bytes)); + break; + case "disableBatteryOptimization": + disableBatteryOptimization(); + handler.post(() -> result.success(null)); + break; + case "isBatteryOptimizationDisabled": + boolean isDisabled = isBatteryOptimizationDisabled(); + handler.post(() -> result.success(isDisabled)); + break; + default: + handler.post(() -> result.notImplemented()); + } + } catch (Exception e) { + handler.post(() -> result.error("UNCAUGHT_ERROR", e.getMessage(), null)); + } + } + + private void disableBatteryOptimization() { + String packageName = getPackageName(); + PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); + if (!pm.isIgnoringBatteryOptimizations(packageName)) { + Intent intent = new Intent(); + intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); + intent.setData(Uri.parse("package:" + packageName)); + startActivity(intent); + } + } + + private boolean isBatteryOptimizationDisabled() { + String packageName = getPackageName(); + PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); + return pm.isIgnoringBatteryOptimizations(packageName); + } + +} \ No newline at end of file diff --git a/assets/salvium_node_list.yml b/assets/salvium_node_list.yml new file mode 100644 index 0000000000..8a5d47a9b0 --- /dev/null +++ b/assets/salvium_node_list.yml @@ -0,0 +1,3 @@ +- uri: nodes.salvium.org:443 + useSSL: true + is_default: true diff --git a/cw_core/lib/crypto_currency.dart b/cw_core/lib/crypto_currency.dart index 0280bb45af..ad6bc31a17 100644 --- a/cw_core/lib/crypto_currency.dart +++ b/cw_core/lib/crypto_currency.dart @@ -107,6 +107,7 @@ class CryptoCurrency extends EnumerableItem with Serializable implemen CryptoCurrency.tbtc, CryptoCurrency.wow, CryptoCurrency.ton, + CryptoCurrency.salvium, ]; static const havenCurrencies = [ @@ -226,6 +227,8 @@ class CryptoCurrency extends EnumerableItem with Serializable implemen static const wow = CryptoCurrency(title: 'WOW', fullName: 'Wownero', raw: 94, name: 'wow', iconPath: 'assets/images/wownero_icon.png', decimals: 11); static const ton = CryptoCurrency(title: 'TON', fullName: 'Toncoin', raw: 95, name: 'ton', iconPath: 'assets/images/ton_icon.png', decimals: 8); + static const salvium = CryptoCurrency(title: 'SAL', fullName: 'Salvium', raw: 96, name: 'sal', iconPath: 'assets/images/sal_logo.png', decimals: 12); + static final Map _rawCurrencyMap = [...all, ...havenCurrencies].fold>({}, (acc, item) { diff --git a/cw_core/lib/currency_for_wallet_type.dart b/cw_core/lib/currency_for_wallet_type.dart index af6037a3b4..fb5d753ea5 100644 --- a/cw_core/lib/currency_for_wallet_type.dart +++ b/cw_core/lib/currency_for_wallet_type.dart @@ -14,6 +14,8 @@ CryptoCurrency currencyForWalletType(WalletType type, {bool? isTestnet}) { return CryptoCurrency.ltc; case WalletType.haven: return CryptoCurrency.xhv; + case WalletType.salvium: + return CryptoCurrency.salvium; case WalletType.ethereum: return CryptoCurrency.eth; case WalletType.bitcoinCash: @@ -46,6 +48,8 @@ WalletType? walletTypeForCurrency(CryptoCurrency currency) { return WalletType.litecoin; case CryptoCurrency.xhv: return WalletType.haven; + case CryptoCurrency.salvium: + return WalletType.salvium; case CryptoCurrency.eth: return WalletType.ethereum; case CryptoCurrency.bch: diff --git a/cw_core/lib/get_height_by_date.dart b/cw_core/lib/get_height_by_date.dart index 7929d7cbd1..43c065805c 100644 --- a/cw_core/lib/get_height_by_date.dart +++ b/cw_core/lib/get_height_by_date.dart @@ -243,6 +243,28 @@ Future getHavenCurrentHeight() async { } } +const salviumDates = { + +}; + +int getSalviumHeightByDate({required DateTime date}) { + String closestKey = + salviumDates.keys.firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => ''); + + return salviumDates[closestKey] ?? 0; +} + +Future getSalviumCurrentHeight() async { + final response = await http.get(Uri.parse('https://explorer.salvium.org/api/networkinfo')); + + if (response.statusCode == 200) { + final info = jsonDecode(response.body); + return info['data']['height'] as int; + } else { + throw Exception('Failed to load current blockchain height'); + } +} + // Data taken from https://timechaincalendar.com/ const bitcoinDates = { "2024-08": 854889, diff --git a/cw_core/lib/node.dart b/cw_core/lib/node.dart index 18d2ffc443..4d44ebbf53 100644 --- a/cw_core/lib/node.dart +++ b/cw_core/lib/node.dart @@ -90,6 +90,7 @@ class Node extends HiveObject with Keyable { switch (type) { case WalletType.monero: case WalletType.haven: + case WalletType.salvium: case WalletType.wownero: return Uri.http(uriRaw, ''); case WalletType.bitcoin: @@ -155,6 +156,7 @@ class Node extends HiveObject with Keyable { switch (type) { case WalletType.monero: case WalletType.haven: + case WalletType.salvium: case WalletType.wownero: return requestMoneroNode(); case WalletType.nano: diff --git a/cw_core/lib/wallet_info.dart b/cw_core/lib/wallet_info.dart index bd035e30a7..375163e5c8 100644 --- a/cw_core/lib/wallet_info.dart +++ b/cw_core/lib/wallet_info.dart @@ -210,7 +210,7 @@ class WalletInfo extends HiveObject { bool get isShowIntroCakePayCard { if (showIntroCakePayCard == null) { - return type != WalletType.haven; + return type != WalletType.haven && type != WalletType.salvium; } return showIntroCakePayCard!; } diff --git a/cw_core/lib/wallet_type.dart b/cw_core/lib/wallet_type.dart index e3957b4e7a..53fd64d1de 100644 --- a/cw_core/lib/wallet_type.dart +++ b/cw_core/lib/wallet_type.dart @@ -9,6 +9,7 @@ const walletTypes = [ WalletType.bitcoin, WalletType.litecoin, WalletType.haven, + WalletType.salvium, WalletType.ethereum, WalletType.bitcoinCash, WalletType.nano, @@ -58,6 +59,9 @@ enum WalletType { @HiveField(12) wownero, + + @HiveField(13) + salvium, } int serializeToInt(WalletType type) { @@ -86,6 +90,8 @@ int serializeToInt(WalletType type) { return 10; case WalletType.wownero: return 11; + case WalletType.salvium: + return 12; case WalletType.none: return -1; } @@ -117,6 +123,8 @@ WalletType deserializeFromInt(int raw) { return WalletType.tron; case 11: return WalletType.wownero; + case 12: + return WalletType.salvium; default: throw Exception('Unexpected token: $raw for WalletType deserializeFromInt'); } @@ -148,6 +156,8 @@ String walletTypeToString(WalletType type) { return 'Tron'; case WalletType.wownero: return 'Wownero'; + case WalletType.salvium: + return 'Salvium'; case WalletType.none: return ''; } @@ -179,6 +189,8 @@ String walletTypeToDisplayName(WalletType type) { return 'Tron (TRX)'; case WalletType.wownero: return 'Wownero (WOW)'; + case WalletType.salvium: + return 'Salvium (SAL)'; case WalletType.none: return ''; } @@ -213,6 +225,8 @@ CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = fal return CryptoCurrency.trx; case WalletType.wownero: return CryptoCurrency.wow; + case WalletType.salvium: + return CryptoCurrency.salvium; case WalletType.none: throw Exception( 'Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency'); diff --git a/cw_salvium/.gitignore b/cw_salvium/.gitignore new file mode 100644 index 0000000000..e9dc58d3d6 --- /dev/null +++ b/cw_salvium/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +.dart_tool/ + +.packages +.pub/ + +build/ diff --git a/cw_salvium/.metadata b/cw_salvium/.metadata new file mode 100644 index 0000000000..cb1a29e7c8 --- /dev/null +++ b/cw_salvium/.metadata @@ -0,0 +1,10 @@ +# 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 and should not be manually edited. + +version: + revision: 4d7946a68d26794349189cf21b3f68cc6fe61dcb + channel: stable + +project_type: plugin diff --git a/cw_salvium/CHANGELOG.md b/cw_salvium/CHANGELOG.md new file mode 100644 index 0000000000..41cc7d8192 --- /dev/null +++ b/cw_salvium/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/cw_salvium/LICENSE b/cw_salvium/LICENSE new file mode 100644 index 0000000000..ba75c69f7f --- /dev/null +++ b/cw_salvium/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/cw_salvium/README.md b/cw_salvium/README.md new file mode 100644 index 0000000000..9b0a6c8a5e --- /dev/null +++ b/cw_salvium/README.md @@ -0,0 +1,14 @@ +# cw_salvium + +A new flutter plugin project. + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/developing-packages/), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/cw_salvium/android/.gitignore b/cw_salvium/android/.gitignore new file mode 100644 index 0000000000..c6cbe562a4 --- /dev/null +++ b/cw_salvium/android/.gitignore @@ -0,0 +1,8 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures diff --git a/cw_salvium/android/CMakeLists.txt b/cw_salvium/android/CMakeLists.txt new file mode 100644 index 0000000000..697ca16387 --- /dev/null +++ b/cw_salvium/android/CMakeLists.txt @@ -0,0 +1,220 @@ +cmake_minimum_required(VERSION 3.4.1) + +add_library( cw_salvium + SHARED + ../ios/Classes/salvium_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) + +############# +# Salvium +############# + +add_library(wallet_api STATIC IMPORTED) +set_target_properties(wallet_api PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libwallet_api.a) + +add_library(wallet STATIC IMPORTED) +set_target_properties(wallet PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libwallet.a) + +add_library(cryptonote_core STATIC IMPORTED) +set_target_properties(cryptonote_core PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libcryptonote_core.a) + +add_library(cryptonote_basic STATIC IMPORTED) +set_target_properties(cryptonote_basic PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libcryptonote_basic.a) + +add_library(mnemonics STATIC IMPORTED) +set_target_properties(mnemonics PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libmnemonics.a) + +add_library(common STATIC IMPORTED) +set_target_properties(common PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libcommon.a) + +add_library(cncrypto STATIC IMPORTED) +set_target_properties(cncrypto PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libcncrypto.a) + +add_library(ringct STATIC IMPORTED) +set_target_properties(ringct PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libringct.a) + +add_library(ringct_basic STATIC IMPORTED) +set_target_properties(ringct_basic PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libringct_basic.a) + +add_library(blockchain_db STATIC IMPORTED) +set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libblockchain_db.a) + +add_library(lmdb STATIC IMPORTED) +set_target_properties(lmdb PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/liblmdb.a) + +add_library(easylogging STATIC IMPORTED) +set_target_properties(easylogging PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libeasylogging.a) + +add_library(unbound STATIC IMPORTED) +set_target_properties(unbound PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libunbound.a) + +add_library(epee STATIC IMPORTED) +set_target_properties(epee PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libepee.a) + +add_library(checkpoints STATIC IMPORTED) +set_target_properties(checkpoints PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libcheckpoints.a) + +add_library(device STATIC IMPORTED) +set_target_properties(device PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libdevice.a) + +add_library(device_trezor STATIC IMPORTED) +set_target_properties(device_trezor PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libdevice_trezor.a) + +add_library(multisig STATIC IMPORTED) +set_target_properties(multisig PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libmultisig.a) + +add_library(version STATIC IMPORTED) +set_target_properties(version PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libversion.a) + +add_library(net STATIC IMPORTED) +set_target_properties(net PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libnet.a) + +add_library(hardforks STATIC IMPORTED) +set_target_properties(hardforks PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libhardforks.a) + +add_library(randomx STATIC IMPORTED) +set_target_properties(randomx PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/librandomx.a) + +add_library(offshore STATIC IMPORTED) +set_target_properties(offshore PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/liboffshore.a) + + +add_library(rpc_base STATIC IMPORTED) +set_target_properties(rpc_base PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/librpc_base.a) + +add_library(wallet-crypto STATIC IMPORTED) +set_target_properties(wallet-crypto PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/salvium/libwallet-crypto.a) + +include_directories( ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/include ) + +target_link_libraries( cw_salvium + + wallet_api + wallet + cryptonote_core + cryptonote_basic + mnemonics + ringct + ringct_basic + net + common + cncrypto + blockchain_db + lmdb + easylogging + unbound + epee + checkpoints + device + device_trezor + multisig + version + randomx + offshore + hardforks + rpc_base + + 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_salvium/android/build.gradle b/cw_salvium/android/build.gradle new file mode 100644 index 0000000000..99f2e052bc --- /dev/null +++ b/cw_salvium/android/build.gradle @@ -0,0 +1,56 @@ +group 'com.cakewallet.cw_salvium' +version '1.0-SNAPSHOT' + +buildscript { + ext.kotlin_version = '2.0.21' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:8.7.1' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +rootProject.allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion 33 + + if (project.android.hasProperty("namespace")) { + namespace 'com.cakewallet.cw_salvium' + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = '17' + } + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + defaultConfig { + minSdkVersion 21 + } + externalNativeBuild { + cmake { + } + } +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/cw_salvium/android/gradle.properties b/cw_salvium/android/gradle.properties new file mode 100644 index 0000000000..94adc3a3f9 --- /dev/null +++ b/cw_salvium/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/cw_salvium/android/gradle/wrapper/gradle-wrapper.properties b/cw_salvium/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..3c9d0852bf --- /dev/null +++ b/cw_salvium/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/cw_salvium/android/settings.gradle b/cw_salvium/android/settings.gradle new file mode 100644 index 0000000000..46288f5d43 --- /dev/null +++ b/cw_salvium/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'cw_salvium' diff --git a/cw_salvium/android/src/main/AndroidManifest.xml b/cw_salvium/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..5aca136a1c --- /dev/null +++ b/cw_salvium/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/cw_salvium/android/src/main/kotlin/com/cakewallet/cw_salvium/CwSalviumPlugin.kt b/cw_salvium/android/src/main/kotlin/com/cakewallet/cw_salvium/CwSalviumPlugin.kt new file mode 100644 index 0000000000..b9b18aa3f4 --- /dev/null +++ b/cw_salvium/android/src/main/kotlin/com/cakewallet/cw_salvium/CwSalviumPlugin.kt @@ -0,0 +1,36 @@ +package com.cakewallet.cw_salvium + +import androidx.annotation.NonNull + +import io.flutter.embedding.engine.plugins.FlutterPlugin +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 + +/** CwSalviumPlugin */ +class CwSalviumPlugin: FlutterPlugin, MethodCallHandler { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private lateinit var channel : MethodChannel + + override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + channel = MethodChannel(flutterPluginBinding.binaryMessenger, "cw_salvium") + channel.setMethodCallHandler(this) + } + + override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { + if (call.method == "getPlatformVersion") { + result.success("Android ${android.os.Build.VERSION.RELEASE}") + } else { + result.notImplemented() + } + } + + override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) + } +} diff --git a/cw_salvium/ios/.gitignore b/cw_salvium/ios/.gitignore new file mode 100644 index 0000000000..aa479fd3ce --- /dev/null +++ b/cw_salvium/ios/.gitignore @@ -0,0 +1,37 @@ +.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_salvium/ios/Assets/.gitkeep b/cw_salvium/ios/Assets/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cw_salvium/ios/Classes/CwSalviumPlugin.h b/cw_salvium/ios/Classes/CwSalviumPlugin.h new file mode 100644 index 0000000000..d9731ada8c --- /dev/null +++ b/cw_salvium/ios/Classes/CwSalviumPlugin.h @@ -0,0 +1,4 @@ +#import + +@interface CwSalviumPlugin : NSObject +@end diff --git a/cw_salvium/ios/Classes/CwSalviumPlugin.m b/cw_salvium/ios/Classes/CwSalviumPlugin.m new file mode 100644 index 0000000000..6b619244b7 --- /dev/null +++ b/cw_salvium/ios/Classes/CwSalviumPlugin.m @@ -0,0 +1,15 @@ +#import "CwSalviumPlugin.h" +#if __has_include() +#import +#else +// Support project import fallback if the generated compatibility header +// is not copied when this plugin is created as a library. +// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 +#import "cw_salvium-Swift.h" +#endif + +@implementation CwSalviumPlugin ++ (void)registerWithRegistrar:(NSObject*)registrar { + [SwiftCwSalviumPlugin registerWithRegistrar:registrar]; +} +@end diff --git a/cw_salvium/ios/Classes/SwiftCwSalviumPlugin.swift b/cw_salvium/ios/Classes/SwiftCwSalviumPlugin.swift new file mode 100644 index 0000000000..4a6f56e61e --- /dev/null +++ b/cw_salvium/ios/Classes/SwiftCwSalviumPlugin.swift @@ -0,0 +1,14 @@ +import Flutter +import UIKit + +public class SwiftCwSalviumPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "cw_salvium", binaryMessenger: registrar.messenger()) + let instance = SwiftCwSalviumPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + result("iOS " + UIDevice.current.systemVersion) + } +} diff --git a/cw_salvium/ios/Classes/salvium_api.cpp b/cw_salvium/ios/Classes/salvium_api.cpp new file mode 100644 index 0000000000..b78c8cdc4b --- /dev/null +++ b/cw_salvium/ios/Classes/salvium_api.cpp @@ -0,0 +1,942 @@ +#include +#include "cstdlib" +#include +#include +#include +#include +#include +#include "thread" +#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 SalviumBalance + { + uint64_t amount; + char *assetType; + + SalviumBalance(char *_assetType, uint64_t _amount) + { + amount = _amount; + assetType = _assetType; + } + }; + + struct SalviumRate + { + uint64_t rate; + char *assetType; + + SalviumRate(char *_assetType, uint64_t _rate) + { + rate = _rate; + assetType = _assetType; + } + }; + + 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, std::string assetType) + { + m_new_transaction = true; + } + + void moneyReceived(const std::string &txId, uint64_t amount, std::string assetType) + { + 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; + char *assetType; + + 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()); + assetType = strdup(transaction->assetType().c_str()); + } + }; + + struct PendingTransactionRaw + { + uint64_t amount; + uint64_t fee; + char *hash; + Monero::PendingTransaction *transaction; + + PendingTransactionRaw(Monero::PendingTransaction *_transaction) + { + transaction = _transaction; + amount = _transaction->amount(); + fee = _transaction->fee(); + hash = strdup(_transaction->txid()[0].c_str()); + } + }; + + 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::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; + } + } + + Monero::Wallet *get_current_wallet() + { + return m_wallet; + } + + bool create_wallet(char *path, char *password, char *language, int32_t networkType, char *error) + { + Monero::WalletManagerFactory::setLogLevel(4); + + 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 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() + { + return strdup(get_current_wallet()->seed().c_str()); + } + + int64_t *get_full_balance(uint32_t account_index) + { + std::map accountBalance; + std::map> balanceSubaddresses = get_current_wallet()->balance(account_index); + std::vector assetList = Monero::Assets::list(); + //prefill balances + for (const auto &asset_type : assetList) { + + accountBalance[asset_type] = 0; + } + // balances are mapped to their subaddress + // we compute total balances of account + for (auto const& balanceSubaddress : balanceSubaddresses) + { + + std::map balanceOfSubaddress = balanceSubaddress.second; + + for (auto const& balance : balanceOfSubaddress) + { + + const std::string &assetType = balance.first; + const uint64_t &amount = balance.second; + accountBalance[assetType] +=amount; + } + } + + size_t size = accountBalance.size(); + int64_t *balanceAddresses = (int64_t *)malloc(size * sizeof(int64_t)); + int i = 0; + + for (auto const& balance : accountBalance) + { + char *assetType = strdup(balance.first.c_str()); + SalviumBalance *hb = new SalviumBalance(assetType, balance.second); + balanceAddresses[i] = reinterpret_cast(hb); + i++; + } + return balanceAddresses; + } + + int64_t *get_unlocked_balance(uint32_t account_index) + { + std::map accountBalance; + std::map> balanceSubaddresses = get_current_wallet()->unlockedBalance(account_index); + std::vector assetList = Monero::Assets::list(); + + //prefill balances + for (const auto &asset_type : assetList) { + + accountBalance[asset_type] = 0; + } + // balances are mapped to their subaddress + // we compute total balances of account + for (auto const& balanceSubaddress : balanceSubaddresses) + { + + std::map balanceOfSubaddress = balanceSubaddress.second; + + for (auto const& balance : balanceOfSubaddress) + { + + const std::string &assetType = balance.first; + const uint64_t &amount = balance.second; + accountBalance[assetType] +=amount; + } + } + + size_t size = accountBalance.size(); + int64_t *balanceAddresses = (int64_t *)malloc(size * sizeof(int64_t)); + int i = 0; + + for (auto const& balance : accountBalance) + { + char *assetType = strdup(balance.first.c_str()); + SalviumBalance *hb = new SalviumBalance(assetType, balance.second); + balanceAddresses[i] = reinterpret_cast(hb); + i++; + } + return balanceAddresses; + } + + 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 *error) + { + nice(19); + Monero::Wallet *wallet = get_current_wallet(); + + std::string _login = ""; + std::string _password = ""; + + if (login != nullptr) + { + _login = std::string(login); + } + + if (password != nullptr) + { + _password = std::string(password); + } + + bool inited = wallet->init(std::string(address), 0, _login, _password, use_ssl, is_light_wallet); + + 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 *asset_type, char *payment_id, char *amount, + uint8_t priority_raw, uint32_t subaddr_account, Utf8Box &error, PendingTransactionRaw &pendingTransaction) + { + nice(19); + + 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, std::string(asset_type), std::string(asset_type), m_wallet->defaultMixin(), priority, subaddr_account, {}); + } + else + { + transaction = m_wallet->createTransaction(std::string(address), _payment_id, Monero::optional(),std::string(asset_type), std::string(asset_type), 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_create_mult_dest(char **addresses, char *asset_type, char *payment_id, char **amounts, uint32_t size, + uint8_t priority_raw, uint32_t subaddr_account, 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++; + } + + 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, + std::string(asset_type), std::string(asset_type), 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(); + } + + 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(4); + } + + void rescan_blockchain() + { + m_wallet->rescanBlockchainAsync(); + } + + char * get_tx_key(char * txId) + { + return strdup(m_wallet->getTxKey(std::string(txId)).c_str()); + } + + int32_t asset_types_size() + { + return Monero::Assets::list().size(); + } + + char **asset_types() + { + size_t size = Monero::Assets::list().size(); + std::vector assetList = Monero::Assets::list(); + char **assetTypesPts; + assetTypesPts = (char **) malloc( size * sizeof(char*)); + + for (int i = 0; i < size; i++) + { + + std::string asset = assetList[i]; + //assetTypes[i] = (char *)malloc( 5 * sizeof(char)); + assetTypesPts[i] = strdup(asset.c_str()); + } + + return assetTypesPts; + } + + std::map rates; + + void update_rate() + { + rates = get_current_wallet()->oracleRates(); + } + + int64_t *get_rate() + { + size_t size = rates.size(); + int64_t *salviumRates = (int64_t *)malloc(size * sizeof(int64_t)); + int i = 0; + + for (auto const& rate : rates) + { + char *assetType = strdup(rate.first.c_str()); + SalviumRate *salviumRate = new SalviumRate(assetType, rate.second); + salviumRates[i] = reinterpret_cast(salviumRate); + i++; + } + + return salviumRates; + } + + int32_t size_of_rate() + { + return static_cast(rates.size()); + } + + void set_trusted_daemon(bool arg) + { + m_wallet->setTrustedDaemon(arg); + } + + bool trusted_daemon() + { + return m_wallet->trustedDaemon(); + } + +#ifdef __cplusplus +} +#endif diff --git a/cw_salvium/ios/cw_salvium.podspec b/cw_salvium/ios/cw_salvium.podspec new file mode 100644 index 0000000000..f089163d8b --- /dev/null +++ b/cw_salvium/ios/cw_salvium.podspec @@ -0,0 +1,50 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint cw_salvium.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'cw_salvium' + s.version = '0.0.1' + s.summary = 'Cake Wallet Salvium' + s.description = 'Cake Wallet wrapper over Salvium project' + s.homepage = 'http://cakewallet.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Cake Wallet' => 'support@cakewallet.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.public_header_files = 'Classes/**/*.h, Classes/*.h, ../shared_external/ios/libs/monero/include/src/**/*.h, ../shared_external/ios/libs/monero/include/contrib/**/*.h, ../shared_external/ios/libs/monero/include/../shared_external/ios/**/*.h' + s.dependency 'Flutter' + s.dependency 'cw_shared_external' + s.platform = :ios, '10.0' + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS' => 'arm64', 'ENABLE_BITCODE' => 'NO' } + s.swift_version = '5.0' + 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 '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 'Salvium' do |salvium| + salvium.preserve_paths = 'External/ios/include/**/*.h' + salvium.vendored_libraries = 'External/ios/lib/libsalvium.a' + salvium.libraries = 'salvium' + salvium.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/ios/include" } + end +end diff --git a/cw_salvium/lib/api/account_list.dart b/cw_salvium/lib/api/account_list.dart new file mode 100644 index 0000000000..31eb9c31a9 --- /dev/null +++ b/cw_salvium/lib/api/account_list.dart @@ -0,0 +1,81 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'package:cw_salvium/api/signatures.dart'; +import 'package:cw_salvium/api/types.dart'; +import 'package:cw_salvium/api/salvium_api.dart'; +import 'package:cw_salvium/api/structs/account_row.dart'; +import 'package:cw_salvium/api/wallet.dart'; + +final accountSizeNative = salviumApi + .lookup>('account_size') + .asFunction(); + +final accountRefreshNative = salviumApi + .lookup>('account_refresh') + .asFunction(); + +final accountGetAllNative = salviumApi + .lookup>('account_get_all') + .asFunction(); + +final accountAddNewNative = salviumApi + .lookup>('account_add_row') + .asFunction(); + +final accountSetLabelNative = salviumApi + .lookup>('account_set_label_row') + .asFunction(); + +bool isUpdating = false; + +void refreshAccounts() { + try { + isUpdating = true; + accountRefreshNative(); + isUpdating = false; + } catch (e) { + isUpdating = false; + rethrow; + } +} + +List getAllAccount() { + final size = accountSizeNative(); + final accountAddressesPointer = accountGetAllNative(); + final accountAddresses = accountAddressesPointer.asTypedList(size); + + return accountAddresses + .map((addr) => Pointer.fromAddress(addr).ref) + .toList(); +} + +void addAccountSync({required String label}) { + final labelPointer = label.toNativeUtf8(); + accountAddNewNative(labelPointer); + calloc.free(labelPointer); +} + +void setLabelForAccountSync({required int accountIndex, required String label}) { + final labelPointer = label.toNativeUtf8(); + accountSetLabelNative(accountIndex, labelPointer); + calloc.free(labelPointer); +} + +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_salvium/lib/api/asset_types.dart b/cw_salvium/lib/api/asset_types.dart new file mode 100644 index 0000000000..852fc49f1b --- /dev/null +++ b/cw_salvium/lib/api/asset_types.dart @@ -0,0 +1,23 @@ +import 'dart:ffi'; +import 'package:cw_salvium/api/convert_utf8_to_string.dart'; +import 'package:cw_salvium/api/signatures.dart'; +import 'package:cw_salvium/api/types.dart'; +import 'package:cw_salvium/api/salvium_api.dart'; +import 'package:ffi/ffi.dart'; + +final assetTypesSizeNative = salviumApi + .lookup>('asset_types_size') + .asFunction(); + +final getAssetTypesNative = salviumApi + .lookup>('asset_types') + .asFunction(); + +List getAssetTypes() { + List assetTypes = []; + Pointer> assetTypePointers = getAssetTypesNative(); + Pointer assetpointer = assetTypePointers.elementAt(0)[0]; + String asset = convertUTF8ToString(pointer: assetpointer); + + return assetTypes; +} diff --git a/cw_salvium/lib/api/balance_list.dart b/cw_salvium/lib/api/balance_list.dart new file mode 100644 index 0000000000..c6cb50009e --- /dev/null +++ b/cw_salvium/lib/api/balance_list.dart @@ -0,0 +1,58 @@ +import 'dart:ffi'; +import 'package:cw_salvium/api/signatures.dart'; +import 'package:cw_salvium/api/types.dart'; +import 'package:cw_salvium/api/salvium_api.dart'; +import 'package:cw_salvium/api/structs/salvium_balance_row.dart'; +import 'package:cw_salvium/api/structs/salvium_rate.dart'; +import 'asset_types.dart'; + +List getSalviumFullBalance({int accountIndex = 0}) { + final size = assetTypesSizeNative(); + final balanceAddressesPointer = getSalviumFullBalanceNative(accountIndex); + final balanceAddresses = balanceAddressesPointer.asTypedList(size); + + return balanceAddresses + .map((addr) => Pointer.fromAddress(addr).ref) + .toList(); +} + +List getSalviumUnlockedBalance({int accountIndex = 0}) { + final size = assetTypesSizeNative(); + final balanceAddressesPointer = getSalviumUnlockedBalanceNative(accountIndex); + final balanceAddresses = balanceAddressesPointer.asTypedList(size); + + return balanceAddresses + .map((addr) => Pointer.fromAddress(addr).ref) + .toList(); +} + +List getRate() { + updateRateNative(); + final size = sizeOfRateNative(); + final ratePointer = getRateNative(); + final rate = ratePointer.asTypedList(size); + + return rate + .map((addr) => Pointer.fromAddress(addr).ref) + .toList(); +} + +final getSalviumFullBalanceNative = salviumApi + .lookup>('get_full_balance') + .asFunction(); + +final getSalviumUnlockedBalanceNative = salviumApi + .lookup>('get_unlocked_balance') + .asFunction(); + +final getRateNative = salviumApi + .lookup>('get_rate') + .asFunction(); + +final sizeOfRateNative = salviumApi + .lookup>('size_of_rate') + .asFunction(); + +final updateRateNative = salviumApi + .lookup>('update_rate') + .asFunction(); diff --git a/cw_salvium/lib/api/convert_utf8_to_string.dart b/cw_salvium/lib/api/convert_utf8_to_string.dart new file mode 100644 index 0000000000..41a6b648a5 --- /dev/null +++ b/cw_salvium/lib/api/convert_utf8_to_string.dart @@ -0,0 +1,8 @@ +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_salvium/lib/api/cw_salvium.dart b/cw_salvium/lib/api/cw_salvium.dart new file mode 100644 index 0000000000..0f6a2c6ccd --- /dev/null +++ b/cw_salvium/lib/api/cw_salvium.dart @@ -0,0 +1,14 @@ + +import 'dart:async'; + +import 'package:flutter/services.dart'; + +class CwSalvium { + static const MethodChannel _channel = + const MethodChannel('cw_salvium'); + + static Future get platformVersion async { + final String version = await _channel.invokeMethod('getPlatformVersion') ?? ''; + return version; + } +} diff --git a/cw_salvium/lib/api/exceptions/connection_to_node_exception.dart b/cw_salvium/lib/api/exceptions/connection_to_node_exception.dart new file mode 100644 index 0000000000..483b0a174c --- /dev/null +++ b/cw_salvium/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_salvium/lib/api/exceptions/creation_transaction_exception.dart b/cw_salvium/lib/api/exceptions/creation_transaction_exception.dart new file mode 100644 index 0000000000..7b55ec0746 --- /dev/null +++ b/cw_salvium/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_salvium/lib/api/exceptions/setup_wallet_exception.dart b/cw_salvium/lib/api/exceptions/setup_wallet_exception.dart new file mode 100644 index 0000000000..9d985665b7 --- /dev/null +++ b/cw_salvium/lib/api/exceptions/setup_wallet_exception.dart @@ -0,0 +1,10 @@ +class SetupWalletException implements Exception { + SetupWalletException({required this.message}); + + final String message; + + @override + String toString() { + return message; + } +} \ No newline at end of file diff --git a/cw_salvium/lib/api/exceptions/wallet_creation_exception.dart b/cw_salvium/lib/api/exceptions/wallet_creation_exception.dart new file mode 100644 index 0000000000..6052366b9c --- /dev/null +++ b/cw_salvium/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_salvium/lib/api/exceptions/wallet_opening_exception.dart b/cw_salvium/lib/api/exceptions/wallet_opening_exception.dart new file mode 100644 index 0000000000..df7a850a4f --- /dev/null +++ b/cw_salvium/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_salvium/lib/api/exceptions/wallet_restore_from_keys_exception.dart b/cw_salvium/lib/api/exceptions/wallet_restore_from_keys_exception.dart new file mode 100644 index 0000000000..3ff5f24380 --- /dev/null +++ b/cw_salvium/lib/api/exceptions/wallet_restore_from_keys_exception.dart @@ -0,0 +1,10 @@ +class WalletRestoreFromKeysException implements Exception { + WalletRestoreFromKeysException({required this.message}); + + final String message; + + @override + String toString() { + return message; + } +} \ No newline at end of file diff --git a/cw_salvium/lib/api/exceptions/wallet_restore_from_seed_exception.dart b/cw_salvium/lib/api/exceptions/wallet_restore_from_seed_exception.dart new file mode 100644 index 0000000000..004cd79583 --- /dev/null +++ b/cw_salvium/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_salvium/lib/api/monero_output.dart b/cw_salvium/lib/api/monero_output.dart new file mode 100644 index 0000000000..a6d735bd3c --- /dev/null +++ b/cw_salvium/lib/api/monero_output.dart @@ -0,0 +1,8 @@ +import 'package:flutter/foundation.dart'; + +class MoneroOutput { + MoneroOutput({required this.address, required this.amount}); + + final String address; + final String amount; +} \ No newline at end of file diff --git a/cw_salvium/lib/api/salvium_api.dart b/cw_salvium/lib/api/salvium_api.dart new file mode 100644 index 0000000000..4195fdda78 --- /dev/null +++ b/cw_salvium/lib/api/salvium_api.dart @@ -0,0 +1,6 @@ +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary salviumApi = Platform.isAndroid + ? DynamicLibrary.open("libcw_salvium.so") + : DynamicLibrary.open("cw_salvium.framework/cw_salvium"); diff --git a/cw_salvium/lib/api/signatures.dart b/cw_salvium/lib/api/signatures.dart new file mode 100644 index 0000000000..f36bb6e58e --- /dev/null +++ b/cw_salvium/lib/api/signatures.dart @@ -0,0 +1,144 @@ +import 'dart:ffi'; +import 'package:cw_salvium/api/structs/pending_transaction.dart'; +import 'package:cw_salvium/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 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_balance = Pointer Function(Int32); + +typedef get_unlocked_balance = Pointer Function(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_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 assetType, + Pointer paymentId, + Pointer amount, + Int8 priorityRaw, + Int32 subaddrAccount, + Pointer error, + Pointer pendingTransaction); + +typedef transaction_create_mult_dest = Int8 Function( + Pointer> addresses, + Pointer assetType, + Pointer paymentId, + Pointer> amounts, + Int32 size, + Int8 priorityRaw, + Int32 subaddrAccount, + 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 asset_types = Pointer> Function(); + +typedef asset_types_size = Int32 Function(); + +typedef get_rate = Pointer Function(); + +typedef size_of_rate = Int32 Function(); + +typedef update_rate = Void Function(); + +typedef set_trusted_daemon = Void Function(Int8 trusted); + +typedef trusted_daemon = Int8 Function(); \ No newline at end of file diff --git a/cw_salvium/lib/api/structs/account_row.dart b/cw_salvium/lib/api/structs/account_row.dart new file mode 100644 index 0000000000..aa492ee0f1 --- /dev/null +++ b/cw_salvium/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_salvium/lib/api/structs/pending_transaction.dart b/cw_salvium/lib/api/structs/pending_transaction.dart new file mode 100644 index 0000000000..12e5233f13 --- /dev/null +++ b/cw_salvium/lib/api/structs/pending_transaction.dart @@ -0,0 +1,27 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +class PendingTransactionRaw extends Struct { + @Int64() + external int amount; + + @Int64() + external int fee; + + external Pointer hash; + + String getHash() => hash.toDartString(); +} + +class PendingTransactionDescription { + PendingTransactionDescription({ + required this.amount, + required this.fee, + required this.hash, + required this.pointerAddress}); + + final int amount; + final int fee; + final String hash; + final int pointerAddress; +} \ No newline at end of file diff --git a/cw_salvium/lib/api/structs/salvium_balance_row.dart b/cw_salvium/lib/api/structs/salvium_balance_row.dart new file mode 100644 index 0000000000..6905b6c21c --- /dev/null +++ b/cw_salvium/lib/api/structs/salvium_balance_row.dart @@ -0,0 +1,12 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +class SalviumBalanceRow extends Struct { + @Int64() + external int amount; + + external Pointer assetType; + + int getAmount() => amount; + String getAssetType() => assetType.toDartString(); +} diff --git a/cw_salvium/lib/api/structs/salvium_rate.dart b/cw_salvium/lib/api/structs/salvium_rate.dart new file mode 100644 index 0000000000..e6ad712b99 --- /dev/null +++ b/cw_salvium/lib/api/structs/salvium_rate.dart @@ -0,0 +1,12 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +class SalviumRate extends Struct { + @Int64() + external int rate; + + external Pointer assetType; + + int getRate() => rate; + String getAssetType() => assetType.toDartString(); +} diff --git a/cw_salvium/lib/api/structs/subaddress_row.dart b/cw_salvium/lib/api/structs/subaddress_row.dart new file mode 100644 index 0000000000..d593a793d9 --- /dev/null +++ b/cw_salvium/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_salvium/lib/api/structs/transaction_info_row.dart b/cw_salvium/lib/api/structs/transaction_info_row.dart new file mode 100644 index 0000000000..177cdfde72 --- /dev/null +++ b/cw_salvium/lib/api/structs/transaction_info_row.dart @@ -0,0 +1,44 @@ +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; + + external Pointer assetType; + + @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(); + String getAssetType() => assetType.toDartString(); +} diff --git a/cw_salvium/lib/api/structs/ut8_box.dart b/cw_salvium/lib/api/structs/ut8_box.dart new file mode 100644 index 0000000000..53e678c88f --- /dev/null +++ b/cw_salvium/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_salvium/lib/api/subaddress_list.dart b/cw_salvium/lib/api/subaddress_list.dart new file mode 100644 index 0000000000..c3e583c684 --- /dev/null +++ b/cw_salvium/lib/api/subaddress_list.dart @@ -0,0 +1,97 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'package:flutter/foundation.dart'; +import 'package:cw_salvium/api/signatures.dart'; +import 'package:cw_salvium/api/types.dart'; +import 'package:cw_salvium/api/salvium_api.dart'; +import 'package:cw_salvium/api/structs/subaddress_row.dart'; +import 'package:cw_salvium/api/wallet.dart'; + +final subaddressSizeNative = salviumApi + .lookup>('subaddrress_size') + .asFunction(); + +final subaddressRefreshNative = salviumApi + .lookup>('subaddress_refresh') + .asFunction(); + +final subaddrressGetAllNative = salviumApi + .lookup>('subaddrress_get_all') + .asFunction(); + +final subaddrressAddNewNative = salviumApi + .lookup>('subaddress_add_row') + .asFunction(); + +final subaddrressSetLabelNative = salviumApi + .lookup>('subaddress_set_label') + .asFunction(); + +bool isUpdating = false; + +void refreshSubaddresses({required int accountIndex}) { + try { + isUpdating = true; + subaddressRefreshNative(accountIndex); + isUpdating = false; + } catch (e) { + isUpdating = false; + rethrow; + } +} + +List getAllSubaddresses() { + final size = subaddressSizeNative(); + final subaddressAddressesPointer = subaddrressGetAllNative(); + final subaddressAddresses = subaddressAddressesPointer.asTypedList(size); + + return subaddressAddresses + .map((addr) => Pointer.fromAddress(addr).ref) + .toList(); +} + +void addSubaddressSync({required int accountIndex, required String label}) { + final labelPointer = label.toNativeUtf8(); + subaddrressAddNewNative(accountIndex, labelPointer); + calloc.free(labelPointer); +} + +void setLabelForSubaddressSync( + {required int accountIndex, required int addressIndex, required String label}) { + final labelPointer = label.toNativeUtf8(); + + subaddrressSetLabelNative(accountIndex, addressIndex, labelPointer); + calloc.free(labelPointer); +} + +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 { + await compute, void>( + _addSubaddress, {'accountIndex': accountIndex, 'label': label}); + await store(); +} + +Future setLabelForSubaddress( + {required int accountIndex, required int addressIndex, required String label}) async { + await compute, void>(_setLabelForSubaddress, { + 'accountIndex': accountIndex, + 'addressIndex': addressIndex, + 'label': label + }); + await store(); +} diff --git a/cw_salvium/lib/api/transaction_history.dart b/cw_salvium/lib/api/transaction_history.dart new file mode 100644 index 0000000000..63c6c3fb23 --- /dev/null +++ b/cw_salvium/lib/api/transaction_history.dart @@ -0,0 +1,246 @@ +import 'dart:ffi'; +import 'package:cw_salvium/api/convert_utf8_to_string.dart'; +import 'package:cw_salvium/api/monero_output.dart'; +import 'package:cw_salvium/api/structs/ut8_box.dart'; +import 'package:ffi/ffi.dart'; +import 'package:flutter/foundation.dart'; +import 'package:cw_salvium/api/signatures.dart'; +import 'package:cw_salvium/api/types.dart'; +import 'package:cw_salvium/api/salvium_api.dart'; +import 'package:cw_salvium/api/structs/transaction_info_row.dart'; +import 'package:cw_salvium/api/structs/pending_transaction.dart'; +import 'package:cw_salvium/api/exceptions/creation_transaction_exception.dart'; + +final transactionsRefreshNative = salviumApi + .lookup>('transactions_refresh') + .asFunction(); + +final transactionsCountNative = salviumApi + .lookup>('transactions_count') + .asFunction(); + +final transactionsGetAllNative = salviumApi + .lookup>('transactions_get_all') + .asFunction(); + +final transactionCreateNative = salviumApi + .lookup>('transaction_create') + .asFunction(); + +final transactionCreateMultDestNative = salviumApi + .lookup>('transaction_create_mult_dest') + .asFunction(); + +final transactionCommitNative = salviumApi + .lookup>('transaction_commit') + .asFunction(); + +final getTxKeyNative = salviumApi + .lookup>('get_tx_key') + .asFunction(); + +String getTxKey(String txId) { + final txIdPointer = txId.toNativeUtf8(); + final keyPointer = getTxKeyNative(txIdPointer); + + calloc.free(txIdPointer); + + if (keyPointer != null) { + return convertUTF8ToString(pointer: keyPointer); + } + + return ''; +} + +void refreshTransactions() => transactionsRefreshNative(); + +int countOfTransactions() => transactionsCountNative(); + +List getAllTransations() { + final size = transactionsCountNative(); + final transactionsPointer = transactionsGetAllNative(); + final transactionsAddresses = transactionsPointer.asTypedList(size); + + return transactionsAddresses + .map((addr) => Pointer.fromAddress(addr).ref) + .toList(); +} + +PendingTransactionDescription createTransactionSync( + {required String address, + required String assetType, + required String paymentId, + required int priorityRaw, + String? amount, + int accountIndex = 0}) { + final addressPointer = address.toNativeUtf8(); + final assetTypePointer = assetType.toNativeUtf8(); + final paymentIdPointer = paymentId.toNativeUtf8(); + final amountPointer = amount != null ? amount.toNativeUtf8() : nullptr; + final errorMessagePointer = calloc(); + final pendingTransactionRawPointer = calloc(); + final created = transactionCreateNative( + addressPointer, + assetTypePointer, + paymentIdPointer, + amountPointer, + priorityRaw, + accountIndex, + errorMessagePointer, + pendingTransactionRawPointer) != + 0; + + calloc.free(addressPointer); + calloc.free(assetTypePointer); + calloc.free(paymentIdPointer); + + if (amountPointer != nullptr) { + calloc.free(amountPointer); + } + + 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(), + pointerAddress: pendingTransactionRawPointer.address); +} + +PendingTransactionDescription createTransactionMultDestSync( + {required List outputs, + required String assetType, + required String paymentId, + required int priorityRaw, + int accountIndex = 0}) { + 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 assetTypePointer = assetType.toNativeUtf8(); + final paymentIdPointer = paymentId.toNativeUtf8(); + final errorMessagePointer = calloc(); + final pendingTransactionRawPointer = calloc(); + final created = transactionCreateMultDestNative( + addressesPointerPointer, + assetTypePointer, + paymentIdPointer, + amountsPointerPointer, + size, + priorityRaw, + accountIndex, + errorMessagePointer, + pendingTransactionRawPointer) != + 0; + + calloc.free(addressesPointerPointer); + calloc.free(assetTypePointer); + calloc.free(amountsPointerPointer); + + addressesPointers.forEach((element) => calloc.free(element)); + amountsPointers.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(), + pointerAddress: pendingTransactionRawPointer.address); +} + +void commitTransactionFromPointerAddress({required int address}) => commitTransaction( + transactionPointer: Pointer.fromAddress(address)); + +void commitTransaction({required Pointer transactionPointer}) { + final errorMessagePointer = calloc(); + final isCommited = + transactionCommitNative(transactionPointer, errorMessagePointer) != 0; + + if (!isCommited) { + final message = errorMessagePointer.ref.getValue(); + calloc.free(errorMessagePointer); + throw CreationTransactionException(message: message); + } +} + +PendingTransactionDescription _createTransactionSync(Map args) { + final address = args['address'] as String; + final assetType = args['assetType'] 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; + + return createTransactionSync( + address: address, + assetType: assetType, + paymentId: paymentId, + amount: amount, + priorityRaw: priorityRaw, + accountIndex: accountIndex); +} + +PendingTransactionDescription _createTransactionMultDestSync(Map args) { + final outputs = args['outputs'] as List; + final assetType = args['assetType'] as String; + final paymentId = args['paymentId'] as String; + final priorityRaw = args['priorityRaw'] as int; + final accountIndex = args['accountIndex'] as int; + + return createTransactionMultDestSync( + outputs: outputs, + assetType: assetType, + paymentId: paymentId, + priorityRaw: priorityRaw, + accountIndex: accountIndex); +} + +Future createTransaction( + {required String address, + required String assetType, + required int priorityRaw, + String? amount, + String paymentId = '', + int accountIndex = 0}) => + compute(_createTransactionSync, { + 'address': address, + 'assetType': assetType, + 'paymentId': paymentId, + 'amount': amount, + 'priorityRaw': priorityRaw, + 'accountIndex': accountIndex + }); + +Future createTransactionMultDest( + {required List outputs, + required int priorityRaw, + String? assetType, + String paymentId = '', + int accountIndex = 0}) => + compute(_createTransactionMultDestSync, { + 'outputs': outputs, + 'assetType': assetType, + 'paymentId': paymentId, + 'priorityRaw': priorityRaw, + 'accountIndex': accountIndex + }); diff --git a/cw_salvium/lib/api/types.dart b/cw_salvium/lib/api/types.dart new file mode 100644 index 0000000000..037a3730a9 --- /dev/null +++ b/cw_salvium/lib/api/types.dart @@ -0,0 +1,142 @@ +import 'dart:ffi'; +import 'package:cw_salvium/api/structs/pending_transaction.dart'; +import 'package:cw_salvium/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 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 GetSalviumFullBalance = Pointer Function(int); + +typedef GetSalviumUnlockedBalance = Pointer Function(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 GetTxKey = Pointer? Function(Pointer txId); + +typedef TransactionsCount = int Function(); + +typedef TransactionsGetAll = Pointer Function(); + +typedef TransactionCreate = int Function( + Pointer address, + Pointer assetType, + Pointer paymentId, + Pointer amount, + int priorityRaw, + int subaddrAccount, + Pointer error, + Pointer pendingTransaction); + +typedef TransactionCreateMultDest = int Function( + Pointer> addresses, + Pointer assetType, + Pointer paymentId, + Pointer> amounts, + int size, + int priorityRaw, + int subaddrAccount, + 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 AssetTypes = Pointer> Function(); + +typedef AssetTypesSize = int Function(); + +typedef GetRate = Pointer Function(); + +typedef SizeOfRate = int Function(); + +typedef UpdateRate = void Function(); + +typedef SetTrustedDaemon = void Function(int); + +typedef TrustedDaemon = int Function(); \ No newline at end of file diff --git a/cw_salvium/lib/api/wallet.dart b/cw_salvium/lib/api/wallet.dart new file mode 100644 index 0000000000..ce988a9bb5 --- /dev/null +++ b/cw_salvium/lib/api/wallet.dart @@ -0,0 +1,376 @@ +import 'dart:async'; +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'package:cw_salvium/api/structs/ut8_box.dart'; +import 'package:cw_salvium/api/convert_utf8_to_string.dart'; +import 'package:cw_salvium/api/signatures.dart'; +import 'package:cw_salvium/api/types.dart'; +import 'package:cw_salvium/api/salvium_api.dart'; +import 'package:cw_salvium/api/exceptions/setup_wallet_exception.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +int _boolToInt(bool value) => value ? 1 : 0; + +final getFileNameNative = salviumApi + .lookup>('get_filename') + .asFunction(); + +final getSeedNative = + salviumApi.lookup>('seed').asFunction(); + +final getAddressNative = salviumApi + .lookup>('get_address') + .asFunction(); + +final getFullBalanceNative = salviumApi + .lookup>('get_full_balance') + .asFunction(); + +final getUnlockedBalanceNative = salviumApi + .lookup>('get_unlocked_balance') + .asFunction(); + +final getCurrentHeightNative = salviumApi + .lookup>('get_current_height') + .asFunction(); + +final getNodeHeightNative = salviumApi + .lookup>('get_node_height') + .asFunction(); + +final isConnectedNative = salviumApi + .lookup>('is_connected') + .asFunction(); + +final setupNodeNative = salviumApi + .lookup>('setup_node') + .asFunction(); + +final startRefreshNative = salviumApi + .lookup>('start_refresh') + .asFunction(); + +final connecToNodeNative = salviumApi + .lookup>('connect_to_node') + .asFunction(); + +final setRefreshFromBlockHeightNative = salviumApi + .lookup>( + 'set_refresh_from_block_height') + .asFunction(); + +final setRecoveringFromSeedNative = salviumApi + .lookup>( + 'set_recovering_from_seed') + .asFunction(); + +final storeNative = + salviumApi.lookup>('store').asFunction(); + +final setPasswordNative = + salviumApi.lookup>('set_password').asFunction(); + +final setListenerNative = salviumApi + .lookup>('set_listener') + .asFunction(); + +final getSyncingHeightNative = salviumApi + .lookup>('get_syncing_height') + .asFunction(); + +final isNeededToRefreshNative = salviumApi + .lookup>('is_needed_to_refresh') + .asFunction(); + +final isNewTransactionExistNative = salviumApi + .lookup>( + 'is_new_transaction_exist') + .asFunction(); + +final getSecretViewKeyNative = salviumApi + .lookup>('secret_view_key') + .asFunction(); + +final getPublicViewKeyNative = salviumApi + .lookup>('public_view_key') + .asFunction(); + +final getSecretSpendKeyNative = salviumApi + .lookup>('secret_spend_key') + .asFunction(); + +final getPublicSpendKeyNative = salviumApi + .lookup>('public_spend_key') + .asFunction(); + +final closeCurrentWalletNative = salviumApi + .lookup>('close_current_wallet') + .asFunction(); + +final onStartupNative = salviumApi + .lookup>('on_startup') + .asFunction(); + +final rescanBlockchainAsyncNative = salviumApi + .lookup>('rescan_blockchain') + .asFunction(); + +final setTrustedDaemonNative = salviumApi + .lookup>('set_trusted_daemon') + .asFunction(); + +final trustedDaemonNative = salviumApi + .lookup>('trusted_daemon') + .asFunction(); + +int getSyncingHeight() => getSyncingHeightNative(); + +bool isNeededToRefresh() => isNeededToRefreshNative() != 0; + +bool isNewTransactionExist() => isNewTransactionExistNative() != 0; + +String getFilename() => convertUTF8ToString(pointer: getFileNameNative()); + +String getSeed() => convertUTF8ToString(pointer: getSeedNative()); + +String getAddress({int accountIndex = 0, int addressIndex = 0}) => + convertUTF8ToString(pointer: getAddressNative(accountIndex, addressIndex)); + +int getFullBalance({int accountIndex = 0}) => + getFullBalanceNative(accountIndex); + +int getUnlockedBalance({int accountIndex = 0}) => + getUnlockedBalanceNative(accountIndex); + +int getCurrentHeight() => getCurrentHeightNative(); + +int getNodeHeightSync() => getNodeHeightNative(); + +bool isConnectedSync() => isConnectedNative() != 0; + +bool 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; + + if (login != null) { + loginPointer = login.toNativeUtf8(); + } + + 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; +} + +void startRefreshSync() => startRefreshNative(); + +Future connectToNode() async => connecToNodeNative() != 0; + +void setRefreshFromBlockHeight({required int height}) => + setRefreshFromBlockHeightNative(height); + +void setRecoveringFromSeed({required bool isRecovery}) => + setRecoveringFromSeedNative(_boolToInt(isRecovery)); + +void storeSync() { + final pathPointer = ''.toNativeUtf8(); + storeNative(pathPointer); + calloc.free(pathPointer); +} + +void setPasswordSync(String password) { + final passwordPointer = password.toNativeUtf8(); + final errorMessagePointer = calloc(); + final changed = setPasswordNative(passwordPointer, errorMessagePointer) != 0; + calloc.free(passwordPointer); + + if (!changed) { + final message = errorMessagePointer.ref.getValue(); + calloc.free(errorMessagePointer); + throw Exception(message); + } + + calloc.free(errorMessagePointer); +} + +void closeCurrentWallet() => closeCurrentWalletNative(); + +String getSecretViewKey() => + convertUTF8ToString(pointer: getSecretViewKeyNative()); + +String getPublicViewKey() => + convertUTF8ToString(pointer: getPublicViewKeyNative()); + +String getSecretSpendKey() => + convertUTF8ToString(pointer: getSecretSpendKeyNative()); + +String getPublicSpendKey() => + convertUTF8ToString(pointer: getPublicSpendKeyNative()); + +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?.call(); + } + + var syncHeight = getSyncingHeight(); + + if (syncHeight <= 0) { + syncHeight = getCurrentHeight(); + } + + if (_initialSyncHeight <= 0) { + _initialSyncHeight = syncHeight; + } + + final bchHeight = await getNodeHeightOrUpdate(syncHeight); + + if (_lastKnownBlockHeight == syncHeight || syncHeight == null) { + 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() => onStartupNative(); + +void _storeSync(Object _) => storeSync(); + +bool _setupNodeSync(Map args) { + 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}) => + compute, void>(_setupNodeSync, { + 'address': address, + 'login': login, + 'password': password, + 'useSSL': useSSL, + 'isLightWallet': isLightWallet, + 'socksProxyAddress': socksProxyAddress + }); + +Future store() => compute(_storeSync, 0); + +Future isConnected() => compute(_isConnected, 0); + +Future getNodeHeight() => compute(_getNodeHeight, 0); + +void rescanBlockchainAsync() => rescanBlockchainAsyncNative(); + +Future setTrustedDaemon(bool trusted) async => setTrustedDaemonNative(_boolToInt(trusted)); + +Future trustedDaemon() async => trustedDaemonNative() != 0; diff --git a/cw_salvium/lib/api/wallet_manager.dart b/cw_salvium/lib/api/wallet_manager.dart new file mode 100644 index 0000000000..c9b8fe91bb --- /dev/null +++ b/cw_salvium/lib/api/wallet_manager.dart @@ -0,0 +1,248 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'package:flutter/foundation.dart'; +import 'package:cw_salvium/api/convert_utf8_to_string.dart'; +import 'package:cw_salvium/api/signatures.dart'; +import 'package:cw_salvium/api/types.dart'; +import 'package:cw_salvium/api/salvium_api.dart'; +import 'package:cw_salvium/api/wallet.dart'; +import 'package:cw_salvium/api/exceptions/wallet_opening_exception.dart'; +import 'package:cw_salvium/api/exceptions/wallet_creation_exception.dart'; +import 'package:cw_salvium/api/exceptions/wallet_restore_from_keys_exception.dart'; +import 'package:cw_salvium/api/exceptions/wallet_restore_from_seed_exception.dart'; + +final createWalletNative = salviumApi + .lookup>('create_wallet') + .asFunction(); + +final restoreWalletFromSeedNative = salviumApi + .lookup>( + 'restore_wallet_from_seed') + .asFunction(); + +final restoreWalletFromKeysNative = salviumApi + .lookup>( + 'restore_wallet_from_keys') + .asFunction(); + +final isWalletExistNative = salviumApi + .lookup>('is_wallet_exist') + .asFunction(); + +final loadWalletNative = salviumApi + .lookup>('load_wallet') + .asFunction(); + +final errorStringNative = salviumApi + .lookup>('error_string') + .asFunction(); + +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; + + calloc.free(pathPointer); + calloc.free(passwordPointer); + calloc.free(languagePointer); + + if (!isWalletCreated) { + throw WalletCreationException( + message: convertUTF8ToString(pointer: errorMessagePointer)); + } + + // 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; +} + +void restoreWalletFromSeedSync( + {required String path, + required String password, + 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; + + calloc.free(pathPointer); + calloc.free(passwordPointer); + calloc.free(seedPointer); + + if (!isWalletRestored) { + throw WalletRestoreFromSeedException( + message: convertUTF8ToString(pointer: errorMessagePointer)); + } +} + +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}) { + 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; + + calloc.free(pathPointer); + calloc.free(passwordPointer); + calloc.free(languagePointer); + calloc.free(addressPointer); + calloc.free(viewKeyPointer); + calloc.free(spendKeyPointer); + + if (!isWalletRestored) { + throw WalletRestoreFromKeysException( + message: convertUTF8ToString(pointer: errorMessagePointer)); + } +} + +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 _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); +} + +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 => + loadWallet(path: path, password: password, nettype: nettype); + +Future openWalletAsync(Map args) async => + compute(_openWallet, args); + +Future createWallet( + {required String path, + required String password, + required String language, + int nettype = 0}) async => + compute(_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 => + compute, void>(_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 => + compute, void>(_restoreFromKeys, { + 'path': path, + 'password': password, + 'language': language, + 'address': address, + 'viewKey': viewKey, + 'spendKey': spendKey, + 'nettype': nettype, + 'restoreHeight': restoreHeight + }); + +Future isWalletExist({required String path}) => compute(_isWalletExist, path); diff --git a/cw_salvium/lib/mnemonics/chinese_simplified.dart b/cw_salvium/lib/mnemonics/chinese_simplified.dart new file mode 100644 index 0000000000..da32250416 --- /dev/null +++ b/cw_salvium/lib/mnemonics/chinese_simplified.dart @@ -0,0 +1,1630 @@ +class ChineseSimplifiedMnemonics { + static const words = [ + "的", + "一", + "是", + "在", + "不", + "了", + "有", + "和", + "人", + "这", + "中", + "大", + "为", + "上", + "个", + "国", + "我", + "以", + "要", + "他", + "时", + "来", + "用", + "们", + "生", + "到", + "作", + "地", + "于", + "出", + "就", + "分", + "对", + "成", + "会", + "可", + "主", + "发", + "年", + "动", + "同", + "工", + "也", + "能", + "下", + "过", + "子", + "说", + "产", + "种", + "面", + "而", + "方", + "后", + "多", + "定", + "行", + "学", + "法", + "所", + "民", + "得", + "经", + "十", + "三", + "之", + "进", + "着", + "等", + "部", + "度", + "家", + "电", + "力", + "里", + "如", + "水", + "化", + "高", + "自", + "二", + "理", + "起", + "小", + "物", + "现", + "实", + "加", + "量", + "都", + "两", + "体", + "制", + "机", + "当", + "使", + "点", + "从", + "业", + "本", + "去", + "把", + "性", + "好", + "应", + "开", + "它", + "合", + "还", + "因", + "由", + "其", + "些", + "然", + "前", + "外", + "天", + "政", + "四", + "日", + "那", + "社", + "义", + "事", + "平", + "形", + "相", + "全", + "表", + "间", + "样", + "与", + "关", + "各", + "重", + "新", + "线", + "内", + "数", + "正", + "心", + "反", + "你", + "明", + "看", + "原", + "又", + "么", + "利", + "比", + "或", + "但", + "质", + "气", + "第", + "向", + "道", + "命", + "此", + "变", + "条", + "只", + "没", + "结", + "解", + "问", + "意", + "建", + "月", + "公", + "无", + "系", + "军", + "很", + "情", + "者", + "最", + "立", + "代", + "想", + "已", + "通", + "并", + "提", + "直", + "题", + "党", + "程", + "展", + "五", + "果", + "料", + "象", + "员", + "革", + "位", + "入", + "常", + "文", + "总", + "次", + "品", + "式", + "活", + "设", + "及", + "管", + "特", + "件", + "长", + "求", + "老", + "头", + "基", + "资", + "边", + "流", + "路", + "级", + "少", + "图", + "山", + "统", + "接", + "知", + "较", + "将", + "组", + "见", + "计", + "别", + "她", + "手", + "角", + "期", + "根", + "论", + "运", + "农", + "指", + "几", + "九", + "区", + "强", + "放", + "决", + "西", + "被", + "干", + "做", + "必", + "战", + "先", + "回", + "则", + "任", + "取", + "据", + "处", + "队", + "南", + "给", + "色", + "光", + "门", + "即", + "保", + "治", + "北", + "造", + "百", + "规", + "热", + "领", + "七", + "海", + "口", + "东", + "导", + "器", + "压", + "志", + "世", + "金", + "增", + "争", + "济", + "阶", + "油", + "思", + "术", + "极", + "交", + "受", + "联", + "什", + "认", + "六", + "共", + "权", + "收", + "证", + "改", + "清", + "美", + "再", + "采", + "转", + "更", + "单", + "风", + "切", + "打", + "白", + "教", + "速", + "花", + "带", + "安", + "场", + "身", + "车", + "例", + "真", + "务", + "具", + "万", + "每", + "目", + "至", + "达", + "走", + "积", + "示", + "议", + "声", + "报", + "斗", + "完", + "类", + "八", + "离", + "华", + "名", + "确", + "才", + "科", + "张", + "信", + "马", + "节", + "话", + "米", + "整", + "空", + "元", + "况", + "今", + "集", + "温", + "传", + "土", + "许", + "步", + "群", + "广", + "石", + "记", + "需", + "段", + "研", + "界", + "拉", + "林", + "律", + "叫", + "且", + "究", + "观", + "越", + "织", + "装", + "影", + "算", + "低", + "持", + "音", + "众", + "书", + "布", + "复", + "容", + "儿", + "须", + "际", + "商", + "非", + "验", + "连", + "断", + "深", + "难", + "近", + "矿", + "千", + "周", + "委", + "素", + "技", + "备", + "半", + "办", + "青", + "省", + "列", + "习", + "响", + "约", + "支", + "般", + "史", + "感", + "劳", + "便", + "团", + "往", + "酸", + "历", + "市", + "克", + "何", + "除", + "消", + "构", + "府", + "称", + "太", + "准", + "精", + "值", + "号", + "率", + "族", + "维", + "划", + "选", + "标", + "写", + "存", + "候", + "毛", + "亲", + "快", + "效", + "斯", + "院", + "查", + "江", + "型", + "眼", + "王", + "按", + "格", + "养", + "易", + "置", + "派", + "层", + "片", + "始", + "却", + "专", + "状", + "育", + "厂", + "京", + "识", + "适", + "属", + "圆", + "包", + "火", + "住", + "调", + "满", + "县", + "局", + "照", + "参", + "红", + "细", + "引", + "听", + "该", + "铁", + "价", + "严", + "首", + "底", + "液", + "官", + "德", + "随", + "病", + "苏", + "失", + "尔", + "死", + "讲", + "配", + "女", + "黄", + "推", + "显", + "谈", + "罪", + "神", + "艺", + "呢", + "席", + "含", + "企", + "望", + "密", + "批", + "营", + "项", + "防", + "举", + "球", + "英", + "氧", + "势", + "告", + "李", + "台", + "落", + "木", + "帮", + "轮", + "破", + "亚", + "师", + "围", + "注", + "远", + "字", + "材", + "排", + "供", + "河", + "态", + "封", + "另", + "施", + "减", + "树", + "溶", + "怎", + "止", + "案", + "言", + "士", + "均", + "武", + "固", + "叶", + "鱼", + "波", + "视", + "仅", + "费", + "紧", + "爱", + "左", + "章", + "早", + "朝", + "害", + "续", + "轻", + "服", + "试", + "食", + "充", + "兵", + "源", + "判", + "护", + "司", + "足", + "某", + "练", + "差", + "致", + "板", + "田", + "降", + "黑", + "犯", + "负", + "击", + "范", + "继", + "兴", + "似", + "余", + "坚", + "曲", + "输", + "修", + "故", + "城", + "夫", + "够", + "送", + "笔", + "船", + "占", + "右", + "财", + "吃", + "富", + "春", + "职", + "觉", + "汉", + "画", + "功", + "巴", + "跟", + "虽", + "杂", + "飞", + "检", + "吸", + "助", + "升", + "阳", + "互", + "初", + "创", + "抗", + "考", + "投", + "坏", + "策", + "古", + "径", + "换", + "未", + "跑", + "留", + "钢", + "曾", + "端", + "责", + "站", + "简", + "述", + "钱", + "副", + "尽", + "帝", + "射", + "草", + "冲", + "承", + "独", + "令", + "限", + "阿", + "宣", + "环", + "双", + "请", + "超", + "微", + "让", + "控", + "州", + "良", + "轴", + "找", + "否", + "纪", + "益", + "依", + "优", + "顶", + "础", + "载", + "倒", + "房", + "突", + "坐", + "粉", + "敌", + "略", + "客", + "袁", + "冷", + "胜", + "绝", + "析", + "块", + "剂", + "测", + "丝", + "协", + "诉", + "念", + "陈", + "仍", + "罗", + "盐", + "友", + "洋", + "错", + "苦", + "夜", + "刑", + "移", + "频", + "逐", + "靠", + "混", + "母", + "短", + "皮", + "终", + "聚", + "汽", + "村", + "云", + "哪", + "既", + "距", + "卫", + "停", + "烈", + "央", + "察", + "烧", + "迅", + "境", + "若", + "印", + "洲", + "刻", + "括", + "激", + "孔", + "搞", + "甚", + "室", + "待", + "核", + "校", + "散", + "侵", + "吧", + "甲", + "游", + "久", + "菜", + "味", + "旧", + "模", + "湖", + "货", + "损", + "预", + "阻", + "毫", + "普", + "稳", + "乙", + "妈", + "植", + "息", + "扩", + "银", + "语", + "挥", + "酒", + "守", + "拿", + "序", + "纸", + "医", + "缺", + "雨", + "吗", + "针", + "刘", + "啊", + "急", + "唱", + "误", + "训", + "愿", + "审", + "附", + "获", + "茶", + "鲜", + "粮", + "斤", + "孩", + "脱", + "硫", + "肥", + "善", + "龙", + "演", + "父", + "渐", + "血", + "欢", + "械", + "掌", + "歌", + "沙", + "刚", + "攻", + "谓", + "盾", + "讨", + "晚", + "粒", + "乱", + "燃", + "矛", + "乎", + "杀", + "药", + "宁", + "鲁", + "贵", + "钟", + "煤", + "读", + "班", + "伯", + "香", + "介", + "迫", + "句", + "丰", + "培", + "握", + "兰", + "担", + "弦", + "蛋", + "沉", + "假", + "穿", + "执", + "答", + "乐", + "谁", + "顺", + "烟", + "缩", + "征", + "脸", + "喜", + "松", + "脚", + "困", + "异", + "免", + "背", + "星", + "福", + "买", + "染", + "井", + "概", + "慢", + "怕", + "磁", + "倍", + "祖", + "皇", + "促", + "静", + "补", + "评", + "翻", + "肉", + "践", + "尼", + "衣", + "宽", + "扬", + "棉", + "希", + "伤", + "操", + "垂", + "秋", + "宜", + "氢", + "套", + "督", + "振", + "架", + "亮", + "末", + "宪", + "庆", + "编", + "牛", + "触", + "映", + "雷", + "销", + "诗", + "座", + "居", + "抓", + "裂", + "胞", + "呼", + "娘", + "景", + "威", + "绿", + "晶", + "厚", + "盟", + "衡", + "鸡", + "孙", + "延", + "危", + "胶", + "屋", + "乡", + "临", + "陆", + "顾", + "掉", + "呀", + "灯", + "岁", + "措", + "束", + "耐", + "剧", + "玉", + "赵", + "跳", + "哥", + "季", + "课", + "凯", + "胡", + "额", + "款", + "绍", + "卷", + "齐", + "伟", + "蒸", + "殖", + "永", + "宗", + "苗", + "川", + "炉", + "岩", + "弱", + "零", + "杨", + "奏", + "沿", + "露", + "杆", + "探", + "滑", + "镇", + "饭", + "浓", + "航", + "怀", + "赶", + "库", + "夺", + "伊", + "灵", + "税", + "途", + "灭", + "赛", + "归", + "召", + "鼓", + "播", + "盘", + "裁", + "险", + "康", + "唯", + "录", + "菌", + "纯", + "借", + "糖", + "盖", + "横", + "符", + "私", + "努", + "堂", + "域", + "枪", + "润", + "幅", + "哈", + "竟", + "熟", + "虫", + "泽", + "脑", + "壤", + "碳", + "欧", + "遍", + "侧", + "寨", + "敢", + "彻", + "虑", + "斜", + "薄", + "庭", + "纳", + "弹", + "饲", + "伸", + "折", + "麦", + "湿", + "暗", + "荷", + "瓦", + "塞", + "床", + "筑", + "恶", + "户", + "访", + "塔", + "奇", + "透", + "梁", + "刀", + "旋", + "迹", + "卡", + "氯", + "遇", + "份", + "毒", + "泥", + "退", + "洗", + "摆", + "灰", + "彩", + "卖", + "耗", + "夏", + "择", + "忙", + "铜", + "献", + "硬", + "予", + "繁", + "圈", + "雪", + "函", + "亦", + "抽", + "篇", + "阵", + "阴", + "丁", + "尺", + "追", + "堆", + "雄", + "迎", + "泛", + "爸", + "楼", + "避", + "谋", + "吨", + "野", + "猪", + "旗", + "累", + "偏", + "典", + "馆", + "索", + "秦", + "脂", + "潮", + "爷", + "豆", + "忽", + "托", + "惊", + "塑", + "遗", + "愈", + "朱", + "替", + "纤", + "粗", + "倾", + "尚", + "痛", + "楚", + "谢", + "奋", + "购", + "磨", + "君", + "池", + "旁", + "碎", + "骨", + "监", + "捕", + "弟", + "暴", + "割", + "贯", + "殊", + "释", + "词", + "亡", + "壁", + "顿", + "宝", + "午", + "尘", + "闻", + "揭", + "炮", + "残", + "冬", + "桥", + "妇", + "警", + "综", + "招", + "吴", + "付", + "浮", + "遭", + "徐", + "您", + "摇", + "谷", + "赞", + "箱", + "隔", + "订", + "男", + "吹", + "园", + "纷", + "唐", + "败", + "宋", + "玻", + "巨", + "耕", + "坦", + "荣", + "闭", + "湾", + "键", + "凡", + "驻", + "锅", + "救", + "恩", + "剥", + "凝", + "碱", + "齿", + "截", + "炼", + "麻", + "纺", + "禁", + "废", + "盛", + "版", + "缓", + "净", + "睛", + "昌", + "婚", + "涉", + "筒", + "嘴", + "插", + "岸", + "朗", + "庄", + "街", + "藏", + "姑", + "贸", + "腐", + "奴", + "啦", + "惯", + "乘", + "伙", + "恢", + "匀", + "纱", + "扎", + "辩", + "耳", + "彪", + "臣", + "亿", + "璃", + "抵", + "脉", + "秀", + "萨", + "俄", + "网", + "舞", + "店", + "喷", + "纵", + "寸", + "汗", + "挂", + "洪", + "贺", + "闪", + "柬", + "爆", + "烯", + "津", + "稻", + "墙", + "软", + "勇", + "像", + "滚", + "厘", + "蒙", + "芳", + "肯", + "坡", + "柱", + "荡", + "腿", + "仪", + "旅", + "尾", + "轧", + "冰", + "贡", + "登", + "黎", + "削", + "钻", + "勒", + "逃", + "障", + "氨", + "郭", + "峰", + "币", + "港", + "伏", + "轨", + "亩", + "毕", + "擦", + "莫", + "刺", + "浪", + "秘", + "援", + "株", + "健", + "售", + "股", + "岛", + "甘", + "泡", + "睡", + "童", + "铸", + "汤", + "阀", + "休", + "汇", + "舍", + "牧", + "绕", + "炸", + "哲", + "磷", + "绩", + "朋", + "淡", + "尖", + "启", + "陷", + "柴", + "呈", + "徒", + "颜", + "泪", + "稍", + "忘", + "泵", + "蓝", + "拖", + "洞", + "授", + "镜", + "辛", + "壮", + "锋", + "贫", + "虚", + "弯", + "摩", + "泰", + "幼", + "廷", + "尊", + "窗", + "纲", + "弄", + "隶", + "疑", + "氏", + "宫", + "姐", + "震", + "瑞", + "怪", + "尤", + "琴", + "循", + "描", + "膜", + "违", + "夹", + "腰", + "缘", + "珠", + "穷", + "森", + "枝", + "竹", + "沟", + "催", + "绳", + "忆", + "邦", + "剩", + "幸", + "浆", + "栏", + "拥", + "牙", + "贮", + "礼", + "滤", + "钠", + "纹", + "罢", + "拍", + "咱", + "喊", + "袖", + "埃", + "勤", + "罚", + "焦", + "潜", + "伍", + "墨", + "欲", + "缝", + "姓", + "刊", + "饱", + "仿", + "奖", + "铝", + "鬼", + "丽", + "跨", + "默", + "挖", + "链", + "扫", + "喝", + "袋", + "炭", + "污", + "幕", + "诸", + "弧", + "励", + "梅", + "奶", + "洁", + "灾", + "舟", + "鉴", + "苯", + "讼", + "抱", + "毁", + "懂", + "寒", + "智", + "埔", + "寄", + "届", + "跃", + "渡", + "挑", + "丹", + "艰", + "贝", + "碰", + "拔", + "爹", + "戴", + "码", + "梦", + "芽", + "熔", + "赤", + "渔", + "哭", + "敬", + "颗", + "奔", + "铅", + "仲", + "虎", + "稀", + "妹", + "乏", + "珍", + "申", + "桌", + "遵", + "允", + "隆", + "螺", + "仓", + "魏", + "锐", + "晓", + "氮", + "兼", + "隐", + "碍", + "赫", + "拨", + "忠", + "肃", + "缸", + "牵", + "抢", + "博", + "巧", + "壳", + "兄", + "杜", + "讯", + "诚", + "碧", + "祥", + "柯", + "页", + "巡", + "矩", + "悲", + "灌", + "龄", + "伦", + "票", + "寻", + "桂", + "铺", + "圣", + "恐", + "恰", + "郑", + "趣", + "抬", + "荒", + "腾", + "贴", + "柔", + "滴", + "猛", + "阔", + "辆", + "妻", + "填", + "撤", + "储", + "签", + "闹", + "扰", + "紫", + "砂", + "递", + "戏", + "吊", + "陶", + "伐", + "喂", + "疗", + "瓶", + "婆", + "抚", + "臂", + "摸", + "忍", + "虾", + "蜡", + "邻", + "胸", + "巩", + "挤", + "偶", + "弃", + "槽", + "劲", + "乳", + "邓", + "吉", + "仁", + "烂", + "砖", + "租", + "乌", + "舰", + "伴", + "瓜", + "浅", + "丙", + "暂", + "燥", + "橡", + "柳", + "迷", + "暖", + "牌", + "秧", + "胆", + "详", + "簧", + "踏", + "瓷", + "谱", + "呆", + "宾", + "糊", + "洛", + "辉", + "愤", + "竞", + "隙", + "怒", + "粘", + "乃", + "绪", + "肩", + "籍", + "敏", + "涂", + "熙", + "皆", + "侦", + "悬", + "掘", + "享", + "纠", + "醒", + "狂", + "锁", + "淀", + "恨", + "牲", + "霸", + "爬", + "赏", + "逆", + "玩", + "陵", + "祝", + "秒", + "浙", + "貌" + ]; +} \ No newline at end of file diff --git a/cw_salvium/lib/mnemonics/dutch.dart b/cw_salvium/lib/mnemonics/dutch.dart new file mode 100644 index 0000000000..3a1d00cfca --- /dev/null +++ b/cw_salvium/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_salvium/lib/mnemonics/english.dart b/cw_salvium/lib/mnemonics/english.dart new file mode 100644 index 0000000000..fb464d04e8 --- /dev/null +++ b/cw_salvium/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_salvium/lib/mnemonics/french.dart b/cw_salvium/lib/mnemonics/french.dart new file mode 100644 index 0000000000..76d556f6a2 --- /dev/null +++ b/cw_salvium/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_salvium/lib/mnemonics/german.dart b/cw_salvium/lib/mnemonics/german.dart new file mode 100644 index 0000000000..1491c9b0ed --- /dev/null +++ b/cw_salvium/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_salvium/lib/mnemonics/italian.dart b/cw_salvium/lib/mnemonics/italian.dart new file mode 100644 index 0000000000..275f85bf44 --- /dev/null +++ b/cw_salvium/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_salvium/lib/mnemonics/japanese.dart b/cw_salvium/lib/mnemonics/japanese.dart new file mode 100644 index 0000000000..5d17fdb147 --- /dev/null +++ b/cw_salvium/lib/mnemonics/japanese.dart @@ -0,0 +1,1630 @@ +class JapaneseMnemonics { + static const words = [ + "あいこくしん", + "あいさつ", + "あいだ", + "あおぞら", + "あかちゃん", + "あきる", + "あけがた", + "あける", + "あこがれる", + "あさい", + "あさひ", + "あしあと", + "あじわう", + "あずかる", + "あずき", + "あそぶ", + "あたえる", + "あたためる", + "あたりまえ", + "あたる", + "あつい", + "あつかう", + "あっしゅく", + "あつまり", + "あつめる", + "あてな", + "あてはまる", + "あひる", + "あぶら", + "あぶる", + "あふれる", + "あまい", + "あまど", + "あまやかす", + "あまり", + "あみもの", + "あめりか", + "あやまる", + "あゆむ", + "あらいぐま", + "あらし", + "あらすじ", + "あらためる", + "あらゆる", + "あらわす", + "ありがとう", + "あわせる", + "あわてる", + "あんい", + "あんがい", + "あんこ", + "あんぜん", + "あんてい", + "あんない", + "あんまり", + "いいだす", + "いおん", + "いがい", + "いがく", + "いきおい", + "いきなり", + "いきもの", + "いきる", + "いくじ", + "いくぶん", + "いけばな", + "いけん", + "いこう", + "いこく", + "いこつ", + "いさましい", + "いさん", + "いしき", + "いじゅう", + "いじょう", + "いじわる", + "いずみ", + "いずれ", + "いせい", + "いせえび", + "いせかい", + "いせき", + "いぜん", + "いそうろう", + "いそがしい", + "いだい", + "いだく", + "いたずら", + "いたみ", + "いたりあ", + "いちおう", + "いちじ", + "いちど", + "いちば", + "いちぶ", + "いちりゅう", + "いつか", + "いっしゅん", + "いっせい", + "いっそう", + "いったん", + "いっち", + "いってい", + "いっぽう", + "いてざ", + "いてん", + "いどう", + "いとこ", + "いない", + "いなか", + "いねむり", + "いのち", + "いのる", + "いはつ", + "いばる", + "いはん", + "いびき", + "いひん", + "いふく", + "いへん", + "いほう", + "いみん", + "いもうと", + "いもたれ", + "いもり", + "いやがる", + "いやす", + "いよかん", + "いよく", + "いらい", + "いらすと", + "いりぐち", + "いりょう", + "いれい", + "いれもの", + "いれる", + "いろえんぴつ", + "いわい", + "いわう", + "いわかん", + "いわば", + "いわゆる", + "いんげんまめ", + "いんさつ", + "いんしょう", + "いんよう", + "うえき", + "うえる", + "うおざ", + "うがい", + "うかぶ", + "うかべる", + "うきわ", + "うくらいな", + "うくれれ", + "うけたまわる", + "うけつけ", + "うけとる", + "うけもつ", + "うける", + "うごかす", + "うごく", + "うこん", + "うさぎ", + "うしなう", + "うしろがみ", + "うすい", + "うすぎ", + "うすぐらい", + "うすめる", + "うせつ", + "うちあわせ", + "うちがわ", + "うちき", + "うちゅう", + "うっかり", + "うつくしい", + "うったえる", + "うつる", + "うどん", + "うなぎ", + "うなじ", + "うなずく", + "うなる", + "うねる", + "うのう", + "うぶげ", + "うぶごえ", + "うまれる", + "うめる", + "うもう", + "うやまう", + "うよく", + "うらがえす", + "うらぐち", + "うらない", + "うりあげ", + "うりきれ", + "うるさい", + "うれしい", + "うれゆき", + "うれる", + "うろこ", + "うわき", + "うわさ", + "うんこう", + "うんちん", + "うんてん", + "うんどう", + "えいえん", + "えいが", + "えいきょう", + "えいご", + "えいせい", + "えいぶん", + "えいよう", + "えいわ", + "えおり", + "えがお", + "えがく", + "えきたい", + "えくせる", + "えしゃく", + "えすて", + "えつらん", + "えのぐ", + "えほうまき", + "えほん", + "えまき", + "えもじ", + "えもの", + "えらい", + "えらぶ", + "えりあ", + "えんえん", + "えんかい", + "えんぎ", + "えんげき", + "えんしゅう", + "えんぜつ", + "えんそく", + "えんちょう", + "えんとつ", + "おいかける", + "おいこす", + "おいしい", + "おいつく", + "おうえん", + "おうさま", + "おうじ", + "おうせつ", + "おうたい", + "おうふく", + "おうべい", + "おうよう", + "おえる", + "おおい", + "おおう", + "おおどおり", + "おおや", + "おおよそ", + "おかえり", + "おかず", + "おがむ", + "おかわり", + "おぎなう", + "おきる", + "おくさま", + "おくじょう", + "おくりがな", + "おくる", + "おくれる", + "おこす", + "おこなう", + "おこる", + "おさえる", + "おさない", + "おさめる", + "おしいれ", + "おしえる", + "おじぎ", + "おじさん", + "おしゃれ", + "おそらく", + "おそわる", + "おたがい", + "おたく", + "おだやか", + "おちつく", + "おっと", + "おつり", + "おでかけ", + "おとしもの", + "おとなしい", + "おどり", + "おどろかす", + "おばさん", + "おまいり", + "おめでとう", + "おもいで", + "おもう", + "おもたい", + "おもちゃ", + "おやつ", + "おやゆび", + "およぼす", + "おらんだ", + "おろす", + "おんがく", + "おんけい", + "おんしゃ", + "おんせん", + "おんだん", + "おんちゅう", + "おんどけい", + "かあつ", + "かいが", + "がいき", + "がいけん", + "がいこう", + "かいさつ", + "かいしゃ", + "かいすいよく", + "かいぜん", + "かいぞうど", + "かいつう", + "かいてん", + "かいとう", + "かいふく", + "がいへき", + "かいほう", + "かいよう", + "がいらい", + "かいわ", + "かえる", + "かおり", + "かかえる", + "かがく", + "かがし", + "かがみ", + "かくご", + "かくとく", + "かざる", + "がぞう", + "かたい", + "かたち", + "がちょう", + "がっきゅう", + "がっこう", + "がっさん", + "がっしょう", + "かなざわし", + "かのう", + "がはく", + "かぶか", + "かほう", + "かほご", + "かまう", + "かまぼこ", + "かめれおん", + "かゆい", + "かようび", + "からい", + "かるい", + "かろう", + "かわく", + "かわら", + "がんか", + "かんけい", + "かんこう", + "かんしゃ", + "かんそう", + "かんたん", + "かんち", + "がんばる", + "きあい", + "きあつ", + "きいろ", + "ぎいん", + "きうい", + "きうん", + "きえる", + "きおう", + "きおく", + "きおち", + "きおん", + "きかい", + "きかく", + "きかんしゃ", + "ききて", + "きくばり", + "きくらげ", + "きけんせい", + "きこう", + "きこえる", + "きこく", + "きさい", + "きさく", + "きさま", + "きさらぎ", + "ぎじかがく", + "ぎしき", + "ぎじたいけん", + "ぎじにってい", + "ぎじゅつしゃ", + "きすう", + "きせい", + "きせき", + "きせつ", + "きそう", + "きぞく", + "きぞん", + "きたえる", + "きちょう", + "きつえん", + "ぎっちり", + "きつつき", + "きつね", + "きてい", + "きどう", + "きどく", + "きない", + "きなが", + "きなこ", + "きぬごし", + "きねん", + "きのう", + "きのした", + "きはく", + "きびしい", + "きひん", + "きふく", + "きぶん", + "きぼう", + "きほん", + "きまる", + "きみつ", + "きむずかしい", + "きめる", + "きもだめし", + "きもち", + "きもの", + "きゃく", + "きやく", + "ぎゅうにく", + "きよう", + "きょうりゅう", + "きらい", + "きらく", + "きりん", + "きれい", + "きれつ", + "きろく", + "ぎろん", + "きわめる", + "ぎんいろ", + "きんかくじ", + "きんじょ", + "きんようび", + "ぐあい", + "くいず", + "くうかん", + "くうき", + "くうぐん", + "くうこう", + "ぐうせい", + "くうそう", + "ぐうたら", + "くうふく", + "くうぼ", + "くかん", + "くきょう", + "くげん", + "ぐこう", + "くさい", + "くさき", + "くさばな", + "くさる", + "くしゃみ", + "くしょう", + "くすのき", + "くすりゆび", + "くせげ", + "くせん", + "ぐたいてき", + "くださる", + "くたびれる", + "くちこみ", + "くちさき", + "くつした", + "ぐっすり", + "くつろぐ", + "くとうてん", + "くどく", + "くなん", + "くねくね", + "くのう", + "くふう", + "くみあわせ", + "くみたてる", + "くめる", + "くやくしょ", + "くらす", + "くらべる", + "くるま", + "くれる", + "くろう", + "くわしい", + "ぐんかん", + "ぐんしょく", + "ぐんたい", + "ぐんて", + "けあな", + "けいかく", + "けいけん", + "けいこ", + "けいさつ", + "げいじゅつ", + "けいたい", + "げいのうじん", + "けいれき", + "けいろ", + "けおとす", + "けおりもの", + "げきか", + "げきげん", + "げきだん", + "げきちん", + "げきとつ", + "げきは", + "げきやく", + "げこう", + "げこくじょう", + "げざい", + "けさき", + "げざん", + "けしき", + "けしごむ", + "けしょう", + "げすと", + "けたば", + "けちゃっぷ", + "けちらす", + "けつあつ", + "けつい", + "けつえき", + "けっこん", + "けつじょ", + "けっせき", + "けってい", + "けつまつ", + "げつようび", + "げつれい", + "けつろん", + "げどく", + "けとばす", + "けとる", + "けなげ", + "けなす", + "けなみ", + "けぬき", + "げねつ", + "けねん", + "けはい", + "げひん", + "けぶかい", + "げぼく", + "けまり", + "けみかる", + "けむし", + "けむり", + "けもの", + "けらい", + "けろけろ", + "けわしい", + "けんい", + "けんえつ", + "けんお", + "けんか", + "げんき", + "けんげん", + "けんこう", + "けんさく", + "けんしゅう", + "けんすう", + "げんそう", + "けんちく", + "けんてい", + "けんとう", + "けんない", + "けんにん", + "げんぶつ", + "けんま", + "けんみん", + "けんめい", + "けんらん", + "けんり", + "こあくま", + "こいぬ", + "こいびと", + "ごうい", + "こうえん", + "こうおん", + "こうかん", + "ごうきゅう", + "ごうけい", + "こうこう", + "こうさい", + "こうじ", + "こうすい", + "ごうせい", + "こうそく", + "こうたい", + "こうちゃ", + "こうつう", + "こうてい", + "こうどう", + "こうない", + "こうはい", + "ごうほう", + "ごうまん", + "こうもく", + "こうりつ", + "こえる", + "こおり", + "ごかい", + "ごがつ", + "ごかん", + "こくご", + "こくさい", + "こくとう", + "こくない", + "こくはく", + "こぐま", + "こけい", + "こける", + "ここのか", + "こころ", + "こさめ", + "こしつ", + "こすう", + "こせい", + "こせき", + "こぜん", + "こそだて", + "こたい", + "こたえる", + "こたつ", + "こちょう", + "こっか", + "こつこつ", + "こつばん", + "こつぶ", + "こてい", + "こてん", + "ことがら", + "ことし", + "ことば", + "ことり", + "こなごな", + "こねこね", + "このまま", + "このみ", + "このよ", + "ごはん", + "こひつじ", + "こふう", + "こふん", + "こぼれる", + "ごまあぶら", + "こまかい", + "ごますり", + "こまつな", + "こまる", + "こむぎこ", + "こもじ", + "こもち", + "こもの", + "こもん", + "こやく", + "こやま", + "こゆう", + "こゆび", + "こよい", + "こよう", + "こりる", + "これくしょん", + "ころっけ", + "こわもて", + "こわれる", + "こんいん", + "こんかい", + "こんき", + "こんしゅう", + "こんすい", + "こんだて", + "こんとん", + "こんなん", + "こんびに", + "こんぽん", + "こんまけ", + "こんや", + "こんれい", + "こんわく", + "ざいえき", + "さいかい", + "さいきん", + "ざいげん", + "ざいこ", + "さいしょ", + "さいせい", + "ざいたく", + "ざいちゅう", + "さいてき", + "ざいりょう", + "さうな", + "さかいし", + "さがす", + "さかな", + "さかみち", + "さがる", + "さぎょう", + "さくし", + "さくひん", + "さくら", + "さこく", + "さこつ", + "さずかる", + "ざせき", + "さたん", + "さつえい", + "ざつおん", + "ざっか", + "ざつがく", + "さっきょく", + "ざっし", + "さつじん", + "ざっそう", + "さつたば", + "さつまいも", + "さてい", + "さといも", + "さとう", + "さとおや", + "さとし", + "さとる", + "さのう", + "さばく", + "さびしい", + "さべつ", + "さほう", + "さほど", + "さます", + "さみしい", + "さみだれ", + "さむけ", + "さめる", + "さやえんどう", + "さゆう", + "さよう", + "さよく", + "さらだ", + "ざるそば", + "さわやか", + "さわる", + "さんいん", + "さんか", + "さんきゃく", + "さんこう", + "さんさい", + "ざんしょ", + "さんすう", + "さんせい", + "さんそ", + "さんち", + "さんま", + "さんみ", + "さんらん", + "しあい", + "しあげ", + "しあさって", + "しあわせ", + "しいく", + "しいん", + "しうち", + "しえい", + "しおけ", + "しかい", + "しかく", + "じかん", + "しごと", + "しすう", + "じだい", + "したうけ", + "したぎ", + "したて", + "したみ", + "しちょう", + "しちりん", + "しっかり", + "しつじ", + "しつもん", + "してい", + "してき", + "してつ", + "じてん", + "じどう", + "しなぎれ", + "しなもの", + "しなん", + "しねま", + "しねん", + "しのぐ", + "しのぶ", + "しはい", + "しばかり", + "しはつ", + "しはらい", + "しはん", + "しひょう", + "しふく", + "じぶん", + "しへい", + "しほう", + "しほん", + "しまう", + "しまる", + "しみん", + "しむける", + "じむしょ", + "しめい", + "しめる", + "しもん", + "しゃいん", + "しゃうん", + "しゃおん", + "じゃがいも", + "しやくしょ", + "しゃくほう", + "しゃけん", + "しゃこ", + "しゃざい", + "しゃしん", + "しゃせん", + "しゃそう", + "しゃたい", + "しゃちょう", + "しゃっきん", + "じゃま", + "しゃりん", + "しゃれい", + "じゆう", + "じゅうしょ", + "しゅくはく", + "じゅしん", + "しゅっせき", + "しゅみ", + "しゅらば", + "じゅんばん", + "しょうかい", + "しょくたく", + "しょっけん", + "しょどう", + "しょもつ", + "しらせる", + "しらべる", + "しんか", + "しんこう", + "じんじゃ", + "しんせいじ", + "しんちく", + "しんりん", + "すあげ", + "すあし", + "すあな", + "ずあん", + "すいえい", + "すいか", + "すいとう", + "ずいぶん", + "すいようび", + "すうがく", + "すうじつ", + "すうせん", + "すおどり", + "すきま", + "すくう", + "すくない", + "すける", + "すごい", + "すこし", + "ずさん", + "すずしい", + "すすむ", + "すすめる", + "すっかり", + "ずっしり", + "ずっと", + "すてき", + "すてる", + "すねる", + "すのこ", + "すはだ", + "すばらしい", + "ずひょう", + "ずぶぬれ", + "すぶり", + "すふれ", + "すべて", + "すべる", + "ずほう", + "すぼん", + "すまい", + "すめし", + "すもう", + "すやき", + "すらすら", + "するめ", + "すれちがう", + "すろっと", + "すわる", + "すんぜん", + "すんぽう", + "せあぶら", + "せいかつ", + "せいげん", + "せいじ", + "せいよう", + "せおう", + "せかいかん", + "せきにん", + "せきむ", + "せきゆ", + "せきらんうん", + "せけん", + "せこう", + "せすじ", + "せたい", + "せたけ", + "せっかく", + "せっきゃく", + "ぜっく", + "せっけん", + "せっこつ", + "せっさたくま", + "せつぞく", + "せつだん", + "せつでん", + "せっぱん", + "せつび", + "せつぶん", + "せつめい", + "せつりつ", + "せなか", + "せのび", + "せはば", + "せびろ", + "せぼね", + "せまい", + "せまる", + "せめる", + "せもたれ", + "せりふ", + "ぜんあく", + "せんい", + "せんえい", + "せんか", + "せんきょ", + "せんく", + "せんげん", + "ぜんご", + "せんさい", + "せんしゅ", + "せんすい", + "せんせい", + "せんぞ", + "せんたく", + "せんちょう", + "せんてい", + "せんとう", + "せんぬき", + "せんねん", + "せんぱい", + "ぜんぶ", + "ぜんぽう", + "せんむ", + "せんめんじょ", + "せんもん", + "せんやく", + "せんゆう", + "せんよう", + "ぜんら", + "ぜんりゃく", + "せんれい", + "せんろ", + "そあく", + "そいとげる", + "そいね", + "そうがんきょう", + "そうき", + "そうご", + "そうしん", + "そうだん", + "そうなん", + "そうび", + "そうめん", + "そうり", + "そえもの", + "そえん", + "そがい", + "そげき", + "そこう", + "そこそこ", + "そざい", + "そしな", + "そせい", + "そせん", + "そそぐ", + "そだてる", + "そつう", + "そつえん", + "そっかん", + "そつぎょう", + "そっけつ", + "そっこう", + "そっせん", + "そっと", + "そとがわ", + "そとづら", + "そなえる", + "そなた", + "そふぼ", + "そぼく", + "そぼろ", + "そまつ", + "そまる", + "そむく", + "そむりえ", + "そめる", + "そもそも", + "そよかぜ", + "そらまめ", + "そろう", + "そんかい", + "そんけい", + "そんざい", + "そんしつ", + "そんぞく", + "そんちょう", + "ぞんび", + "ぞんぶん", + "そんみん", + "たあい", + "たいいん", + "たいうん", + "たいえき", + "たいおう", + "だいがく", + "たいき", + "たいぐう", + "たいけん", + "たいこ", + "たいざい", + "だいじょうぶ", + "だいすき", + "たいせつ", + "たいそう", + "だいたい", + "たいちょう", + "たいてい", + "だいどころ", + "たいない", + "たいねつ", + "たいのう", + "たいはん", + "だいひょう", + "たいふう", + "たいへん", + "たいほ", + "たいまつばな", + "たいみんぐ", + "たいむ", + "たいめん", + "たいやき", + "たいよう", + "たいら", + "たいりょく", + "たいる", + "たいわん", + "たうえ", + "たえる", + "たおす", + "たおる", + "たおれる", + "たかい", + "たかね", + "たきび", + "たくさん", + "たこく", + "たこやき", + "たさい", + "たしざん", + "だじゃれ", + "たすける", + "たずさわる", + "たそがれ", + "たたかう", + "たたく", + "ただしい", + "たたみ", + "たちばな", + "だっかい", + "だっきゃく", + "だっこ", + "だっしゅつ", + "だったい", + "たてる", + "たとえる", + "たなばた", + "たにん", + "たぬき", + "たのしみ", + "たはつ", + "たぶん", + "たべる", + "たぼう", + "たまご", + "たまる", + "だむる", + "ためいき", + "ためす", + "ためる", + "たもつ", + "たやすい", + "たよる", + "たらす", + "たりきほんがん", + "たりょう", + "たりる", + "たると", + "たれる", + "たれんと", + "たろっと", + "たわむれる", + "だんあつ", + "たんい", + "たんおん", + "たんか", + "たんき", + "たんけん", + "たんご", + "たんさん", + "たんじょうび", + "だんせい", + "たんそく", + "たんたい", + "だんち", + "たんてい", + "たんとう", + "だんな", + "たんにん", + "だんねつ", + "たんのう", + "たんぴん", + "だんぼう", + "たんまつ", + "たんめい", + "だんれつ", + "だんろ", + "だんわ", + "ちあい", + "ちあん", + "ちいき", + "ちいさい", + "ちえん", + "ちかい", + "ちから", + "ちきゅう", + "ちきん", + "ちけいず", + "ちけん", + "ちこく", + "ちさい", + "ちしき", + "ちしりょう", + "ちせい", + "ちそう", + "ちたい", + "ちたん", + "ちちおや", + "ちつじょ", + "ちてき", + "ちてん", + "ちぬき", + "ちぬり", + "ちのう", + "ちひょう", + "ちへいせん", + "ちほう", + "ちまた", + "ちみつ", + "ちみどろ", + "ちめいど", + "ちゃんこなべ", + "ちゅうい", + "ちゆりょく", + "ちょうし", + "ちょさくけん", + "ちらし", + "ちらみ", + "ちりがみ", + "ちりょう", + "ちるど", + "ちわわ", + "ちんたい", + "ちんもく", + "ついか", + "ついたち", + "つうか", + "つうじょう", + "つうはん", + "つうわ", + "つかう", + "つかれる", + "つくね", + "つくる", + "つけね", + "つける", + "つごう", + "つたえる", + "つづく", + "つつじ", + "つつむ", + "つとめる", + "つながる", + "つなみ", + "つねづね", + "つのる", + "つぶす", + "つまらない", + "つまる", + "つみき", + "つめたい", + "つもり", + "つもる", + "つよい", + "つるぼ", + "つるみく", + "つわもの", + "つわり", + "てあし", + "てあて", + "てあみ", + "ていおん", + "ていか", + "ていき", + "ていけい", + "ていこく", + "ていさつ", + "ていし", + "ていせい", + "ていたい", + "ていど", + "ていねい", + "ていひょう", + "ていへん", + "ていぼう", + "てうち", + "ておくれ", + "てきとう", + "てくび", + "でこぼこ", + "てさぎょう", + "てさげ", + "てすり", + "てそう", + "てちがい", + "てちょう", + "てつがく", + "てつづき", + "でっぱ", + "てつぼう", + "てつや", + "でぬかえ", + "てぬき", + "てぬぐい", + "てのひら", + "てはい", + "てぶくろ", + "てふだ", + "てほどき", + "てほん", + "てまえ", + "てまきずし", + "てみじか", + "てみやげ", + "てらす", + "てれび", + "てわけ", + "てわたし", + "でんあつ", + "てんいん", + "てんかい", + "てんき", + "てんぐ", + "てんけん", + "てんごく", + "てんさい", + "てんし", + "てんすう", + "でんち", + "てんてき", + "てんとう", + "てんない", + "てんぷら", + "てんぼうだい", + "てんめつ", + "てんらんかい", + "でんりょく", + "でんわ", + "どあい", + "といれ", + "どうかん", + "とうきゅう", + "どうぐ", + "とうし", + "とうむぎ", + "とおい", + "とおか", + "とおく", + "とおす", + "とおる", + "とかい", + "とかす", + "ときおり", + "ときどき", + "とくい", + "とくしゅう", + "とくてん", + "とくに", + "とくべつ", + "とけい", + "とける", + "とこや", + "とさか", + "としょかん", + "とそう", + "とたん", + "とちゅう", + "とっきゅう", + "とっくん", + "とつぜん", + "とつにゅう", + "とどける", + "ととのえる", + "とない", + "となえる", + "となり", + "とのさま", + "とばす", + "どぶがわ", + "とほう", + "とまる", + "とめる", + "ともだち", + "ともる", + "どようび", + "とらえる", + "とんかつ", + "どんぶり", + "ないかく", + "ないこう", + "ないしょ", + "ないす", + "ないせん", + "ないそう", + "なおす", + "ながい", + "なくす", + "なげる", + "なこうど", + "なさけ", + "なたでここ", + "なっとう", + "なつやすみ", + "ななおし", + "なにごと", + "なにもの", + "なにわ", + "なのか", + "なふだ", + "なまいき", + "なまえ", + "なまみ", + "なみだ", + "なめらか", + "なめる", + "なやむ", + "ならう", + "ならび", + "ならぶ", + "なれる", + "なわとび", + "なわばり", + "にあう", + "にいがた", + "にうけ", + "におい", + "にかい", + "にがて", + "にきび", + "にくしみ", + "にくまん", + "にげる", + "にさんかたんそ", + "にしき", + "にせもの", + "にちじょう", + "にちようび", + "にっか", + "にっき", + "にっけい", + "にっこう", + "にっさん", + "にっしょく", + "にっすう", + "にっせき", + "にってい", + "になう", + "にほん", + "にまめ", + "にもつ", + "にやり", + "にゅういん", + "にりんしゃ", + "にわとり", + "にんい", + "にんか", + "にんき", + "にんげん", + "にんしき", + "にんずう", + "にんそう", + "にんたい", + "にんち", + "にんてい", + "にんにく", + "にんぷ", + "にんまり", + "にんむ", + "にんめい", + "にんよう", + "ぬいくぎ", + "ぬかす", + "ぬぐいとる", + "ぬぐう", + "ぬくもり", + "ぬすむ", + "ぬまえび", + "ぬめり", + "ぬらす", + "ぬんちゃく", + "ねあげ", + "ねいき", + "ねいる", + "ねいろ", + "ねぐせ", + "ねくたい", + "ねくら", + "ねこぜ", + "ねこむ", + "ねさげ", + "ねすごす", + "ねそべる", + "ねだん", + "ねつい", + "ねっしん", + "ねつぞう", + "ねったいぎょ", + "ねぶそく", + "ねふだ", + "ねぼう", + "ねほりはほり", + "ねまき", + "ねまわし", + "ねみみ", + "ねむい", + "ねむたい", + "ねもと", + "ねらう", + "ねわざ", + "ねんいり", + "ねんおし", + "ねんかん", + "ねんきん", + "ねんぐ", + "ねんざ", + "ねんし", + "ねんちゃく", + "ねんど", + "ねんぴ", + "ねんぶつ", + "ねんまつ", + "ねんりょう", + "ねんれい", + "のいず", + "のおづま", + "のがす", + "のきなみ", + "のこぎり", + "のこす", + "のこる", + "のせる", + "のぞく", + "のぞむ", + "のたまう", + "のちほど", + "のっく", + "のばす", + "のはら", + "のべる", + "のぼる", + "のみもの", + "のやま", + "のらいぬ", + "のらねこ", + "のりもの", + "のりゆき", + "のれん", + "のんき", + "ばあい", + "はあく", + "ばあさん", + "ばいか", + "ばいく", + "はいけん", + "はいご", + "はいしん", + "はいすい", + "はいせん", + "はいそう", + "はいち", + "ばいばい", + "はいれつ", + "はえる", + "はおる", + "はかい", + "ばかり", + "はかる", + "はくしゅ", + "はけん", + "はこぶ", + "はさみ", + "はさん", + "はしご", + "ばしょ", + "はしる", + "はせる", + "ぱそこん", + "はそん", + "はたん", + "はちみつ", + "はつおん", + "はっかく", + "はづき", + "はっきり", + "はっくつ", + "はっけん", + "はっこう", + "はっさん", + "はっしん", + "はったつ", + "はっちゅう", + "はってん", + "はっぴょう", + "はっぽう", + "はなす", + "はなび", + "はにかむ", + "はぶらし", + "はみがき", + "はむかう", + "はめつ", + "はやい", + "はやし", + "はらう", + "はろうぃん", + "はわい", + "はんい", + "はんえい", + "はんおん", + "はんかく", + "はんきょう", + "ばんぐみ", + "はんこ", + "はんしゃ", + "はんすう", + "はんだん", + "ぱんち", + "ぱんつ", + "はんてい", + "はんとし", + "はんのう", + "はんぱ", + "はんぶん", + "はんぺん", + "はんぼうき", + "はんめい", + "はんらん", + "はんろん", + "ひいき", + "ひうん", + "ひえる", + "ひかく", + "ひかり", + "ひかる", + "ひかん", + "ひくい", + "ひけつ", + "ひこうき", + "ひこく", + "ひさい", + "ひさしぶり", + "ひさん", + "びじゅつかん", + "ひしょ" + ]; +} \ No newline at end of file diff --git a/cw_salvium/lib/mnemonics/portuguese.dart b/cw_salvium/lib/mnemonics/portuguese.dart new file mode 100644 index 0000000000..bdd63d3b28 --- /dev/null +++ b/cw_salvium/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_salvium/lib/mnemonics/russian.dart b/cw_salvium/lib/mnemonics/russian.dart new file mode 100644 index 0000000000..f10af0ff6d --- /dev/null +++ b/cw_salvium/lib/mnemonics/russian.dart @@ -0,0 +1,1630 @@ +class RussianMnemonics { + static const words = [ + "абажур", + "абзац", + "абонент", + "абрикос", + "абсурд", + "авангард", + "август", + "авиация", + "авоська", + "автор", + "агат", + "агент", + "агитатор", + "агнец", + "агония", + "агрегат", + "адвокат", + "адмирал", + "адрес", + "ажиотаж", + "азарт", + "азбука", + "азот", + "аист", + "айсберг", + "академия", + "аквариум", + "аккорд", + "акробат", + "аксиома", + "актер", + "акула", + "акция", + "алгоритм", + "алебарда", + "аллея", + "алмаз", + "алтарь", + "алфавит", + "алхимик", + "алый", + "альбом", + "алюминий", + "амбар", + "аметист", + "амнезия", + "ампула", + "амфора", + "анализ", + "ангел", + "анекдот", + "анимация", + "анкета", + "аномалия", + "ансамбль", + "антенна", + "апатия", + "апельсин", + "апофеоз", + "аппарат", + "апрель", + "аптека", + "арабский", + "арбуз", + "аргумент", + "арест", + "ария", + "арка", + "армия", + "аромат", + "арсенал", + "артист", + "архив", + "аршин", + "асбест", + "аскетизм", + "аспект", + "ассорти", + "астроном", + "асфальт", + "атака", + "ателье", + "атлас", + "атом", + "атрибут", + "аудитор", + "аукцион", + "аура", + "афера", + "афиша", + "ахинея", + "ацетон", + "аэропорт", + "бабушка", + "багаж", + "бадья", + "база", + "баклажан", + "балкон", + "бампер", + "банк", + "барон", + "бассейн", + "батарея", + "бахрома", + "башня", + "баян", + "бегство", + "бедро", + "бездна", + "бекон", + "белый", + "бензин", + "берег", + "беседа", + "бетонный", + "биатлон", + "библия", + "бивень", + "бигуди", + "бидон", + "бизнес", + "бикини", + "билет", + "бинокль", + "биология", + "биржа", + "бисер", + "битва", + "бицепс", + "благо", + "бледный", + "близкий", + "блок", + "блуждать", + "блюдо", + "бляха", + "бобер", + "богатый", + "бодрый", + "боевой", + "бокал", + "большой", + "борьба", + "босой", + "ботинок", + "боцман", + "бочка", + "боярин", + "брать", + "бревно", + "бригада", + "бросать", + "брызги", + "брюки", + "бублик", + "бугор", + "будущее", + "буква", + "бульвар", + "бумага", + "бунт", + "бурный", + "бусы", + "бутылка", + "буфет", + "бухта", + "бушлат", + "бывалый", + "быль", + "быстрый", + "быть", + "бюджет", + "бюро", + "бюст", + "вагон", + "важный", + "ваза", + "вакцина", + "валюта", + "вампир", + "ванная", + "вариант", + "вассал", + "вата", + "вафля", + "вахта", + "вдова", + "вдыхать", + "ведущий", + "веер", + "вежливый", + "везти", + "веко", + "великий", + "вена", + "верить", + "веселый", + "ветер", + "вечер", + "вешать", + "вещь", + "веяние", + "взаимный", + "взбучка", + "взвод", + "взгляд", + "вздыхать", + "взлетать", + "взмах", + "взнос", + "взор", + "взрыв", + "взывать", + "взятка", + "вибрация", + "визит", + "вилка", + "вино", + "вирус", + "висеть", + "витрина", + "вихрь", + "вишневый", + "включать", + "вкус", + "власть", + "влечь", + "влияние", + "влюблять", + "внешний", + "внимание", + "внук", + "внятный", + "вода", + "воевать", + "вождь", + "воздух", + "войти", + "вокзал", + "волос", + "вопрос", + "ворота", + "восток", + "впадать", + "впускать", + "врач", + "время", + "вручать", + "всадник", + "всеобщий", + "вспышка", + "встреча", + "вторник", + "вулкан", + "вурдалак", + "входить", + "въезд", + "выбор", + "вывод", + "выгодный", + "выделять", + "выезжать", + "выживать", + "вызывать", + "выигрыш", + "вылезать", + "выносить", + "выпивать", + "высокий", + "выходить", + "вычет", + "вышка", + "выяснять", + "вязать", + "вялый", + "гавань", + "гадать", + "газета", + "гаишник", + "галстук", + "гамма", + "гарантия", + "гастроли", + "гвардия", + "гвоздь", + "гектар", + "гель", + "генерал", + "геолог", + "герой", + "гешефт", + "гибель", + "гигант", + "гильза", + "гимн", + "гипотеза", + "гитара", + "глаз", + "глина", + "глоток", + "глубокий", + "глыба", + "глядеть", + "гнать", + "гнев", + "гнить", + "гном", + "гнуть", + "говорить", + "годовой", + "голова", + "гонка", + "город", + "гость", + "готовый", + "граница", + "грех", + "гриб", + "громкий", + "группа", + "грызть", + "грязный", + "губа", + "гудеть", + "гулять", + "гуманный", + "густой", + "гуща", + "давать", + "далекий", + "дама", + "данные", + "дарить", + "дать", + "дача", + "дверь", + "движение", + "двор", + "дебют", + "девушка", + "дедушка", + "дежурный", + "дезертир", + "действие", + "декабрь", + "дело", + "демократ", + "день", + "депутат", + "держать", + "десяток", + "детский", + "дефицит", + "дешевый", + "деятель", + "джаз", + "джинсы", + "джунгли", + "диалог", + "диван", + "диета", + "дизайн", + "дикий", + "динамика", + "диплом", + "директор", + "диск", + "дитя", + "дичь", + "длинный", + "дневник", + "добрый", + "доверие", + "договор", + "дождь", + "доза", + "документ", + "должен", + "домашний", + "допрос", + "дорога", + "доход", + "доцент", + "дочь", + "дощатый", + "драка", + "древний", + "дрожать", + "друг", + "дрянь", + "дубовый", + "дуга", + "дудка", + "дукат", + "дуло", + "думать", + "дупло", + "дурак", + "дуть", + "духи", + "душа", + "дуэт", + "дымить", + "дыня", + "дыра", + "дыханье", + "дышать", + "дьявол", + "дюжина", + "дюйм", + "дюна", + "дядя", + "дятел", + "егерь", + "единый", + "едкий", + "ежевика", + "ежик", + "езда", + "елка", + "емкость", + "ерунда", + "ехать", + "жадный", + "жажда", + "жалеть", + "жанр", + "жара", + "жать", + "жгучий", + "ждать", + "жевать", + "желание", + "жемчуг", + "женщина", + "жертва", + "жесткий", + "жечь", + "живой", + "жидкость", + "жизнь", + "жилье", + "жирный", + "житель", + "журнал", + "жюри", + "забывать", + "завод", + "загадка", + "задача", + "зажечь", + "зайти", + "закон", + "замечать", + "занимать", + "западный", + "зарплата", + "засыпать", + "затрата", + "захват", + "зацепка", + "зачет", + "защита", + "заявка", + "звать", + "звезда", + "звонить", + "звук", + "здание", + "здешний", + "здоровье", + "зебра", + "зевать", + "зеленый", + "земля", + "зенит", + "зеркало", + "зефир", + "зигзаг", + "зима", + "зиять", + "злак", + "злой", + "змея", + "знать", + "зной", + "зодчий", + "золотой", + "зомби", + "зона", + "зоопарк", + "зоркий", + "зрачок", + "зрение", + "зритель", + "зубной", + "зыбкий", + "зять", + "игла", + "иголка", + "играть", + "идея", + "идиот", + "идол", + "идти", + "иерархия", + "избрать", + "известие", + "изгонять", + "издание", + "излагать", + "изменять", + "износ", + "изоляция", + "изрядный", + "изучать", + "изымать", + "изящный", + "икона", + "икра", + "иллюзия", + "имбирь", + "иметь", + "имидж", + "иммунный", + "империя", + "инвестор", + "индивид", + "инерция", + "инженер", + "иномарка", + "институт", + "интерес", + "инфекция", + "инцидент", + "ипподром", + "ирис", + "ирония", + "искать", + "история", + "исходить", + "исчезать", + "итог", + "июль", + "июнь", + "кабинет", + "кавалер", + "кадр", + "казарма", + "кайф", + "кактус", + "калитка", + "камень", + "канал", + "капитан", + "картина", + "касса", + "катер", + "кафе", + "качество", + "каша", + "каюта", + "квартира", + "квинтет", + "квота", + "кедр", + "кекс", + "кенгуру", + "кепка", + "керосин", + "кетчуп", + "кефир", + "кибитка", + "кивнуть", + "кидать", + "километр", + "кино", + "киоск", + "кипеть", + "кирпич", + "кисть", + "китаец", + "класс", + "клетка", + "клиент", + "клоун", + "клуб", + "клык", + "ключ", + "клятва", + "книга", + "кнопка", + "кнут", + "князь", + "кобура", + "ковер", + "коготь", + "кодекс", + "кожа", + "козел", + "койка", + "коктейль", + "колено", + "компания", + "конец", + "копейка", + "короткий", + "костюм", + "котел", + "кофе", + "кошка", + "красный", + "кресло", + "кричать", + "кровь", + "крупный", + "крыша", + "крючок", + "кубок", + "кувшин", + "кудрявый", + "кузов", + "кукла", + "культура", + "кумир", + "купить", + "курс", + "кусок", + "кухня", + "куча", + "кушать", + "кювет", + "лабиринт", + "лавка", + "лагерь", + "ладонь", + "лазерный", + "лайнер", + "лакей", + "лампа", + "ландшафт", + "лапа", + "ларек", + "ласковый", + "лауреат", + "лачуга", + "лаять", + "лгать", + "лебедь", + "левый", + "легкий", + "ледяной", + "лежать", + "лекция", + "лента", + "лепесток", + "лесной", + "лето", + "лечь", + "леший", + "лживый", + "либерал", + "ливень", + "лига", + "лидер", + "ликовать", + "лиловый", + "лимон", + "линия", + "липа", + "лирика", + "лист", + "литр", + "лифт", + "лихой", + "лицо", + "личный", + "лишний", + "лобовой", + "ловить", + "логика", + "лодка", + "ложка", + "лозунг", + "локоть", + "ломать", + "лоно", + "лопата", + "лорд", + "лось", + "лоток", + "лохматый", + "лошадь", + "лужа", + "лукавый", + "луна", + "лупить", + "лучший", + "лыжный", + "лысый", + "львиный", + "льгота", + "льдина", + "любить", + "людской", + "люстра", + "лютый", + "лягушка", + "магазин", + "мадам", + "мазать", + "майор", + "максимум", + "мальчик", + "манера", + "март", + "масса", + "мать", + "мафия", + "махать", + "мачта", + "машина", + "маэстро", + "маяк", + "мгла", + "мебель", + "медведь", + "мелкий", + "мемуары", + "менять", + "мера", + "место", + "метод", + "механизм", + "мечтать", + "мешать", + "миграция", + "мизинец", + "микрофон", + "миллион", + "минута", + "мировой", + "миссия", + "митинг", + "мишень", + "младший", + "мнение", + "мнимый", + "могила", + "модель", + "мозг", + "мойка", + "мокрый", + "молодой", + "момент", + "монах", + "море", + "мост", + "мотор", + "мохнатый", + "мочь", + "мошенник", + "мощный", + "мрачный", + "мстить", + "мудрый", + "мужчина", + "музыка", + "мука", + "мумия", + "мундир", + "муравей", + "мусор", + "мутный", + "муфта", + "муха", + "мучить", + "мушкетер", + "мыло", + "мысль", + "мыть", + "мычать", + "мышь", + "мэтр", + "мюзикл", + "мягкий", + "мякиш", + "мясо", + "мятый", + "мячик", + "набор", + "навык", + "нагрузка", + "надежда", + "наемный", + "нажать", + "называть", + "наивный", + "накрыть", + "налог", + "намерен", + "наносить", + "написать", + "народ", + "натура", + "наука", + "нация", + "начать", + "небо", + "невеста", + "негодяй", + "неделя", + "нежный", + "незнание", + "нелепый", + "немалый", + "неправда", + "нервный", + "нести", + "нефть", + "нехватка", + "нечистый", + "неясный", + "нива", + "нижний", + "низкий", + "никель", + "нирвана", + "нить", + "ничья", + "ниша", + "нищий", + "новый", + "нога", + "ножницы", + "ноздря", + "ноль", + "номер", + "норма", + "нота", + "ночь", + "ноша", + "ноябрь", + "нрав", + "нужный", + "нутро", + "нынешний", + "нырнуть", + "ныть", + "нюанс", + "нюхать", + "няня", + "оазис", + "обаяние", + "обвинять", + "обгонять", + "обещать", + "обжигать", + "обзор", + "обида", + "область", + "обмен", + "обнимать", + "оборона", + "образ", + "обучение", + "обходить", + "обширный", + "общий", + "объект", + "обычный", + "обязать", + "овальный", + "овес", + "овощи", + "овраг", + "овца", + "овчарка", + "огненный", + "огонь", + "огромный", + "огурец", + "одежда", + "одинокий", + "одобрить", + "ожидать", + "ожог", + "озарение", + "озеро", + "означать", + "оказать", + "океан", + "оклад", + "окно", + "округ", + "октябрь", + "окурок", + "олень", + "опасный", + "операция", + "описать", + "оплата", + "опора", + "оппонент", + "опрос", + "оптимизм", + "опускать", + "опыт", + "орать", + "орбита", + "орган", + "орден", + "орел", + "оригинал", + "оркестр", + "орнамент", + "оружие", + "осадок", + "освещать", + "осень", + "осина", + "осколок", + "осмотр", + "основной", + "особый", + "осуждать", + "отбор", + "отвечать", + "отдать", + "отец", + "отзыв", + "открытие", + "отмечать", + "относить", + "отпуск", + "отрасль", + "отставка", + "оттенок", + "отходить", + "отчет", + "отъезд", + "офицер", + "охапка", + "охота", + "охрана", + "оценка", + "очаг", + "очередь", + "очищать", + "очки", + "ошейник", + "ошибка", + "ощущение", + "павильон", + "падать", + "паек", + "пакет", + "палец", + "память", + "панель", + "папка", + "партия", + "паспорт", + "патрон", + "пауза", + "пафос", + "пахнуть", + "пациент", + "пачка", + "пашня", + "певец", + "педагог", + "пейзаж", + "пельмень", + "пенсия", + "пепел", + "период", + "песня", + "петля", + "пехота", + "печать", + "пешеход", + "пещера", + "пианист", + "пиво", + "пиджак", + "пиковый", + "пилот", + "пионер", + "пирог", + "писать", + "пить", + "пицца", + "пишущий", + "пища", + "план", + "плечо", + "плита", + "плохой", + "плыть", + "плюс", + "пляж", + "победа", + "повод", + "погода", + "подумать", + "поехать", + "пожимать", + "позиция", + "поиск", + "покой", + "получать", + "помнить", + "пони", + "поощрять", + "попадать", + "порядок", + "пост", + "поток", + "похожий", + "поцелуй", + "почва", + "пощечина", + "поэт", + "пояснить", + "право", + "предмет", + "проблема", + "пруд", + "прыгать", + "прямой", + "психолог", + "птица", + "публика", + "пугать", + "пудра", + "пузырь", + "пуля", + "пункт", + "пурга", + "пустой", + "путь", + "пухлый", + "пучок", + "пушистый", + "пчела", + "пшеница", + "пыль", + "пытка", + "пыхтеть", + "пышный", + "пьеса", + "пьяный", + "пятно", + "работа", + "равный", + "радость", + "развитие", + "район", + "ракета", + "рамка", + "ранний", + "рапорт", + "рассказ", + "раунд", + "рация", + "рвать", + "реальный", + "ребенок", + "реветь", + "регион", + "редакция", + "реестр", + "режим", + "резкий", + "рейтинг", + "река", + "религия", + "ремонт", + "рента", + "реплика", + "ресурс", + "реформа", + "рецепт", + "речь", + "решение", + "ржавый", + "рисунок", + "ритм", + "рифма", + "робкий", + "ровный", + "рогатый", + "родитель", + "рождение", + "розовый", + "роковой", + "роль", + "роман", + "ронять", + "рост", + "рота", + "роща", + "рояль", + "рубль", + "ругать", + "руда", + "ружье", + "руины", + "рука", + "руль", + "румяный", + "русский", + "ручка", + "рыба", + "рывок", + "рыдать", + "рыжий", + "рынок", + "рысь", + "рыть", + "рыхлый", + "рыцарь", + "рычаг", + "рюкзак", + "рюмка", + "рябой", + "рядовой", + "сабля", + "садовый", + "сажать", + "салон", + "самолет", + "сани", + "сапог", + "сарай", + "сатира", + "сауна", + "сахар", + "сбегать", + "сбивать", + "сбор", + "сбыт", + "свадьба", + "свет", + "свидание", + "свобода", + "связь", + "сгорать", + "сдвигать", + "сеанс", + "северный", + "сегмент", + "седой", + "сезон", + "сейф", + "секунда", + "сельский", + "семья", + "сентябрь", + "сердце", + "сеть", + "сечение", + "сеять", + "сигнал", + "сидеть", + "сизый", + "сила", + "символ", + "синий", + "сирота", + "система", + "ситуация", + "сиять", + "сказать", + "скважина", + "скелет", + "скидка", + "склад", + "скорый", + "скрывать", + "скучный", + "слава", + "слеза", + "слияние", + "слово", + "случай", + "слышать", + "слюна", + "смех", + "смирение", + "смотреть", + "смутный", + "смысл", + "смятение", + "снаряд", + "снег", + "снижение", + "сносить", + "снять", + "событие", + "совет", + "согласие", + "сожалеть", + "сойти", + "сокол", + "солнце", + "сомнение", + "сонный", + "сообщать", + "соперник", + "сорт", + "состав", + "сотня", + "соус", + "социолог", + "сочинять", + "союз", + "спать", + "спешить", + "спина", + "сплошной", + "способ", + "спутник", + "средство", + "срок", + "срывать", + "стать", + "ствол", + "стена", + "стихи", + "сторона", + "страна", + "студент", + "стыд", + "субъект", + "сувенир", + "сугроб", + "судьба", + "суета", + "суждение", + "сукно", + "сулить", + "сумма", + "сунуть", + "супруг", + "суровый", + "сустав", + "суть", + "сухой", + "суша", + "существо", + "сфера", + "схема", + "сцена", + "счастье", + "счет", + "считать", + "сшивать", + "съезд", + "сынок", + "сыпать", + "сырье", + "сытый", + "сыщик", + "сюжет", + "сюрприз", + "таблица", + "таежный", + "таинство", + "тайна", + "такси", + "талант", + "таможня", + "танец", + "тарелка", + "таскать", + "тахта", + "тачка", + "таять", + "тварь", + "твердый", + "творить", + "театр", + "тезис", + "текст", + "тело", + "тема", + "тень", + "теория", + "теплый", + "терять", + "тесный", + "тетя", + "техника", + "течение", + "тигр", + "типичный", + "тираж", + "титул", + "тихий", + "тишина", + "ткань", + "товарищ", + "толпа", + "тонкий", + "топливо", + "торговля", + "тоска", + "точка", + "тощий", + "традиция", + "тревога", + "трибуна", + "трогать", + "труд", + "трюк", + "тряпка", + "туалет", + "тугой", + "туловище", + "туман", + "тундра", + "тупой", + "турнир", + "тусклый", + "туфля", + "туча", + "туша", + "тыкать", + "тысяча", + "тьма", + "тюльпан", + "тюрьма", + "тяга", + "тяжелый", + "тянуть", + "убеждать", + "убирать", + "убогий", + "убыток", + "уважение", + "уверять", + "увлекать", + "угнать", + "угол", + "угроза", + "удар", + "удивлять", + "удобный", + "уезд", + "ужас", + "ужин", + "узел", + "узкий", + "узнавать", + "узор", + "уйма", + "уклон", + "укол", + "уксус", + "улетать", + "улица", + "улучшать", + "улыбка", + "уметь", + "умиление", + "умный", + "умолять", + "умысел", + "унижать", + "уносить", + "уныние", + "упасть", + "уплата", + "упор", + "упрекать", + "упускать", + "уран", + "урна", + "уровень", + "усадьба", + "усердие", + "усилие", + "ускорять", + "условие", + "усмешка", + "уснуть", + "успеть", + "усыпать", + "утешать", + "утка", + "уточнять", + "утро", + "утюг", + "уходить", + "уцелеть", + "участие", + "ученый", + "учитель", + "ушко", + "ущерб", + "уютный", + "уяснять", + "фабрика", + "фаворит", + "фаза", + "файл", + "факт", + "фамилия", + "фантазия", + "фара", + "фасад", + "февраль", + "фельдшер", + "феномен", + "ферма", + "фигура", + "физика", + "фильм", + "финал", + "фирма", + "фишка", + "флаг", + "флейта", + "флот", + "фокус", + "фольклор", + "фонд", + "форма", + "фото", + "фраза", + "фреска", + "фронт", + "фрукт", + "функция", + "фуражка", + "футбол", + "фыркать", + "халат", + "хамство", + "хаос", + "характер", + "хата", + "хватать", + "хвост", + "хижина", + "хилый", + "химия", + "хирург", + "хитрый", + "хищник", + "хлам", + "хлеб", + "хлопать", + "хмурый", + "ходить", + "хозяин", + "хоккей", + "холодный", + "хороший", + "хотеть", + "хохотать", + "храм", + "хрен", + "хриплый", + "хроника", + "хрупкий", + "художник", + "хулиган", + "хутор", + "царь", + "цвет", + "цель", + "цемент", + "центр", + "цепь", + "церковь", + "цикл", + "цилиндр", + "циничный", + "цирк", + "цистерна", + "цитата", + "цифра", + "цыпленок", + "чадо", + "чайник", + "часть", + "чашка", + "человек", + "чемодан", + "чепуха", + "черный", + "честь", + "четкий", + "чехол", + "чиновник", + "число", + "читать", + "членство", + "чреватый", + "чтение", + "чувство", + "чугунный", + "чудо", + "чужой", + "чукча", + "чулок", + "чума", + "чуткий", + "чучело", + "чушь", + "шаблон", + "шагать", + "шайка", + "шакал", + "шалаш", + "шампунь", + "шанс", + "шапка", + "шарик", + "шасси", + "шатер", + "шахта", + "шашлык", + "швейный", + "швырять", + "шевелить", + "шедевр", + "шейка", + "шелковый", + "шептать", + "шерсть", + "шестерка", + "шикарный", + "шинель", + "шипеть", + "широкий", + "шить", + "шишка", + "шкаф", + "школа", + "шкура", + "шланг", + "шлем", + "шлюпка", + "шляпа", + "шнур", + "шоколад", + "шорох", + "шоссе", + "шофер", + "шпага", + "шпион", + "шприц", + "шрам", + "шрифт", + "штаб", + "штора", + "штраф", + "штука", + "штык", + "шуба", + "шуметь", + "шуршать", + "шутка", + "щадить", + "щедрый", + "щека", + "щель", + "щенок", + "щепка", + "щетка", + "щука", + "эволюция", + "эгоизм", + "экзамен", + "экипаж", + "экономия", + "экран", + "эксперт", + "элемент", + "элита", + "эмблема", + "эмигрант", + "эмоция", + "энергия", + "эпизод", + "эпоха", + "эскиз", + "эссе", + "эстрада", + "этап", + "этика", + "этюд", + "эфир", + "эффект", + "эшелон", + "юбилей", + "юбка", + "южный", + "юмор", + "юноша", + "юрист", + "яблоко", + "явление", + "ягода", + "ядерный", + "ядовитый", + "ядро", + "язва", + "язык", + "яйцо", + "якорь", + "январь", + "японец", + "яркий", + "ярмарка", + "ярость", + "ярус", + "ясный", + "яхта", + "ячейка", + "ящик" + ]; +} \ No newline at end of file diff --git a/cw_salvium/lib/mnemonics/spanish.dart b/cw_salvium/lib/mnemonics/spanish.dart new file mode 100644 index 0000000000..531eafd357 --- /dev/null +++ b/cw_salvium/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_salvium/lib/pending_salvium_transaction.dart b/cw_salvium/lib/pending_salvium_transaction.dart new file mode 100644 index 0000000000..e11d7bd87c --- /dev/null +++ b/cw_salvium/lib/pending_salvium_transaction.dart @@ -0,0 +1,56 @@ +import 'package:cw_salvium/api/structs/pending_transaction.dart'; +import 'package:cw_salvium/api/transaction_history.dart' + as salvium_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 XMR in your available balance, or previous transactions are not yet fully processed.'; +} + +class PendingSalviumTransaction with PendingTransaction { + PendingSalviumTransaction(this.pendingTransactionDescription, this.cryptoCurrency); + + final PendingTransactionDescription pendingTransactionDescription; + final CryptoCurrency cryptoCurrency; + + @override + String get id => pendingTransactionDescription.hash; + + @override + String get hex => ''; + + @override + String get amountFormatted => AmountConverter.amountIntToString( + cryptoCurrency, pendingTransactionDescription.amount); + + @override + String get feeFormatted => AmountConverter.amountIntToString( + cryptoCurrency, pendingTransactionDescription.fee); + + @override + Future commit() async { + try { + salvium_transaction_history.commitTransactionFromPointerAddress( + address: pendingTransactionDescription.pointerAddress); + } catch (e) { + final message = e.toString(); + + if (message.contains('Reason: double spend')) { + throw DoubleSpendException(); + } + + rethrow; + } + } + + @override + Future commitUR() { + throw UnimplementedError(); + } +} diff --git a/cw_salvium/lib/salvium_account_list.dart b/cw_salvium/lib/salvium_account_list.dart new file mode 100644 index 0000000000..5efdc155a9 --- /dev/null +++ b/cw_salvium/lib/salvium_account_list.dart @@ -0,0 +1,84 @@ +import 'package:mobx/mobx.dart'; +import 'package:cw_core/account.dart'; +import 'package:cw_core/account_list.dart'; +import 'package:cw_salvium/api/account_list.dart' as account_list; + +part 'salvium_account_list.g.dart'; + +class SalviumAccountList = SalviumAccountListBase with _$SalviumAccountList; + +abstract class SalviumAccountListBase extends AccountList with Store { + SalviumAccountListBase() + : accounts = ObservableList(), + _isRefreshing = false, + _isUpdating = false { + refresh(); + } + + @override + @observable + ObservableList accounts; + bool _isRefreshing; + bool _isUpdating; + + @override + 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; + } + } + + @override + List getAll() => account_list + .getAllAccount() + .map((accountRow) => Account( + id: accountRow.getId(), + label: accountRow.getLabel())) + .toList(); + + @override + Future addAccount({required String label}) async { + await account_list.addAccount(label: label); + update(); + } + + @override + Future setLabelAccount({required int accountIndex, required String label}) async { + await account_list.setLabelForAccount( + accountIndex: accountIndex, label: label); + update(); + } + + @override + void refresh() { + if (_isRefreshing) { + return; + } + + try { + _isRefreshing = true; + account_list.refreshAccounts(); + _isRefreshing = false; + } catch (e) { + _isRefreshing = false; + print(e); + rethrow; + } + } +} diff --git a/cw_salvium/lib/salvium_balance.dart b/cw_salvium/lib/salvium_balance.dart new file mode 100644 index 0000000000..c826686514 --- /dev/null +++ b/cw_salvium/lib/salvium_balance.dart @@ -0,0 +1,34 @@ +import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/monero_balance.dart'; +import 'package:cw_salvium/api/balance_list.dart'; +import 'package:cw_salvium/api/structs/salvium_balance_row.dart'; + +const inactiveBalances = [ + CryptoCurrency.xcad, + CryptoCurrency.xjpy, + CryptoCurrency.xnok, + CryptoCurrency.xnzd]; + +Map getSalviumBalance({required int accountIndex}) { + final fullBalances = getSalviumFullBalance(accountIndex: accountIndex); + final unlockedBalances = getSalviumUnlockedBalance(accountIndex: accountIndex); + final salviumBalances = {}; + final balancesLength = fullBalances.length; + + for (int i = 0; i < balancesLength; i++) { + final assetType = fullBalances[i].getAssetType(); + final fullBalance = fullBalances[i].getAmount(); + final unlockedBalance = unlockedBalances[i].getAmount(); + final moneroBalance = MoneroBalance( + fullBalance: fullBalance, unlockedBalance: unlockedBalance); + final currency = CryptoCurrency.fromString(assetType); + + if (inactiveBalances.indexOf(currency) >= 0) { + continue; + } + + salviumBalances[currency] = moneroBalance; + } + + return salviumBalances; +} diff --git a/cw_salvium/lib/salvium_subaddress_list.dart b/cw_salvium/lib/salvium_subaddress_list.dart new file mode 100644 index 0000000000..5cfac4debb --- /dev/null +++ b/cw_salvium/lib/salvium_subaddress_list.dart @@ -0,0 +1,86 @@ +import 'package:cw_salvium/api/structs/subaddress_row.dart'; +import 'package:flutter/services.dart'; +import 'package:mobx/mobx.dart'; +import 'package:cw_salvium/api/subaddress_list.dart' as subaddress_list; +import 'package:cw_core/subaddress.dart'; + +part 'salvium_subaddress_list.g.dart'; + +class SalviumSubaddressList = SalviumSubaddressListBase + with _$SalviumSubaddressList; + +abstract class SalviumSubaddressListBase with Store { + SalviumSubaddressListBase() + : _isRefreshing = false, + _isUpdating = false, + subaddresses = ObservableList(); + + @observable + ObservableList subaddresses; + + bool _isRefreshing; + bool _isUpdating; + + void update({required int 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((subaddressRow) => Subaddress( + id: subaddressRow.getId(), + address: subaddressRow.getAddress(), + label: subaddressRow.getLabel())) + .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; + } + } +} diff --git a/cw_salvium/lib/salvium_transaction_creation_credentials.dart b/cw_salvium/lib/salvium_transaction_creation_credentials.dart new file mode 100644 index 0000000000..25df7d04e1 --- /dev/null +++ b/cw_salvium/lib/salvium_transaction_creation_credentials.dart @@ -0,0 +1,13 @@ +import 'package:cw_core/monero_transaction_priority.dart'; +import 'package:cw_core/output_info.dart'; + +class SalviumTransactionCreationCredentials { + SalviumTransactionCreationCredentials({ + required this.outputs, + required this.priority, + required this.assetType}); + + final List outputs; + final MoneroTransactionPriority priority; + final String assetType; +} diff --git a/cw_salvium/lib/salvium_transaction_creation_exception.dart b/cw_salvium/lib/salvium_transaction_creation_exception.dart new file mode 100644 index 0000000000..d0b95aec45 --- /dev/null +++ b/cw_salvium/lib/salvium_transaction_creation_exception.dart @@ -0,0 +1,8 @@ +class SalviumTransactionCreationException implements Exception { + SalviumTransactionCreationException(this.message); + + final String message; + + @override + String toString() => message; +} \ No newline at end of file diff --git a/cw_salvium/lib/salvium_transaction_history.dart b/cw_salvium/lib/salvium_transaction_history.dart new file mode 100644 index 0000000000..ed22a686ee --- /dev/null +++ b/cw_salvium/lib/salvium_transaction_history.dart @@ -0,0 +1,27 @@ +import 'dart:core'; +import 'package:mobx/mobx.dart'; +import 'package:cw_core/transaction_history.dart'; +import 'package:cw_salvium/salvium_transaction_info.dart'; + +part 'salvium_transaction_history.g.dart'; + +class SalviumTransactionHistory = SalviumTransactionHistoryBase + with _$SalviumTransactionHistory; + +abstract class SalviumTransactionHistoryBase + extends TransactionHistoryBase with Store { + SalviumTransactionHistoryBase() { + transactions = ObservableMap(); + } + + @override + Future save() async {} + + @override + void addOne(SalviumTransactionInfo transaction) => + transactions[transaction.id] = transaction; + + @override + void addMany(Map transactions) => + this.transactions.addAll(transactions); +} diff --git a/cw_salvium/lib/salvium_transaction_info.dart b/cw_salvium/lib/salvium_transaction_info.dart new file mode 100644 index 0000000000..ca027e6721 --- /dev/null +++ b/cw_salvium/lib/salvium_transaction_info.dart @@ -0,0 +1,73 @@ +import 'package:cw_core/transaction_info.dart'; +import 'package:cw_core/monero_amount_format.dart'; +import 'package:cw_salvium/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_salvium/api/transaction_history.dart'; + +class SalviumTransactionInfo extends TransactionInfo { + SalviumTransactionInfo(this.id, this.height, this.direction, this.date, + this.isPending, this.amount, this.accountIndex, this.addressIndex, this.fee, + this.confirmations); + + SalviumTransactionInfo.fromMap(Map map) + : id = (map['hash'] ?? '') as String, + height = (map['height'] ?? 0) as int, + direction = + parseTransactionDirectionFromNumber(map['direction'] as String) ?? + TransactionDirection.incoming, + date = DateTime.fromMillisecondsSinceEpoch( + int.parse(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; + + SalviumTransactionInfo.fromRow(TransactionInfoRow row) + : id = row.getHash(), + height = row.blockHeight, + direction = parseTransactionDirectionFromInt(row.direction) ?? + TransactionDirection.incoming, + date = DateTime.fromMillisecondsSinceEpoch(row.getDatetime() * 1000), + isPending = row.isPending != 0, + amount = row.getAmount(), + accountIndex = row.subaddrAccount, + addressIndex = row.subaddrIndex, + confirmations = row.confirmations, + key = null, //getTxKey(row.getHash()), + fee = row.fee, + assetType = row.getAssetType(); + + 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; + late String recipientAddress; + late String assetType; + String? _fiatAmount; + String? key; + + @override + String amountFormatted() => + '${formatAmount(moneroAmountToString(amount: amount))} $assetType'; + + @override + String fiatAmount() => _fiatAmount ?? ''; + + @override + void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount); + + @override + String feeFormatted() => + '${formatAmount(moneroAmountToString(amount: fee))} $assetType'; +} diff --git a/cw_salvium/lib/salvium_wallet.dart b/cw_salvium/lib/salvium_wallet.dart new file mode 100644 index 0000000000..b8279e6333 --- /dev/null +++ b/cw_salvium/lib/salvium_wallet.dart @@ -0,0 +1,429 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/pathForWallet.dart'; +import 'package:cw_core/transaction_priority.dart'; +import 'package:cw_salvium/salvium_transaction_creation_credentials.dart'; +import 'package:cw_core/monero_amount_format.dart'; +import 'package:cw_salvium/salvium_transaction_creation_exception.dart'; +import 'package:cw_salvium/salvium_transaction_info.dart'; +import 'package:cw_salvium/salvium_wallet_addresses.dart'; +import 'package:cw_core/monero_wallet_utils.dart'; +import 'package:cw_salvium/api/structs/pending_transaction.dart'; +import 'package:mobx/mobx.dart'; +import 'package:cw_salvium/api/transaction_history.dart' as salvium_transaction_history; +import 'package:cw_salvium/api/wallet.dart' as salvium_wallet; +import 'package:cw_salvium/api/monero_output.dart'; +import 'package:cw_salvium/pending_salvium_transaction.dart'; +import 'package:cw_core/monero_wallet_keys.dart'; +import 'package:cw_core/monero_balance.dart'; +import 'package:cw_salvium/salvium_transaction_history.dart'; +import 'package:cw_core/account.dart'; +import 'package:cw_core/pending_transaction.dart'; +import 'package:cw_core/wallet_base.dart'; +import 'package:cw_core/sync_status.dart'; +import 'package:cw_core/wallet_info.dart'; +import 'package:cw_core/node.dart'; +import 'package:cw_core/monero_transaction_priority.dart'; +import 'package:cw_salvium/salvium_balance.dart'; + +part 'salvium_wallet.g.dart'; + +const moneroBlockSize = 1000; + +class SalviumWallet = SalviumWalletBase with _$SalviumWallet; + +abstract class SalviumWalletBase + extends WalletBase with Store { + SalviumWalletBase({required WalletInfo walletInfo, String? password}) + : balance = ObservableMap.of(getSalviumBalance(accountIndex: 0)), + _isTransactionUpdating = false, + _password = password ?? '', + _hasSyncAfterStartup = false, + walletAddresses = SalviumWalletAddresses(walletInfo), + syncStatus = NotConnectedSyncStatus(), + super(walletInfo) { + transactionHistory = SalviumTransactionHistory(); + _onAccountChangeReaction = reaction((_) => walletAddresses.account, (Account? account) { + if (account == null) { + return; + } + balance.addAll(getSalviumBalance(accountIndex: account.id)); + walletAddresses.updateSubaddressList(accountIndex: account.id); + }); + } + + static const int _autoSaveInterval = 30; + final String _password; + + @override + SalviumWalletAddresses walletAddresses; + + @override + @observable + SyncStatus syncStatus; + + @override + @observable + ObservableMap balance; + + @override + String get seed => salvium_wallet.getSeed(); + + @override + MoneroWalletKeys get keys => MoneroWalletKeys( + primaryAddress: salvium_wallet.getAddress(accountIndex: 0, addressIndex: 0), + privateSpendKey: salvium_wallet.getSecretSpendKey(), + privateViewKey: salvium_wallet.getSecretViewKey(), + publicSpendKey: salvium_wallet.getPublicSpendKey(), + publicViewKey: salvium_wallet.getPublicViewKey()); + + salvium_wallet.SyncListener? _listener; + ReactionDisposer? _onAccountChangeReaction; + bool _isTransactionUpdating; + bool _hasSyncAfterStartup; + Timer? _autoSaveTimer; + + Future init() async { + await walletAddresses.init(); + balance.addAll(getSalviumBalance(accountIndex: walletAddresses.account?.id ?? 0)); + _setListeners(); + await updateTransactions(); + + if (walletInfo.isRecovery) { + salvium_wallet.setRecoveringFromSeed(isRecovery: walletInfo.isRecovery); + + if (salvium_wallet.getCurrentHeight() <= 1) { + salvium_wallet.setRefreshFromBlockHeight(height: walletInfo.restoreHeight); + } + } + + _autoSaveTimer = + Timer.periodic(Duration(seconds: _autoSaveInterval), (_) async => await save()); + } + + @override + Future? updateBalance() => null; + + @override + Future close({required bool shouldCleanup}) async { + _listener?.stop(); + _onAccountChangeReaction?.reaction.dispose(); + _autoSaveTimer?.cancel(); + } + + @override + Future connectToNode({required Node node}) async { + try { + syncStatus = ConnectingSyncStatus(); + await salvium_wallet.setupNode( + address: node.uriRaw, + login: node.login, + password: node.password, + useSSL: node.useSSL ?? false, + isLightWallet: false, + // FIXME: hardcoded value + socksProxyAddress: node.socksProxyAddress); + + salvium_wallet.setTrustedDaemon(node.trusted); + syncStatus = ConnectedSyncStatus(); + } catch (e) { + syncStatus = FailedSyncStatus(); + print(e); + } + } + + @override + Future startSync() async { + try { + _setInitialHeight(); + } catch (_) {} + + try { + syncStatus = AttemptingSyncStatus(); + salvium_wallet.startRefresh(); + _setListeners(); + _listener?.start(); + } catch (e) { + syncStatus = FailedSyncStatus(); + print(e); + rethrow; + } + } + + @override + Future createTransaction(Object credentials) async { + final _credentials = credentials as SalviumTransactionCreationCredentials; + final outputs = _credentials.outputs; + final hasMultiDestination = outputs.length > 1; + final assetType = CryptoCurrency.fromString(_credentials.assetType.toLowerCase()); + final balances = getSalviumBalance(accountIndex: walletAddresses.account!.id); + final unlockedBalance = balances[assetType]!.unlockedBalance; + + PendingTransactionDescription pendingTransactionDescription; + + if (!(syncStatus is SyncedSyncStatus)) { + throw SalviumTransactionCreationException('The wallet is not synced.'); + } + + if (hasMultiDestination) { + if (outputs.any((item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) { + throw SalviumTransactionCreationException( + 'You do not have enough coins to send this amount.'); + } + + final int totalAmount = + outputs.fold(0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0)); + + if (unlockedBalance < totalAmount) { + throw SalviumTransactionCreationException( + 'You do not have enough coins to send this amount.'); + } + + final moneroOutputs = outputs + .map((output) => MoneroOutput( + address: output.address, amount: output.cryptoAmount!.replaceAll(',', '.'))) + .toList(); + + pendingTransactionDescription = await transaction_history.createTransactionMultDest( + outputs: moneroOutputs, + priorityRaw: _credentials.priority.serialize(), + accountIndex: walletAddresses.account!.id); + } else { + final output = outputs.first; + final address = output.isParsedAddress && (output.extractedAddress?.isNotEmpty ?? false) + ? output.extractedAddress! + : output.address; + final amount = output.sendAll ? null : output.cryptoAmount!.replaceAll(',', '.'); + final int? formattedAmount = output.sendAll ? null : output.formattedCryptoAmount; + + if ((formattedAmount != null && unlockedBalance < formattedAmount) || + (formattedAmount == null && unlockedBalance <= 0)) { + final formattedBalance = moneroAmountToString(amount: unlockedBalance); + + throw SalviumTransactionCreationException( + 'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.'); + } + + pendingTransactionDescription = await transaction_history.createTransaction( + address: address, + assetType: _credentials.assetType, + amount: amount, + priorityRaw: _credentials.priority.serialize(), + accountIndex: walletAddresses.account!.id); + } + + return PendingSalviumTransaction(pendingTransactionDescription, assetType); + } + + @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.updateAddressesInBox(); + await backupWalletFiles(name); + await salvium_wallet.store(); + } + + @override + Future renameWalletFiles(String newWalletName) async { + 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(currentWalletPath).delete(recursive: true); + } + + @override + Future changePassword(String password) async { + salvium_wallet.setPasswordSync(password); + } + + Future getNodeHeight() async => salvium_wallet.getNodeHeight(); + + Future isConnected() async => salvium_wallet.isConnected(); + + Future setAsRecovered() async { + walletInfo.isRecovery = false; + await walletInfo.save(); + } + + @override + Future rescan({required int height}) async { + walletInfo.restoreHeight = height; + walletInfo.isRecovery = true; + salvium_wallet.setRefreshFromBlockHeight(height: height); + salvium_wallet.rescanBlockchainAsync(); + await startSync(); + _askForUpdateBalance(); + walletAddresses.accountList.update(); + await _askForUpdateTransactionHistory(); + await save(); + await walletInfo.save(); + } + + String getTransactionAddress(int accountIndex, int addressIndex) => + salvium_wallet.getAddress(accountIndex: accountIndex, addressIndex: addressIndex); + + @override + Future> fetchTransactions() async { + salvium_transaction_history.refreshTransactions(); + return _getAllTransactions(null) + .fold>({}, + (Map acc, SalviumTransactionInfo tx) { + acc[tx.id] = tx; + return acc; + }); + } + + Future updateTransactions() async { + try { + if (_isTransactionUpdating) { + return; + } + + _isTransactionUpdating = true; + final transactions = await fetchTransactions(); + transactionHistory.addMany(transactions); + await transactionHistory.save(); + _isTransactionUpdating = false; + } catch (e) { + print(e); + _isTransactionUpdating = false; + } + } + + List _getAllTransactions(dynamic _) => salvium_transaction_history + .getAllTransations() + .map((row) => SalviumTransactionInfo.fromRow(row)) + .toList(); + + void _setListeners() { + _listener?.stop(); + _listener = salvium_wallet.setListeners(_onNewBlock, _onNewTransaction); + } + + void _setInitialHeight() { + if (walletInfo.isRecovery) { + return; + } + + final currentHeight = salvium_wallet.getCurrentHeight(); + + if (currentHeight <= 1) { + final height = _getHeightByDate(walletInfo.date); + salvium_wallet.setRecoveringFromSeed(isRecovery: true); + salvium_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 = salvium_wallet.getNodeHeightSync(); + final heightDistance = _getHeightDistance(date); + + if (nodeHeight <= 0) { + return 0; + } + + return nodeHeight - heightDistance; + } + + void _askForUpdateBalance() => + balance.addAll(getSalviumBalance(accountIndex: walletAddresses.account!.id)); + + Future _askForUpdateTransactionHistory() async => await updateTransactions(); + + void _onNewBlock(int height, int blocksLeft, double ptc) async { + try { + if (walletInfo.isRecovery) { + await _askForUpdateTransactionHistory(); + _askForUpdateBalance(); + walletAddresses.accountList.update(); + } + + if (blocksLeft < 1000) { + 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()); + } + } + + @override + String get password => _password; + + @override + Future signMessage(String message, {String? address = null}) => + throw UnimplementedError(); + + @override + Future verifyMessage(String message, String signature, {String? address = null}) => + throw UnimplementedError(); +} diff --git a/cw_salvium/lib/salvium_wallet_addresses.dart b/cw_salvium/lib/salvium_wallet_addresses.dart new file mode 100644 index 0000000000..3ebaa06fa4 --- /dev/null +++ b/cw_salvium/lib/salvium_wallet_addresses.dart @@ -0,0 +1,95 @@ +import 'package:cw_core/wallet_addresses_with_account.dart'; +import 'package:cw_core/wallet_info.dart'; +import 'package:cw_core/account.dart'; +import 'package:cw_salvium/api/wallet.dart'; +import 'package:cw_salvium/salvium_account_list.dart'; +import 'package:cw_salvium/salvium_subaddress_list.dart'; +import 'package:cw_core/subaddress.dart'; +import 'package:mobx/mobx.dart'; + +part 'salvium_wallet_addresses.g.dart'; + +class SalviumWalletAddresses = SalviumWalletAddressesBase + with _$SalviumWalletAddresses; + +abstract class SalviumWalletAddressesBase extends WalletAddressesWithAccount with Store { + SalviumWalletAddressesBase(WalletInfo walletInfo) + : accountList = SalviumAccountList(), + subaddressList = SalviumSubaddressList(), + address = '', + super(walletInfo); + + @override + @observable + String address; + + String get primaryAddress => address; + + // @override + @observable + Account? account; + + @observable + Subaddress? subaddress; + + SalviumSubaddressList subaddressList; + + SalviumAccountList accountList; + + @override + Future init() async { + accountList.update(); + account = accountList.accounts.isEmpty ? Account(id: 0, label: "Primary address") : accountList.accounts.first; + updateSubaddressList(accountIndex: account?.id ?? 0); + await updateAddressesInBox(); + } + + @override + Future updateAddressesInBox() async { + try { + final _subaddressList = SalviumSubaddressList(); + + addressesMap.clear(); + + accountList.accounts.forEach((account) { + _subaddressList.update(accountIndex: account.id); + _subaddressList.subaddresses.forEach((subaddress) { + addressesMap[subaddress.address] = subaddress.label; + }); + }); + + 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); + address = subaddressList.subaddresses.isNotEmpty + ? subaddressList.subaddresses.first.address + : getAddress(); + } + + @override + bool containsAddress(String address) => + addressInfos[account?.id ?? 0]?.any((it) => it.address == address) ?? false; +} diff --git a/cw_salvium/lib/salvium_wallet_service.dart b/cw_salvium/lib/salvium_wallet_service.dart new file mode 100644 index 0000000000..797c0ff278 --- /dev/null +++ b/cw_salvium/lib/salvium_wallet_service.dart @@ -0,0 +1,258 @@ +import 'dart:io'; +import 'package:collection/collection.dart'; +import 'package:cw_core/wallet_base.dart'; +import 'package:cw_core/monero_wallet_utils.dart'; +import 'package:hive/hive.dart'; +import 'package:cw_salvium/api/wallet_manager.dart' as salvium_wallet_manager; +import 'package:cw_salvium/api/wallet.dart' as salvium_wallet; +import 'package:cw_salvium/api/exceptions/wallet_opening_exception.dart'; +import 'package:cw_salvium/salvium_wallet.dart'; +import 'package:cw_core/wallet_credentials.dart'; +import 'package:cw_core/wallet_service.dart'; +import 'package:cw_core/pathForWallet.dart'; +import 'package:cw_core/wallet_info.dart'; +import 'package:cw_core/wallet_type.dart'; + +class SalviumNewWalletCredentials extends WalletCredentials { + SalviumNewWalletCredentials({required String name, required this.language, String? password}) + : super(name: name, password: password); + + final String language; +} + +class SalviumRestoreWalletFromSeedCredentials extends WalletCredentials { + SalviumRestoreWalletFromSeedCredentials( + {required String name, + required String password, + required int height, + required this.mnemonic}) + : super(name: name, password: password, height: height); + + final String mnemonic; +} + +class SalviumWalletLoadingException implements Exception { + @override + String toString() => 'Failure to load the wallet.'; +} + +class SalviumRestoreWalletFromKeysCredentials extends WalletCredentials { + SalviumRestoreWalletFromKeysCredentials( + {required String name, + required String password, + required this.language, + required this.address, + required this.viewKey, + required this.spendKey, + required int height}) + : super(name: name, password: password, height: height); + + final String language; + final String address; + final String viewKey; + final String spendKey; +} + +class SalviumWalletService extends WalletService< + SalviumNewWalletCredentials, + SalviumRestoreWalletFromSeedCredentials, + SalviumRestoreWalletFromKeysCredentials, + SalviumNewWalletCredentials> { + SalviumWalletService(this.walletInfoSource); + + final Box walletInfoSource; + + static bool walletFilesExist(String path) => + !File(path).existsSync() && !File('$path.keys').existsSync(); + + @override + WalletType getType() => WalletType.salvium; + + @override + Future create(SalviumNewWalletCredentials credentials, {bool? isTestnet}) async { + try { + final path = await pathForWallet(name: credentials.name, type: getType()); + await salvium_wallet_manager.createWallet( + path: path, + password: credentials.password!, + language: credentials.language); + final wallet = SalviumWallet(walletInfo: credentials.walletInfo!); + await wallet.init(); + return wallet; + } catch (e) { + // TODO: Implement Exception for wallet list service. + print('SalviumWalletsManager Error: ${e.toString()}'); + rethrow; + } + } + + @override + Future isWalletExit(String name) async { + try { + final path = await pathForWallet(name: name, type: getType()); + return salvium_wallet_manager.isWalletExist(path: path); + } catch (e) { + // TODO: Implement Exception for wallet list service. + print('SalviumWalletsManager Error: $e'); + rethrow; + } + } + + @override + Future openWallet(String name, String password) async { + try { + final path = await pathForWallet(name: name, type: getType()); + + if (walletFilesExist(path)) { + await repairOldAndroidWallet(name); + } + + await salvium_wallet_manager + .openWalletAsync({'path': path, 'password': password}); + final walletInfo = walletInfoSource.values.firstWhereOrNull( + (info) => info.id == WalletBase.idFor(name, getType()))!; + final wallet = SalviumWallet(walletInfo: walletInfo); + final isValid = wallet.walletAddresses.validate(); + + if (!isValid) { + await restoreOrResetWalletFiles(name); + wallet.close(shouldCleanup: false); + return openWallet(name, password); + } + + await wallet.init(); + + return wallet; + } catch (e) { + // TODO: Implement Exception for wallet list service. + + if ((e.toString().contains('bad_alloc') || + (e is WalletOpeningException && + (e.message == 'std::bad_alloc' || + e.message.contains('bad_alloc')))) || + (e.toString().contains('does not correspond') || + (e is WalletOpeningException && + e.message.contains('does not correspond')))) { + await restoreOrResetWalletFiles(name); + return openWallet(name, password); + } + + rethrow; + } + } + + @override + Future remove(String wallet) async { + final path = await pathForWalletDir(name: wallet, type: getType()); + 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 = SalviumWallet(walletInfo: currentWalletInfo); + + await currentWallet.renameWalletFiles(newName); + await saveBackup(newName); + + final newWalletInfo = currentWalletInfo; + newWalletInfo.id = WalletBase.idFor(newName, getType()); + newWalletInfo.name = newName; + + await walletInfoSource.put(currentWalletInfo.key, newWalletInfo); + } + + @override + Future restoreFromHardwareWallet(SalviumNewWalletCredentials credentials) { + throw UnimplementedError("Restoring a Salvium wallet from a hardware wallet is not yet supported!"); + } + + @override + Future restoreFromKeys( + SalviumRestoreWalletFromKeysCredentials credentials, {bool? isTestnet}) async { + try { + final path = await pathForWallet(name: credentials.name, type: getType()); + await salvium_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 = SalviumWallet(walletInfo: credentials.walletInfo!); + await wallet.init(); + + return wallet; + } catch (e) { + // TODO: Implement Exception for wallet list service. + print('SalviumWalletsManager Error: $e'); + rethrow; + } + } + + @override + Future restoreFromSeed( + SalviumRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async { + try { + final path = await pathForWallet(name: credentials.name, type: getType()); + await salvium_wallet_manager.restoreFromSeed( + path: path, + password: credentials.password!, + seed: credentials.mnemonic, + restoreHeight: credentials.height!); + final wallet = SalviumWallet(walletInfo: credentials.walletInfo!); + await wallet.init(); + + return wallet; + } catch (e) { + // TODO: Implement Exception for wallet list service. + print('SalviumWalletsManager Error: $e'); + rethrow; + } + } + + 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_salvium/lib/update_salvium_rate.dart b/cw_salvium/lib/update_salvium_rate.dart new file mode 100644 index 0000000000..929edfc62f --- /dev/null +++ b/cw_salvium/lib/update_salvium_rate.dart @@ -0,0 +1,15 @@ +//import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; +import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/monero_amount_format.dart'; +import 'package:cw_salvium/api/balance_list.dart'; + +//Future updateSalviumRate(FiatConversionStore fiatConversionStore) async { +// final rate = getRate(); +// final base = rate.firstWhere((row) => row.getAssetType() == 'XUSD', orElse: () => null); +// rate.forEach((row) { +// final cur = CryptoCurrency.fromString(row.getAssetType()); +// final baseRate = moneroAmountToDouble(amount: base.getRate()); +// final rowRate = moneroAmountToDouble(amount: row.getRate()); +// fiatConversionStore.prices[cur] = baseRate * rowRate; +// }); +//} \ No newline at end of file diff --git a/cw_salvium/pubspec.lock b/cw_salvium/pubspec.lock new file mode 100644 index 0000000000..cb5d3e2c35 --- /dev/null +++ b/cw_salvium/pubspec.lock @@ -0,0 +1,773 @@ +# 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: + name: args + sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 + url: "https://pub.dev" + source: hosted + version: "2.6.0" + asn1lib: + dependency: transitive + description: + name: asn1lib + sha256: "6b151826fcc95ff246cd219a0bf4c753ea14f4081ad71c61939becf3aba27f70" + url: "https://pub.dev" + source: hosted + version: "1.5.5" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + url: "https://pub.dev" + source: hosted + version: "2.4.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: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + 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: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d" + url: "https://pub.dev" + source: hosted + version: "2.4.13" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41" + url: "https://pub.dev" + source: hosted + version: "7.2.10" + 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: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb + url: "https://pub.dev" + source: hosted + version: "8.9.2" + cake_backup: + dependency: transitive + description: + path: "." + ref: main + resolved-ref: "3aba867dcab6737f6707782f5db15d71f303db38" + url: "https://github.com/cake-tech/cake_backup.git" + source: git + version: "1.0.0+1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 + url: "https://pub.dev" + source: hosted + version: "4.10.0" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + cryptography: + dependency: transitive + description: + name: cryptography + sha256: d146b76d33d94548cf035233fbc2f4338c1242fa119013bead807d033fc4ae05 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + cupertino_icons: + dependency: transitive + description: + name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" + source: hosted + version: "1.0.8" + cw_core: + dependency: "direct main" + description: + 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: "2.2.4" + encrypt: + dependency: transitive + description: + name: encrypt + sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2" + url: "https://pub.dev" + source: hosted + version: "5.0.3" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + ffi: + dependency: "direct main" + description: + name: ffi + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_mobx: + dependency: "direct main" + description: + name: flutter_mobx + sha256: "859fbf452fa9c2519d2700b125dd7fb14c508bbdd7fb65e26ca8ff6c92280e2e" + url: "https://pub.dev" + source: hosted + version: "2.2.1+1" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + 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: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + url: "https://pub.dev" + source: hosted + version: "1.2.2" + 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: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + intl: + dependency: "direct main" + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" + source: hosted + version: "0.19.0" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + url: "https://pub.dev" + source: hosted + version: "10.0.5" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + url: "https://pub.dev" + source: hosted + version: "3.0.5" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + url: "https://pub.dev" + source: hosted + version: "1.15.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + mobx: + dependency: "direct main" + description: + name: mobx + sha256: "63920b27b32ad1910adfe767ab1750e4c212e8923232a1f891597b362074ea5e" + url: "https://pub.dev" + source: hosted + version: "2.3.3+2" + mobx_codegen: + dependency: "direct dev" + description: + name: mobx_codegen + sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c + url: "https://pub.dev" + source: hosted + version: "2.3.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.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: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 + url: "https://pub.dev" + source: hosted + version: "2.1.4" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a + url: "https://pub.dev" + source: hosted + version: "2.2.12" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + url: "https://pub.dev" + source: hosted + version: "2.4.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" + url: "https://pub.dev" + source: hosted + version: "3.9.1" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + provider: + dependency: transitive + description: + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" + source: hosted + version: "6.1.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + socks5_proxy: + dependency: transitive + description: + name: socks5_proxy + sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053" + url: "https://pub.dev" + source: hosted + version: "1.0.6" + 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: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + 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: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + unorm_dart: + dependency: transitive + description: + name: unorm_dart + sha256: "23d8bf65605401a6a32cff99435fed66ef3dab3ddcad3454059165df46496a3b" + url: "https://pub.dev" + source: hosted + version: "0.3.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + url: "https://pub.dev" + source: hosted + version: "14.2.5" + watcher: + dependency: "direct overridden" + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + url: "https://pub.dev" + source: hosted + version: "0.1.6" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" +sdks: + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0" diff --git a/cw_salvium/pubspec.yaml b/cw_salvium/pubspec.yaml new file mode 100644 index 0000000000..0ba08f1ee0 --- /dev/null +++ b/cw_salvium/pubspec.yaml @@ -0,0 +1,81 @@ +name: cw_salvium +description: A new flutter plugin project. +version: 0.0.1 +publish_to: none +author: Cake Wallet +homepage: https://cakewallet.com + +environment: + sdk: ">=2.17.5 <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.19.0 + cw_core: + path: ../cw_core + +dev_dependencies: + flutter_test: + sdk: flutter + build_runner: ^2.4.7 + mobx_codegen: ^2.0.7 + build_resolvers: ^2.0.9 + 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 'pluginClass' and Android 'package' 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.cw_salvium + pluginClass: CwSalviumPlugin + ios: + pluginClass: CwSalviumPlugin + + # 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/cw_shared_external/README.md b/cw_shared_external/README.md index 7de77f1ece..6ab2a2cf7a 100644 --- a/cw_shared_external/README.md +++ b/cw_shared_external/README.md @@ -1,7 +1,8 @@ # cw_shared_external -Part of Cake Wallet. Shared external libraries for cw_monero and cw_haven. +Part of Cake Wallet. Shared external libraries for cw_monero, cw_haven and cw_salvium. Libraries: + - Boost - OpenSSL -- Sodium \ No newline at end of file +- Sodium diff --git a/cw_shared_external/ios/cw_shared_external.podspec b/cw_shared_external/ios/cw_shared_external.podspec index b147dc7df9..c519aa19ed 100644 --- a/cw_shared_external/ios/cw_shared_external.podspec +++ b/cw_shared_external/ios/cw_shared_external.podspec @@ -5,8 +5,8 @@ Pod::Spec.new do |s| s.name = 'cw_shared_external' s.version = '0.0.1' - s.summary = 'Shared libraries for monero and haven.' - s.description = 'Shared libraries for monero and haven.' + s.summary = 'Shared libraries for monero, haven and salvium.' + s.description = 'Shared libraries for monero, haven and salvium.' s.homepage = 'http://cakewallet.com' s.license = { :file => '../LICENSE' } s.author = { 'Cake Wallet' => 'm@cakewallet.com' } diff --git a/cw_shared_external/pubspec.yaml b/cw_shared_external/pubspec.yaml index 71d5fcd5a1..b2112b8ebb 100644 --- a/cw_shared_external/pubspec.yaml +++ b/cw_shared_external/pubspec.yaml @@ -1,5 +1,5 @@ name: cw_shared_external -description: Shared external libraries for monero and haven +description: Shared external libraries for monero, haven and salvium version: 0.0.1 author: Cake Walelt homepage: https://cakewallet.com @@ -23,4 +23,4 @@ flutter: package: com.cakewallet.cw_shared_external pluginClass: CwSharedExternalPlugin ios: - pluginClass: CwSharedExternalPlugin \ No newline at end of file + pluginClass: CwSharedExternalPlugin diff --git a/lib/core/seed_validator.dart b/lib/core/seed_validator.dart index 2d2a0c6ee7..261485f356 100644 --- a/lib/core/seed_validator.dart +++ b/lib/core/seed_validator.dart @@ -3,6 +3,7 @@ import 'package:cake_wallet/core/validator.dart'; import 'package:cake_wallet/entities/mnemonic_item.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/salvium/salvium.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/polygon/polygon.dart'; @@ -31,6 +32,8 @@ class SeedValidator extends Validator { return monero!.getMoneroWordList(language); case WalletType.haven: return haven!.getMoneroWordList(language); + case WalletType.salvium: + return salvium!.getMoneroWordList(language); case WalletType.ethereum: return ethereum!.getEthereumWordList(language); case WalletType.bitcoinCash: diff --git a/lib/core/wallet_creation_service.dart b/lib/core/wallet_creation_service.dart index 3ee630b33b..cb18d3d079 100644 --- a/lib/core/wallet_creation_service.dart +++ b/lib/core/wallet_creation_service.dart @@ -87,6 +87,7 @@ class WalletCreationService { case WalletType.wownero: case WalletType.none: case WalletType.haven: + case WalletType.salvium: case WalletType.nano: case WalletType.banano: return false; diff --git a/lib/di.dart b/lib/di.dart index 6531c411f8..bd63142d6e 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -57,6 +57,7 @@ import 'package:cake_wallet/cake_pay/cake_pay_card.dart'; import 'package:cake_wallet/exchange/exchange_template.dart'; import 'package:cake_wallet/exchange/trade.dart'; import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/salvium/salvium.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/polygon/polygon.dart'; @@ -828,7 +829,8 @@ Future setup({ final wallet = getIt.get().wallet!; if (wallet.type == WalletType.monero || wallet.type == WalletType.wownero || - wallet.type == WalletType.haven) { + wallet.type == WalletType.haven || + wallet.type == WalletType.salvium) { return MoneroAccountListViewModel(wallet); } throw Exception( @@ -861,6 +863,7 @@ Future setup({ monero!.getAccountList(getIt.get().wallet!), wownero?.getAccountList(getIt.get().wallet!), haven?.getAccountList(getIt.get().wallet!), + salvium?.getAccountList(getIt.get().wallet!), wallet: getIt.get().wallet!, accountListItem: account)); @@ -1056,6 +1059,8 @@ Future setup({ switch (param1) { case WalletType.haven: return haven!.createHavenWalletService(_walletInfoSource); + case WalletType.salvium: + return salvium!.createSalviumWalletService(_walletInfoSource); case WalletType.monero: return monero!.createMoneroWalletService(_walletInfoSource, _unspentCoinsInfoSource); case WalletType.bitcoin: diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index 7f06796257..b9847e6aee 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -32,6 +32,7 @@ const publicBitcoinTestnetElectrumUri = '$publicBitcoinTestnetElectrumAddress:$publicBitcoinTestnetElectrumPort'; const cakeWalletLitecoinElectrumUri = 'ltc-electrum.cakewallet.com:50002'; const havenDefaultNodeUri = 'nodes.havenprotocol.org:443'; +const salviumDefaultNodeUri = 'nodes.salvium.org:443'; const ethereumDefaultNodeUri = 'ethereum.publicnode.com'; const polygonDefaultNodeUri = 'polygon-bor.publicnode.com'; const cakeWalletBitcoinCashDefaultNodeUri = 'bitcoincash.stackwallet.com:50002'; @@ -98,6 +99,7 @@ Future defaultSettingsMigration( await changeLitecoinCurrentElectrumServerToDefault( sharedPreferences: sharedPreferences, nodes: nodes); await changeHavenCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes); + await changeSalviumCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes); await changeBitcoinCashCurrentNodeToDefault( sharedPreferences: sharedPreferences, nodes: nodes); @@ -288,7 +290,15 @@ Future defaultSettingsMigration( ], ); break; + case 45: + await addSalviumNodeList(nodes: nodes); + await changeSalviumCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes); + await checkCurrentNodes(nodes, powNodes, sharedPreferences); + break; + case 46: + await changeDefaultSalviumNode(nodes); + break; default: break; } @@ -465,7 +475,7 @@ Future _validateWalletInfoBoxData(Box walletInfoSource) async continue; } - if (type == WalletType.monero || type == WalletType.haven) { + if (type == WalletType.monero || type == WalletType.haven || type == WalletType.salvium) { final hasKeysFile = walletFiles.any((element) => element.path.contains(".keys")); if (!hasKeysFile) { @@ -572,6 +582,11 @@ Node? getHavenDefaultNode({required Box nodes}) { nodes.values.firstWhereOrNull((node) => node.type == WalletType.haven); } +Node? getSalviumDefaultNode({required Box nodes}) { + return nodes.values.firstWhereOrNull((Node node) => node.uriRaw == salviumDefaultNodeUri) ?? + nodes.values.firstWhereOrNull((node) => node.type == WalletType.salvium); +} + Node? getEthereumDefaultNode({required Box nodes}) { return nodes.values.firstWhereOrNull((Node node) => node.uriRaw == ethereumDefaultNodeUri) ?? nodes.values.firstWhereOrNull((node) => node.type == WalletType.ethereum); @@ -791,6 +806,14 @@ Future changeHavenCurrentNodeToDefault( await sharedPreferences.setInt(PreferencesKey.currentHavenNodeIdKey, nodeId); } +Future changeSalviumCurrentNodeToDefault( + {required SharedPreferences sharedPreferences, required Box nodes}) async { + final node = getSalviumDefaultNode(nodes: nodes); + final nodeId = node?.key as int? ?? 0; + + await sharedPreferences.setInt(PreferencesKey.currentSalviumNodeIdKey, nodeId); +} + Future replaceDefaultNode( {required SharedPreferences sharedPreferences, required Box nodes}) async { const nodesForReplace = [ @@ -854,6 +877,15 @@ Future addHavenNodeList({required Box nodes}) async { } } +Future addSalviumNodeList({required Box nodes}) async { + final nodeList = await loadDefaultSalviumNodes(); + for (var node in nodeList) { + if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) { + await nodes.add(node); + } + } +} + Future addAddressesForMoneroWallets(Box walletInfoSource) async { final moneroWalletsInfo = walletInfoSource.values.where((info) => info.type == WalletType.monero); moneroWalletsInfo.forEach((info) async { @@ -1085,6 +1117,7 @@ Future checkCurrentNodes( final currentLitecoinElectrumSeverId = sharedPreferences.getInt(PreferencesKey.currentLitecoinElectrumSererIdKey); final currentHavenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey); + final currentSalviumNodeId = sharedPreferences.getInt(PreferencesKey.currentSalviumNodeIdKey); final currentEthereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey); final currentPolygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey); final currentNanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey); @@ -1102,6 +1135,8 @@ Future checkCurrentNodes( nodeSource.values.firstWhereOrNull((node) => node.key == currentLitecoinElectrumSeverId); final currentHavenNodeServer = nodeSource.values.firstWhereOrNull((node) => node.key == currentHavenNodeId); + final currentSalviumNodeServer = + nodeSource.values.firstWhereOrNull((node) => node.key == currentSalviumNodeId); final currentEthereumNodeServer = nodeSource.values.firstWhereOrNull((node) => node.key == currentEthereumNodeId); final currentPolygonNodeServer = @@ -1149,6 +1184,12 @@ Future checkCurrentNodes( await sharedPreferences.setInt(PreferencesKey.currentHavenNodeIdKey, node.key as int); } + if (currentSalviumNodeServer == null) { + final node = Node(uri: salviumDefaultNodeUri, type: WalletType.salvium); + await nodeSource.add(node); + await sharedPreferences.setInt(PreferencesKey.currentSalviumNodeIdKey, node.key as int); + } + if (currentEthereumNodeServer == null) { final node = Node(uri: ethereumDefaultNodeUri, type: WalletType.ethereum); await nodeSource.add(node); @@ -1238,6 +1279,15 @@ Future changeDefaultHavenNode(Box nodeSource) async { }); } +Future changeDefaultSalviumNode(Box nodeSource) async { + const previousSalviumDefaultNodeUri = 'nodes.salvium.org:443'; + final salviumNodes = nodeSource.values.where((node) => node.uriRaw == previousSalviumDefaultNodeUri); + salviumNodes.forEach((node) async { + node.uriRaw = salviumDefaultNodeUri; + await node.save(); + }); +} + Future migrateExchangeStatus(SharedPreferences sharedPreferences) async { final isExchangeDisabled = sharedPreferences.getBool(PreferencesKey.disableExchangeKey); if (isExchangeDisabled == null) { diff --git a/lib/entities/ens_record.dart b/lib/entities/ens_record.dart index b2ce51806c..fe0974be87 100644 --- a/lib/entities/ens_record.dart +++ b/lib/entities/ens_record.dart @@ -35,6 +35,8 @@ class EnsRecord { return await ens.withName(name).getCoinAddress(CoinType.LTC); case WalletType.haven: return await ens.withName(name).getCoinAddress(CoinType.XHV); + case WalletType.salvium: + return await ens.withName(name).getCoinAddress(CoinType.SAL); case WalletType.ethereum: case WalletType.polygon: default: diff --git a/lib/entities/node_list.dart b/lib/entities/node_list.dart index 85e37a7bc8..c0a3c17af3 100644 --- a/lib/entities/node_list.dart +++ b/lib/entities/node_list.dart @@ -68,6 +68,22 @@ Future> loadDefaultHavenNodes() async { return nodes; } +Future> loadDefaultSalviumNodes() async { + final nodesRaw = await rootBundle.loadString('assets/salvium_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.salvium; + nodes.add(node); + } + } + + return nodes; +} + Future> loadDefaultEthereumNodes() async { final nodesRaw = await rootBundle.loadString('assets/ethereum_server_list.yml'); final loadedNodes = loadYaml(nodesRaw) as YamlList; @@ -206,6 +222,7 @@ Future resetToDefault(Box nodeSource) async { final litecoinElectrumServerList = await loadLitecoinElectrumServerList(); final bitcoinCashElectrumServerList = await loadBitcoinCashElectrumServerList(); final havenNodes = await loadDefaultHavenNodes(); + final salviumNodes = await loadDefaultSalviumNodes(); final ethereumNodes = await loadDefaultEthereumNodes(); final nanoNodes = await loadDefaultNanoNodes(); final polygonNodes = await loadDefaultPolygonNodes(); @@ -216,6 +233,7 @@ Future resetToDefault(Box nodeSource) async { bitcoinElectrumServerList + litecoinElectrumServerList + havenNodes + + salviumNodes + ethereumNodes + bitcoinCashElectrumServerList + nanoNodes + diff --git a/lib/entities/parse_address_from_domain.dart b/lib/entities/parse_address_from_domain.dart index 42ab19b312..6249088340 100644 --- a/lib/entities/parse_address_from_domain.dart +++ b/lib/entities/parse_address_from_domain.dart @@ -214,7 +214,7 @@ class AddressResolver { } if (text.hasOnlyEmojis) { if (settingsStore.lookupsYatService) { - if (walletType != WalletType.haven) { + if (walletType != WalletType.haven && walletType != WalletType.salvium) { final addresses = await yatService.fetchYatAddress(text, ticker); return ParsedAddress.fetchEmojiAddress(addresses: addresses, name: text); } diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index 5ed7a7ed62..f65acd1859 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -5,6 +5,7 @@ class PreferencesKey { static const currentBitcoinElectrumSererIdKey = 'current_node_id_btc'; static const currentLitecoinElectrumSererIdKey = 'current_node_id_ltc'; static const currentHavenNodeIdKey = 'current_node_id_xhv'; + static const currentSalviumNodeIdKey = 'current_node_id_sal'; static const currentEthereumNodeIdKey = 'current_node_id_eth'; static const currentPolygonNodeIdKey = 'current_node_id_matic'; static const currentNanoNodeIdKey = 'current_node_id_nano'; @@ -41,6 +42,7 @@ class PreferencesKey { static const moneroTransactionPriority = 'current_fee_priority_monero'; static const bitcoinTransactionPriority = 'current_fee_priority_bitcoin'; static const havenTransactionPriority = 'current_fee_priority_haven'; + static const salviumTransactionPriority = 'current_fee_priority_salvium'; static const litecoinTransactionPriority = 'current_fee_priority_litecoin'; static const ethereumTransactionPriority = 'current_fee_priority_ethereum'; static const polygonTransactionPriority = 'current_fee_priority_polygon'; diff --git a/lib/entities/priority_for_wallet_type.dart b/lib/entities/priority_for_wallet_type.dart index 5342874940..511ac414c9 100644 --- a/lib/entities/priority_for_wallet_type.dart +++ b/lib/entities/priority_for_wallet_type.dart @@ -2,6 +2,7 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/salvium/salvium.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/wownero/wownero.dart'; @@ -20,6 +21,8 @@ List priorityForWalletType(WalletType type) { return bitcoin!.getLitecoinTransactionPriorities(); case WalletType.haven: return haven!.getTransactionPriorities(); + case WalletType.salvium: + return salvium!.getTransactionPriorities(); case WalletType.ethereum: return ethereum!.getTransactionPriorities(); case WalletType.bitcoinCash: diff --git a/lib/entities/provider_types.dart b/lib/entities/provider_types.dart index 42ec74c124..3d12e3c258 100644 --- a/lib/entities/provider_types.dart +++ b/lib/entities/provider_types.dart @@ -76,6 +76,7 @@ class ProvidersHelper { ]; case WalletType.none: case WalletType.haven: + case WalletType.salvium: return []; } } @@ -108,6 +109,7 @@ class ProvidersHelper { case WalletType.banano: case WalletType.none: case WalletType.haven: + case WalletType.salvium: case WalletType.wownero: return []; } diff --git a/lib/entities/update_salvium_rate.dart b/lib/entities/update_salvium_rate.dart new file mode 100644 index 0000000000..ed76015efa --- /dev/null +++ b/lib/entities/update_salvium_rate.dart @@ -0,0 +1,26 @@ +import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; +import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/monero_amount_format.dart'; +import 'package:cake_wallet/salvium/salvium.dart'; + +Future updateSalviumRate(FiatConversionStore fiatConversionStore) async { + try { + final rate = salvium!.getAssetRate(); + final base = rate.firstWhere((row) => row.asset == 'XUSD'); + + rate.forEach((row) { + final cur = CryptoCurrency.fromString(row.asset); + final baseRate = moneroAmountToDouble(amount: base.rate); + final rowRate = moneroAmountToDouble(amount: row.rate); + + if (cur == CryptoCurrency.xusd) { + fiatConversionStore.prices[cur] = 1.0; + return; + } + + fiatConversionStore.prices[cur] = baseRate / rowRate; + }); + } catch(_) { + // FIX-ME: handle exception + } +} \ No newline at end of file diff --git a/lib/reactions/bip39_wallet_utils.dart b/lib/reactions/bip39_wallet_utils.dart index a31fec91f2..b8135ee88b 100644 --- a/lib/reactions/bip39_wallet_utils.dart +++ b/lib/reactions/bip39_wallet_utils.dart @@ -15,6 +15,7 @@ bool isBIP39Wallet(WalletType walletType) { case WalletType.monero: case WalletType.wownero: case WalletType.haven: + case WalletType.salvium: case WalletType.none: return false; } diff --git a/lib/reactions/fiat_rate_update.dart b/lib/reactions/fiat_rate_update.dart index e46ef4b648..999bfbdbbe 100644 --- a/lib/reactions/fiat_rate_update.dart +++ b/lib/reactions/fiat_rate_update.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:cake_wallet/core/fiat_conversion_service.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/update_haven_rate.dart'; +import 'package:cake_wallet/entities/update_salvium_rate.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/solana/solana.dart'; @@ -30,6 +31,8 @@ Future startFiatRateUpdate( if (appStore.wallet!.type == WalletType.haven) { await updateHavenRate(fiatConversionStore); + } else if (appStore.wallet!.type == WalletType.salvium) { + await updateSalviumRate(fiatConversionStore); } else { fiatConversionStore.prices[appStore.wallet!.currency] = await FiatConversionService.fetchPrice( diff --git a/lib/reactions/on_current_wallet_change.dart b/lib/reactions/on_current_wallet_change.dart index b804ff14ef..3594c35ea2 100644 --- a/lib/reactions/on_current_wallet_change.dart +++ b/lib/reactions/on_current_wallet_change.dart @@ -1,6 +1,7 @@ import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/update_haven_rate.dart'; +import 'package:cake_wallet/entities/update_salvium_rate.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/solana/solana.dart'; @@ -83,6 +84,10 @@ void startCurrentWalletChangeReaction( await updateHavenRate(fiatConversionStore); } + if (wallet.type == WalletType.salvium) { + await updateSalviumRate(fiatConversionStore); + } + if (wallet.walletInfo.address.isEmpty) { wallet.walletInfo.address = wallet.walletAddresses.address; diff --git a/lib/reactions/on_wallet_sync_status_change.dart b/lib/reactions/on_wallet_sync_status_change.dart index 96305de04c..85ddc4beb0 100644 --- a/lib/reactions/on_wallet_sync_status_change.dart +++ b/lib/reactions/on_wallet_sync_status_change.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/entities/update_haven_rate.dart'; +import 'package:cake_wallet/entities/update_salvium_rate.dart'; import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:mobx/mobx.dart'; @@ -23,6 +24,10 @@ void startWalletSyncStatusChangeReaction( if (wallet.type == WalletType.haven) { await updateHavenRate(fiatConversionStore); } + + if (wallet.type == WalletType.salvium) { + await updateSalviumRate(fiatConversionStore); + } } if (status is SyncingSyncStatus) { await WakelockPlus.enable(); diff --git a/lib/salvium/cw_salvium.dart b/lib/salvium/cw_salvium.dart new file mode 100644 index 0000000000..078dc45daf --- /dev/null +++ b/lib/salvium/cw_salvium.dart @@ -0,0 +1,331 @@ +part of 'salvium.dart'; + +class CWSalviumAccountList extends SalviumAccountList { + CWSalviumAccountList(this._wallet); + + final Object _wallet; + + @override + @computed + ObservableList get accounts { + final salviumWallet = _wallet as SalviumWallet; + final accounts = salviumWallet.walletAddresses.accountList.accounts + .map((acc) => Account(id: acc.id, label: acc.label)) + .toList(); + return ObservableList.of(accounts); + } + + @override + void update(Object wallet) { + final salviumWallet = wallet as SalviumWallet; + salviumWallet.walletAddresses.accountList.update(); + } + + @override + void refresh(Object wallet) { + final salviumWallet = wallet as SalviumWallet; + salviumWallet.walletAddresses.accountList.refresh(); + } + + @override + List getAll(Object wallet) { + final salviumWallet = wallet as SalviumWallet; + return salviumWallet.walletAddresses.accountList + .getAll() + .map((acc) => Account(id: acc.id, label: acc.label)) + .toList(); + } + + @override + Future addAccount(Object wallet, {required String label}) async { + final salviumWallet = wallet as SalviumWallet; + await salviumWallet.walletAddresses.accountList.addAccount(label: label); + } + + @override + Future setLabelAccount(Object wallet, + {required int accountIndex, required String label}) async { + final salviumWallet = wallet as SalviumWallet; + await salviumWallet.walletAddresses.accountList + .setLabelAccount(accountIndex: accountIndex, label: label); + } +} + +class CWSalviumSubaddressList extends MoneroSubaddressList { + CWSalviumSubaddressList(this._wallet); + + final Object _wallet; + + @override + @computed + ObservableList get subaddresses { + final salviumWallet = _wallet as SalviumWallet; + final subAddresses = salviumWallet.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 salviumWallet = wallet as SalviumWallet; + salviumWallet.walletAddresses.subaddressList.update(accountIndex: accountIndex); + } + + @override + void refresh(Object wallet, {required int accountIndex}) { + final salviumWallet = wallet as SalviumWallet; + salviumWallet.walletAddresses.subaddressList.refresh(accountIndex: accountIndex); + } + + @override + List getAll(Object wallet) { + final salviumWallet = wallet as SalviumWallet; + return salviumWallet.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 salviumWallet = wallet as SalviumWallet; + await salviumWallet.walletAddresses.subaddressList + .addSubaddress(accountIndex: accountIndex, label: label); + } + + @override + Future setLabelSubaddress(Object wallet, + {required int accountIndex, required int addressIndex, required String label}) async { + final salviumWallet = wallet as SalviumWallet; + await salviumWallet.walletAddresses.subaddressList + .setLabelSubaddress(accountIndex: accountIndex, addressIndex: addressIndex, label: label); + } +} + +class CWSalviumWalletDetails extends SalviumWalletDetails { + CWSalviumWalletDetails(this._wallet); + + final Object _wallet; + + @computed + @override + Account get account { + final salviumWallet = _wallet as SalviumWallet; + final acc = salviumWallet.walletAddresses.account as monero_account.Account; + return Account(id: acc.id, label: acc.label); + } + + @computed + @override + SalviumBalance get balance { + final salviumWallet = _wallet as SalviumWallet; + final balance = salviumWallet.balance; + throw Exception('Unimplemented'); + //return SalviumBalance( + // fullBalance: balance.fullBalance, + // unlockedBalance: balance.unlockedBalance); + } +} + +class CWSalvium extends Salvium { + @override + SalviumAccountList getAccountList(Object wallet) { + return CWSalviumAccountList(wallet); + } + + @override + MoneroSubaddressList getSubaddressList(Object wallet) { + return CWSalviumSubaddressList(wallet); + } + + @override + TransactionHistoryBase getTransactionHistory(Object wallet) { + final salviumWallet = wallet as SalviumWallet; + return salviumWallet.transactionHistory; + } + + @override + SalviumWalletDetails getMoneroWalletDetails(Object wallet) { + return CWSalviumWalletDetails(wallet); + } + + @override + int getHeightByDate({required DateTime date}) => getSalviumHeightByDate(date: date); + + @override + Future getCurrentHeight() => getSalviumCurrentHeight(); + + @override + TransactionPriority getDefaultTransactionPriority() { + return SalviumTransactionPriority.automatic; + } + + @override + TransactionPriority deserializeMoneroTransactionPriority({required int raw}) { + return SalviumTransactionPriority.deserialize(raw: raw); + } + + @override + List getTransactionPriorities() { + return SalviumTransactionPriority.all; + } + + @override + List getMoneroWordList(String language) { + 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 createSalviumRestoreWalletFromKeysCredentials( + {required String name, + required String spendKey, + required String viewKey, + required String address, + required String password, + required String language, + required int height}) { + return SalviumRestoreWalletFromKeysCredentials( + name: name, + spendKey: spendKey, + viewKey: viewKey, + address: address, + password: password, + language: language, + height: height); + } + + @override + WalletCredentials createSalviumRestoreWalletFromSeedCredentials( + {required String name, + required String password, + required int height, + required String mnemonic}) { + return SalviumRestoreWalletFromSeedCredentials( + name: name, password: password, height: height, mnemonic: mnemonic); + } + + @override + WalletCredentials createSalviumNewWalletCredentials( + {required String name, required String language, String? password}) { + return SalviumNewWalletCredentials(name: name, password: password, language: language); + } + + @override + Map getKeys(Object wallet) { + final salviumWallet = wallet as SalviumWallet; + final keys = salviumWallet.keys; + return { + 'privateSpendKey': keys.privateSpendKey, + 'privateViewKey': keys.privateViewKey, + 'publicSpendKey': keys.publicSpendKey, + 'publicViewKey': keys.publicViewKey + }; + } + + @override + Object createSalviumTransactionCreationCredentials( + {required List outputs, + required TransactionPriority priority, + required String assetType}) { + return SalviumTransactionCreationCredentials( + 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 SalviumTransactionPriority, + assetType: assetType); + } + + @override + String formatterMoneroAmountToString({required int amount}) { + return moneroAmountToString(amount: amount); + } + + @override + double formatterMoneroAmountToDouble({required int amount}) { + return moneroAmountToDouble(amount: amount); + } + + @override + int formatterMoneroParseAmount({required String amount}) { + return moneroParseAmount(amount: amount); + } + + @override + Account getCurrentAccount(Object wallet) { + final salviumWallet = wallet as SalviumWallet; + final acc = salviumWallet.walletAddresses.account as monero_account.Account; + return Account(id: acc.id, label: acc.label); + } + + @override + void setCurrentAccount(Object wallet, int id, String label) { + final salviumWallet = wallet as SalviumWallet; + salviumWallet.walletAddresses.account = monero_account.Account(id: id, label: label); + } + + @override + void onStartup() { + monero_wallet_api.onStartup(); + } + + @override + int getTransactionInfoAccountId(TransactionInfo tx) { + final salviumTransactionInfo = tx as SalviumTransactionInfo; + return salviumTransactionInfo.accountIndex; + } + + @override + WalletService createSalviumWalletService(Box walletInfoSource) { + return SalviumWalletService(walletInfoSource); + } + + @override + String getTransactionAddress(Object wallet, int accountIndex, int addressIndex) { + final salviumWallet = wallet as SalviumWallet; + return salviumWallet.getTransactionAddress(accountIndex, addressIndex); + } + + @override + CryptoCurrency assetOfTransaction(TransactionInfo tx) { + final transaction = tx as SalviumTransactionInfo; + final asset = CryptoCurrency.fromString(transaction.assetType); + return asset; + } + + @override + List getAssetRate() => + getRate().map((rate) => AssetRate(rate.getAssetType(), rate.getRate())).toList(); +} diff --git a/lib/src/screens/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index 8c236404d6..c644edc083 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -8,6 +8,7 @@ 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/salvium_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'; @@ -366,6 +367,8 @@ class _DashboardPageView extends BasePage { _showHavenPopup(context); + _showSalviumPopup(context); + var needToPresentYat = false; var isInactive = false; @@ -461,4 +464,22 @@ class _DashboardPageView extends BasePage { ); } } + + void _showSalviumPopup(BuildContext context) async { + final List salviumWalletList = await dashboardViewModel.checkForSalviumWallets(); + + if (salviumWalletList.isNotEmpty) { + Future.delayed( + Duration(seconds: 1), + () { + showPopUp( + context: context, + builder: (BuildContext context) { + return SalviumWalletRemovalPopup(salviumWalletList); + }, + ); + }, + ); + } + } } 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 e5f38010d0..b0e19c387d 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 @@ -38,6 +38,7 @@ class _DesktopWalletSelectionDropDownState extends State( context: context, builder: (BuildContext context) { diff --git a/lib/src/screens/dashboard/widgets/menu_widget.dart b/lib/src/screens/dashboard/widgets/menu_widget.dart index b49a085846..9482273f33 100644 --- a/lib/src/screens/dashboard/widgets/menu_widget.dart +++ b/lib/src/screens/dashboard/widgets/menu_widget.dart @@ -30,6 +30,7 @@ class MenuWidgetState extends State { this.bitcoinIcon = Image.asset('assets/images/bitcoin_menu.png'), this.litecoinIcon = Image.asset('assets/images/litecoin_menu.png'), this.havenIcon = Image.asset('assets/images/haven_menu.png'), + this.salviumIcon = Image.asset('assets/images/salvium_menu.png'), this.ethereumIcon = Image.asset('assets/images/eth_icon.png'), this.nanoIcon = Image.asset('assets/images/nano_icon.png'), this.bananoIcon = Image.asset('assets/images/nano_icon.png'), @@ -54,6 +55,7 @@ class MenuWidgetState extends State { Image bitcoinIcon; Image litecoinIcon; Image havenIcon; + Image salviumIcon; Image ethereumIcon; Image bitcoinCashIcon; Image nanoIcon; @@ -229,6 +231,8 @@ class MenuWidgetState extends State { return litecoinIcon; case WalletType.haven: return havenIcon; + case WalletType.salvium: + return salviumIcon; case WalletType.ethereum: return ethereumIcon; case WalletType.bitcoinCash: diff --git a/lib/src/screens/new_wallet/new_wallet_type_page.dart b/lib/src/screens/new_wallet/new_wallet_type_page.dart index 6cf21ae585..5513638bc7 100644 --- a/lib/src/screens/new_wallet/new_wallet_type_page.dart +++ b/lib/src/screens/new_wallet/new_wallet_type_page.dart @@ -178,7 +178,7 @@ class WalletTypeFormState extends State { Future onTypeSelected() async { if (selected == null) throw Exception('Wallet Type is not selected yet.'); - if (selected == WalletType.haven && widget.isCreate) { + if ((selected == WalletType.haven || selected == WalletType.salvium) && widget.isCreate) { return await showPopUp( context: context, builder: (BuildContext context) => PopUpCancellableAlertDialog( diff --git a/lib/src/screens/receive/widgets/address_list.dart b/lib/src/screens/receive/widgets/address_list.dart index 9f15018d02..90e09a3d32 100644 --- a/lib/src/screens/receive/widgets/address_list.dart +++ b/lib/src/screens/receive/widgets/address_list.dart @@ -92,7 +92,8 @@ class _AddressListState extends State { walletAddressListViewModel: widget.addressListViewModel, trailingButtonTap: () async { if (widget.addressListViewModel.type == WalletType.monero || - widget.addressListViewModel.type == WalletType.haven) { + widget.addressListViewModel.type == WalletType.haven || + widget.addressListViewModel.type == WalletType.salvium) { await showPopUp( context: context, builder: (_) => getIt.get()); updateItems(); diff --git a/lib/src/screens/restore/wallet_restore_page.dart b/lib/src/screens/restore/wallet_restore_page.dart index 6215e26c35..88c481f9de 100644 --- a/lib/src/screens/restore/wallet_restore_page.dart +++ b/lib/src/screens/restore/wallet_restore_page.dart @@ -277,14 +277,15 @@ class WalletRestorePage extends BasePage { if ((walletRestoreViewModel.type == WalletType.monero || walletRestoreViewModel.type == WalletType.wownero || - walletRestoreViewModel.type == WalletType.haven) && + walletRestoreViewModel.type == WalletType.haven || + walletRestoreViewModel.type == WalletType.salvium) && seedWords.length != WalletRestoreViewModelBase.moneroSeedMnemonicLength) { return false; } // bip39: final validBip39SeedLengths = [12, 18, 24]; - final nonBip39WalletTypes = [WalletType.monero, WalletType.wownero, WalletType.haven]; + final nonBip39WalletTypes = [WalletType.monero, WalletType.wownero, WalletType.haven, WalletType.salvium]; // if it's a bip39 wallet and the length is not valid return false if (!nonBip39WalletTypes.contains(walletRestoreViewModel.type) && !(validBip39SeedLengths.contains(seedWords.length))) { diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index f7e6515de4..090a765db5 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -124,6 +124,7 @@ class WalletListBodyState extends State { final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24); final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24); final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24); + final salviumIcon = Image.asset('assets/images/salvium_logo.png', height: 24, width: 24); final ethereumIcon = Image.asset('assets/images/eth_icon.png', height: 24, width: 24); final bitcoinCashIcon = Image.asset('assets/images/bch_icon.png', height: 24, width: 24); final nanoIcon = Image.asset('assets/images/nano_icon.png', height: 24, width: 24); diff --git a/lib/src/screens/welcome/create_pin_welcome_page.dart b/lib/src/screens/welcome/create_pin_welcome_page.dart index d8ff1578e5..19ff082639 100644 --- a/lib/src/screens/welcome/create_pin_welcome_page.dart +++ b/lib/src/screens/welcome/create_pin_welcome_page.dart @@ -25,6 +25,10 @@ class CreatePinWelcomePage extends BasePage { return S.of(context).haven_app; } + if (isSalvium) { + return S.of(context).salvium_app; + } + return S.of(context).cake_wallet; } @@ -37,6 +41,10 @@ class CreatePinWelcomePage extends BasePage { return S.of(context).haven_app_wallet_text; } + if (isSalvium) { + return S.of(context).salvium_app_wallet_text; + } + return S.of(context).new_first_wallet_text; } diff --git a/lib/src/widgets/salvium_wallet_removal_popup.dart b/lib/src/widgets/salvium_wallet_removal_popup.dart new file mode 100644 index 0000000000..62950d9091 --- /dev/null +++ b/lib/src/widgets/salvium_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 SalviumWalletRemovalPopup extends StatelessWidget { + final List affectedWalletNames; + + const SalviumWalletRemovalPopup(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 Salvium wallets in your list. Salvium is getting removed in next release of Cake Wallet, and you currently have Salvium in the following wallets:\n\n[${affectedWalletNames.join(", ")}]\n\nPlease move your funds to other wallet, as you will lose access to your Salvium 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 1ecaf50cc9..d9dbe7b8e9 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -125,6 +125,7 @@ abstract class SettingsStoreBase with Store { TransactionPriority? initialMoneroTransactionPriority, TransactionPriority? initialWowneroTransactionPriority, TransactionPriority? initialHavenTransactionPriority, + TransactionPriority? initialSalviumTransactionPriority, TransactionPriority? initialLitecoinTransactionPriority, TransactionPriority? initialEthereumTransactionPriority, TransactionPriority? initialPolygonTransactionPriority, @@ -195,6 +196,10 @@ abstract class SettingsStoreBase with Store { priority[WalletType.haven] = initialHavenTransactionPriority; } + if (initialSalviumTransactionPriority != null) { + priority[WalletType.salvium] = initialSalviumTransactionPriority; + } + if (initialLitecoinTransactionPriority != null) { priority[WalletType.litecoin] = initialLitecoinTransactionPriority; } @@ -255,6 +260,9 @@ abstract class SettingsStoreBase with Store { case WalletType.haven: key = PreferencesKey.havenTransactionPriority; break; + case WalletType.salvium: + key = PreferencesKey.salviumTransactionPriority; + break; case WalletType.ethereum: key = PreferencesKey.ethereumTransactionPriority; break; @@ -854,6 +862,7 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getInt(PreferencesKey.bitcoinTransactionPriority)!); TransactionPriority? havenTransactionPriority; + TransactionPriority? salviumTransactionPriority; TransactionPriority? litecoinTransactionPriority; TransactionPriority? ethereumTransactionPriority; TransactionPriority? polygonTransactionPriority; @@ -864,6 +873,10 @@ abstract class SettingsStoreBase with Store { havenTransactionPriority = monero?.deserializeMoneroTransactionPriority( raw: sharedPreferences.getInt(PreferencesKey.havenTransactionPriority)!); } + if (sharedPreferences.getInt(PreferencesKey.salviumTransactionPriority) != null) { + salviumTransactionPriority = monero?.deserializeMoneroTransactionPriority( + raw: sharedPreferences.getInt(PreferencesKey.salviumTransactionPriority)!); + } if (sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority) != null) { litecoinTransactionPriority = bitcoin?.deserializeLitecoinTransactionPriority( sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority)!); @@ -888,6 +901,7 @@ abstract class SettingsStoreBase with Store { moneroTransactionPriority ??= monero?.getDefaultTransactionPriority(); bitcoinTransactionPriority ??= bitcoin?.getMediumTransactionPriority(); havenTransactionPriority ??= monero?.getDefaultTransactionPriority(); + salviumTransactionPriority ??= monero?.getDefaultTransactionPriority(); litecoinTransactionPriority ??= bitcoin?.getLitecoinTransactionPriorityMedium(); ethereumTransactionPriority ??= ethereum?.getDefaultTransactionPriority(); bitcoinCashTransactionPriority ??= bitcoinCash?.getDefaultTransactionPriority(); @@ -982,6 +996,7 @@ abstract class SettingsStoreBase with Store { final bitcoinCashElectrumServerId = sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey); final havenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey); + final salviumNodeId = sharedPreferences.getInt(PreferencesKey.currentSalviumNodeIdKey); final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey); final polygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey); final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey); @@ -993,6 +1008,7 @@ abstract class SettingsStoreBase with Store { final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId); final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId); final havenNode = nodeSource.get(havenNodeId); + final salviumNode = nodeSource.get(salviumNodeId); final ethereumNode = nodeSource.get(ethereumNodeId); final polygonNode = nodeSource.get(polygonNodeId); final bitcoinCashElectrumServer = nodeSource.get(bitcoinCashElectrumServerId); @@ -1050,6 +1066,10 @@ abstract class SettingsStoreBase with Store { nodes[WalletType.haven] = havenNode; } + if (salviumNode != null) { + nodes[WalletType.salvium] = salviumNode; + } + if (ethereumNode != null) { nodes[WalletType.ethereum] = ethereumNode; } @@ -1243,6 +1263,7 @@ abstract class SettingsStoreBase with Store { initialWowneroTransactionPriority: wowneroTransactionPriority, initialBitcoinTransactionPriority: bitcoinTransactionPriority, initialHavenTransactionPriority: havenTransactionPriority, + initialSalviumTransactionPriority: salviumTransactionPriority, initialLitecoinTransactionPriority: litecoinTransactionPriority, initialBitcoinCashTransactionPriority: bitcoinCashTransactionPriority, initialShouldRequireTOTP2FAForAccessingWallet: shouldRequireTOTP2FAForAccessingWallet, @@ -1295,6 +1316,11 @@ abstract class SettingsStoreBase with Store { priority[WalletType.haven] = monero!.deserializeMoneroTransactionPriority( raw: sharedPreferences.getInt(PreferencesKey.havenTransactionPriority)!); } + if (monero != null && + sharedPreferences.getInt(PreferencesKey.salviumTransactionPriority) != null) { + priority[WalletType.salvium] = monero!.deserializeMoneroTransactionPriority( + raw: sharedPreferences.getInt(PreferencesKey.salviumTransactionPriority)!); + } if (bitcoin != null && sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority) != null) { priority[WalletType.litecoin] = bitcoin!.deserializeLitecoinTransactionPriority( @@ -1414,6 +1440,7 @@ abstract class SettingsStoreBase with Store { final bitcoinCashElectrumServerId = sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey); final havenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey); + final salviumNodeId = sharedPreferences.getInt(PreferencesKey.currentSalviumNodeIdKey); final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey); final polygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey); final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey); @@ -1424,6 +1451,7 @@ abstract class SettingsStoreBase with Store { final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId); final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId); final havenNode = nodeSource.get(havenNodeId); + final salviumNode = nodeSource.get(salviumNodeId); final ethereumNode = nodeSource.get(ethereumNodeId); final polygonNode = nodeSource.get(polygonNodeId); final bitcoinCashNode = nodeSource.get(bitcoinCashElectrumServerId); @@ -1447,6 +1475,10 @@ abstract class SettingsStoreBase with Store { nodes[WalletType.haven] = havenNode; } + if (salviumNode != null) { + nodes[WalletType.salvium] = salviumNode; + } + if (ethereumNode != null) { nodes[WalletType.ethereum] = ethereumNode; } @@ -1589,6 +1621,9 @@ abstract class SettingsStoreBase with Store { case WalletType.haven: await _sharedPreferences.setInt(PreferencesKey.currentHavenNodeIdKey, node.key as int); break; + case WalletType.salvium: + await _sharedPreferences.setInt(PreferencesKey.currentSalviumNodeIdKey, node.key as int); + break; case WalletType.ethereum: await _sharedPreferences.setInt(PreferencesKey.currentEthereumNodeIdKey, node.key as int); break; diff --git a/lib/view_model/advanced_privacy_settings_view_model.dart b/lib/view_model/advanced_privacy_settings_view_model.dart index 85b9dbead1..47564a7071 100644 --- a/lib/view_model/advanced_privacy_settings_view_model.dart +++ b/lib/view_model/advanced_privacy_settings_view_model.dart @@ -54,6 +54,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { case WalletType.wownero: case WalletType.none: case WalletType.haven: + case WalletType.salvium: return false; } } diff --git a/lib/view_model/contact_list/contact_list_view_model.dart b/lib/view_model/contact_list/contact_list_view_model.dart index eb3fb837ee..3298c52f44 100644 --- a/lib/view_model/contact_list/contact_list_view_model.dart +++ b/lib/view_model/contact_list/contact_list_view_model.dart @@ -28,7 +28,7 @@ abstract class ContactListViewModelBase with Store { isAutoGenerateEnabled = settingsStore.autoGenerateSubaddressStatus == AutoGenerateSubaddressStatus.enabled { walletInfoSource.values.forEach((info) { - if ([WalletType.monero, WalletType.wownero, WalletType.haven].contains(info.type) && + if ([WalletType.monero, WalletType.wownero, WalletType.haven, WalletType.salvium].contains(info.type) && info.addressInfos != null) { for (var key in info.addressInfos!.keys) { final value = info.addressInfos![key]; @@ -43,7 +43,7 @@ abstract class ContactListViewModelBase with Store { } } } else if (info.addresses?.isNotEmpty == true && info.addresses!.length > 1) { - if ([WalletType.monero, WalletType.wownero, WalletType.haven].contains(info.type)) { + if ([WalletType.monero, WalletType.wownero, WalletType.haven, WalletType.salvium].contains(info.type)) { final address = info.address; final name = _createName(info.name, "", key: 0); walletContacts.add(WalletContact( @@ -71,7 +71,7 @@ abstract class ContactListViewModelBase with Store { walletContacts.add(WalletContact( info.address, _createName(info.name, "", - key: [WalletType.monero, WalletType.wownero, WalletType.haven].contains(info.type) + key: [WalletType.monero, WalletType.wownero, WalletType.haven, WalletType.salvium].contains(info.type) ? 0 : null), walletTypeToCryptoCurrency(info.type), diff --git a/lib/view_model/dashboard/balance_view_model.dart b/lib/view_model/dashboard/balance_view_model.dart index 20dca292c6..45b528390c 100644 --- a/lib/view_model/dashboard/balance_view_model.dart +++ b/lib/view_model/dashboard/balance_view_model.dart @@ -152,6 +152,7 @@ abstract class BalanceViewModelBase with Store { case WalletType.monero: case WalletType.wownero: case WalletType.haven: + case WalletType.salvium: case WalletType.ethereum: case WalletType.polygon: case WalletType.nano: @@ -170,6 +171,7 @@ abstract class BalanceViewModelBase with Store { case WalletType.monero: case WalletType.wownero: case WalletType.haven: + case WalletType.salvium: case WalletType.ethereum: case WalletType.polygon: case WalletType.solana: diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 808657f66c..fa0cae613e 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -390,7 +390,8 @@ abstract class DashboardViewModelBase with Store { wallet.type == WalletType.monero || wallet.type == WalletType.litecoin || wallet.type == WalletType.wownero || - wallet.type == WalletType.haven; + wallet.type == WalletType.haven || + wallet.type == WalletType.salvium; @computed bool get isMoneroViewOnly { @@ -568,6 +569,7 @@ abstract class DashboardViewModelBase with Store { case WalletType.wownero: return true; case WalletType.haven: + case WalletType.salvium: case WalletType.none: return false; } @@ -739,8 +741,8 @@ abstract class DashboardViewModelBase with Store { } void updateActions() { - hasExchangeAction = !isHaven; - hasTradeAction = !isHaven; + hasExchangeAction = !isHaven || !isSalvium; + hasTradeAction = !isHaven || !isSalvium; } @computed @@ -763,6 +765,14 @@ abstract class DashboardViewModelBase with Store { .toList(); } + Future> checkForSalviumWallets() async { + final walletInfoSource = await CakeHive.openBox(WalletInfo.boxName); + return walletInfoSource.values + .where((element) => element.type == WalletType.salvium) + .map((e) => e.name) + .toList(); + } + Future> checkAffectedWallets() async { try { // await load file diff --git a/lib/view_model/dashboard/receive_option_view_model.dart b/lib/view_model/dashboard/receive_option_view_model.dart index 744e4c58d9..d800a98d9a 100644 --- a/lib/view_model/dashboard/receive_option_view_model.dart +++ b/lib/view_model/dashboard/receive_option_view_model.dart @@ -33,6 +33,9 @@ abstract class ReceiveOptionViewModelBase with Store { case WalletType.haven: _options = [ReceivePageOption.mainnet]; break; + case WalletType.salvium: + _options = [ReceivePageOption.mainnet]; + break; default: _options = ReceivePageOptions; } diff --git a/lib/view_model/dashboard/sign_view_model.dart b/lib/view_model/dashboard/sign_view_model.dart index 5b1b4fc00c..52cb6193be 100644 --- a/lib/view_model/dashboard/sign_view_model.dart +++ b/lib/view_model/dashboard/sign_view_model.dart @@ -25,6 +25,7 @@ abstract class SignViewModelBase with Store { WalletType.bitcoinCash, WalletType.litecoin, WalletType.haven, + WalletType.salvium, ].contains(wallet.type); @action diff --git a/lib/view_model/dashboard/transaction_list_item.dart b/lib/view_model/dashboard/transaction_list_item.dart index d9b361fd24..a42b841519 100644 --- a/lib/view_model/dashboard/transaction_list_item.dart +++ b/lib/view_model/dashboard/transaction_list_item.dart @@ -15,6 +15,7 @@ import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/view_model/dashboard/action_list_item.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/salvium/salvium.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart'; import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart'; @@ -67,6 +68,11 @@ class TransactionListItem extends ActionListItem with Keyable { return ' (${transaction.confirmations}/10)'; } break; + case WalletType.salvium: + if (transaction.confirmations >= 0 && transaction.confirmations < 10) { + return ' (${transaction.confirmations}/10)'; + } + break; case WalletType.wownero: if (transaction.confirmations >= 0 && transaction.confirmations < 3) { return ' (${transaction.confirmations}/3)'; @@ -101,6 +107,7 @@ class TransactionListItem extends ActionListItem with Keyable { if ([ WalletType.monero, WalletType.haven, + WalletType.salvium, WalletType.wownero, WalletType.litecoin, ].contains(balanceViewModel.wallet.type)) { @@ -173,6 +180,13 @@ class TransactionListItem extends ActionListItem with Keyable { cryptoAmount: haven!.formatterMoneroAmountToDouble(amount: transaction.amount), price: price); break; + case WalletType.salvium: + final asset = salvium!.assetOfTransaction(transaction); + final price = balanceViewModel.fiatConvertationStore.prices[asset]; + amount = calculateFiatAmountRaw( + cryptoAmount: salvium!.formatterSalviumAmountToDouble(amount: transaction.amount), + price: price); + break; case WalletType.ethereum: final asset = ethereum!.assetOfTransaction(balanceViewModel.wallet, transaction); final price = balanceViewModel.fiatConvertationStore.prices[asset]; diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index d29b7df6b6..25131a9e86 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -307,6 +307,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with case WalletType.wownero: case WalletType.haven: return transactionPriority == monero!.getMoneroTransactionPrioritySlow(); + case WalletType.salvium: + return transactionPriority == salvium!.getSalviumTransactionPrioritySlow(); case WalletType.bitcoin: return transactionPriority == bitcoin!.getBitcoinTransactionPrioritySlow(); case WalletType.litecoin: @@ -710,6 +712,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with depositCurrency = CryptoCurrency.xhv; receiveCurrency = CryptoCurrency.btc; break; + case WalletType.salvium: + depositCurrency = CryptoCurrency.sal; + receiveCurrency = CryptoCurrency.xmr; + break; case WalletType.ethereum: depositCurrency = CryptoCurrency.eth; receiveCurrency = CryptoCurrency.xmr; @@ -811,6 +817,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with switch (wallet.type) { case WalletType.monero: case WalletType.haven: + case WalletType.salvium: case WalletType.wownero: _settingsStore.priority[wallet.type] = monero!.getMoneroTransactionPriorityAutomatic(); break; diff --git a/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart b/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart index 8d626e2583..169922492a 100644 --- a/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart +++ b/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart @@ -6,6 +6,7 @@ import 'package:mobx/mobx.dart'; import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/salvium/salvium.dart'; import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart'; part 'monero_account_edit_or_create_view_model.g.dart'; @@ -14,7 +15,7 @@ class MoneroAccountEditOrCreateViewModel = MoneroAccountEditOrCreateViewModelBas with _$MoneroAccountEditOrCreateViewModel; abstract class MoneroAccountEditOrCreateViewModelBase with Store { - MoneroAccountEditOrCreateViewModelBase(this._moneroAccountList, this._wowneroAccountList, this._havenAccountList, + MoneroAccountEditOrCreateViewModelBase(this._moneroAccountList, this._wowneroAccountList, this._havenAccountList, this._salviumAccountList, {required WalletBase wallet, AccountListItem? accountListItem}) : state = InitialExecutionState(), isEdit = accountListItem != null, @@ -33,6 +34,7 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store { final MoneroAccountList _moneroAccountList; final WowneroAccountList? _wowneroAccountList; final HavenAccountList? _havenAccountList; + final SalviumAccountList? _salviumAccountList; final AccountListItem? _accountListItem; final WalletBase _wallet; @@ -45,6 +47,10 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store { await saveHaven(); } + if (_wallet.type == WalletType.salvium) { + await saveSalvium(); + } + if (_wallet.type == WalletType.wownero) { await saveWownero(); } @@ -98,6 +104,32 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store { } } + Future saveSalvium() async { + if (!(_wallet.type == WalletType.salvium)) { + return; + } + + try { + state = IsExecutingState(); + + if (_accountListItem != null) { + await _salviumAccountList!.setLabelAccount( + _wallet, + accountIndex: _accountListItem!.id, + label: label); + } else { + await _salviumAccountList!.addAccount( + _wallet, + label: label); + } + + await _wallet.save(); + state = ExecutedSuccessfullyState(); + } catch (e) { + state = FailureState(e.toString()); + } + } + Future saveWownero() async { try { state = IsExecutingState(); diff --git a/lib/view_model/monero_account_list/monero_account_list_view_model.dart b/lib/view_model/monero_account_list/monero_account_list_view_model.dart index 4481067799..0ad5541dae 100644 --- a/lib/view_model/monero_account_list/monero_account_list_view_model.dart +++ b/lib/view_model/monero_account_list/monero_account_list_view_model.dart @@ -6,6 +6,7 @@ import 'package:cw_core/wallet_base.dart'; import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/salvium/salvium.dart'; part 'monero_account_list_view_model.g.dart'; @@ -37,6 +38,14 @@ abstract class MoneroAccountListViewModelBase with Store { .toList(); } + if (_wallet.type == WalletType.salvium) { + return salvium!.getAccountList(_wallet).accounts.map((acc) => AccountListItem( + label: acc.label, + id: acc.id, + isSelected: acc.id == salvium!.getCurrentAccount(_wallet).id)) + .toList(); + } + if (_wallet.type == WalletType.monero) { return monero !.getAccountList(_wallet) @@ -89,5 +98,12 @@ abstract class MoneroAccountListViewModelBase with Store { item.id, item.label); } + + if (_wallet.type == WalletType.salvium) { + salvium!.setCurrentAccount( + _wallet, + item.id, + item.label); + } } } 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 8b3c70c5e6..1bd6c5888f 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 @@ -65,7 +65,7 @@ abstract class NodeCreateOrEditViewModelBase with Store { bool get isReady => address.isNotEmpty && port.isNotEmpty; bool get hasAuthCredentials => - _walletType == WalletType.monero || _walletType == WalletType.wownero || _walletType == WalletType.haven; + _walletType == WalletType.monero || _walletType == WalletType.wownero || _walletType == WalletType.haven || _walletType == WalletType.salvium; bool get hasTestnetSupport => _walletType == WalletType.bitcoin; @@ -82,6 +82,7 @@ abstract class NodeCreateOrEditViewModelBase with Store { case WalletType.monero: case WalletType.wownero: case WalletType.haven: + case WalletType.salvium: case WalletType.litecoin: case WalletType.bitcoinCash: case WalletType.bitcoin: 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 2721fd7b36..a3bd2d1d7e 100644 --- a/lib/view_model/node_list/node_list_view_model.dart +++ b/lib/view_model/node_list/node_list_view_model.dart @@ -67,6 +67,9 @@ abstract class NodeListViewModelBase with Store { case WalletType.haven: node = getHavenDefaultNode(nodes: _nodeSource)!; break; + case WalletType.salvium: + node = getSalviumDefaultNode(nodes: _nodeSource)!; + break; case WalletType.ethereum: node = getEthereumDefaultNode(nodes: _nodeSource)!; break; diff --git a/lib/view_model/send/output.dart b/lib/view_model/send/output.dart index e53127e0ce..6653f00744 100644 --- a/lib/view_model/send/output.dart +++ b/lib/view_model/send/output.dart @@ -4,6 +4,7 @@ import 'package:cake_wallet/entities/parse_address_from_domain.dart'; import 'package:cake_wallet/entities/parsed_address.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/salvium/salvium.dart'; import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/solana/solana.dart'; @@ -102,6 +103,9 @@ abstract class OutputBase with Store { case WalletType.haven: _amount = haven!.formatterMoneroParseAmount(amount: _cryptoAmount); break; + case WalletType.salvium: + _amount = salvium!.formatterSalviumParseAmount(amount: _cryptoAmount); + break; case WalletType.ethereum: _amount = ethereum!.formatterEthereumParseAmount(_cryptoAmount); break; @@ -172,6 +176,10 @@ abstract class OutputBase with Store { return haven!.formatterMoneroAmountToDouble(amount: fee); } + if (_wallet.type == WalletType.salvium) { + return salvium!.formatterSalviumAmountToDouble(amount: fee); + } + if (_wallet.type == WalletType.ethereum) { return ethereum!.formatterEthereumAmountToDouble(amount: BigInt.from(fee)); } @@ -294,6 +302,9 @@ abstract class OutputBase with Store { case WalletType.haven: maximumFractionDigits = 12; break; + case WalletType.salvium: + maximumFractionDigits = 12; + break; case WalletType.ethereum: case WalletType.polygon: maximumFractionDigits = 12; diff --git a/lib/view_model/send/send_template_view_model.dart b/lib/view_model/send/send_template_view_model.dart index 3c78f3000c..1c36fc1224 100644 --- a/lib/view_model/send/send_template_view_model.dart +++ b/lib/view_model/send/send_template_view_model.dart @@ -52,6 +52,7 @@ abstract class SendTemplateViewModelBase with Store { bool get hasMultiRecipient => _wallet.type != WalletType.haven && + _wallet.type != WalletType.salvium && _wallet.type != WalletType.ethereum && _wallet.type != WalletType.polygon && _wallet.type != WalletType.solana && diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index fb96b25458..5e8186376e 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -48,6 +48,7 @@ import 'package:cake_wallet/view_model/send/send_view_model_state.dart'; import 'package:cake_wallet/entities/parsed_address.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/salvium/salvium.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:collection/collection.dart'; @@ -557,6 +558,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor return haven!.createHavenTransactionCreationCredentials( outputs: outputs, priority: priority!, assetType: selectedCryptoCurrency.title); + case WalletType.salvium: + return salvium!.createSalviumTransactionCreationCredentials( + outputs: outputs, priority: priority!, assetType: selectedCryptoCurrency.title); + case WalletType.ethereum: return ethereum!.createEthereumTransactionCredentials(outputs, priority: priority!, currency: selectedCryptoCurrency); @@ -684,7 +689,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor } if (walletType == WalletType.ethereum || walletType == WalletType.polygon || - walletType == WalletType.haven) { + walletType == WalletType.haven || + walletType == WalletType.salvium) { if (errorMessage.contains('gas required exceeds allowance') || errorMessage.contains('insufficient funds')) { return S.current.do_not_have_enough_gas_asset(currency.toString()); diff --git a/lib/view_model/transaction_details_view_model.dart b/lib/view_model/transaction_details_view_model.dart index e675acf173..49235c10c2 100644 --- a/lib/view_model/transaction_details_view_model.dart +++ b/lib/view_model/transaction_details_view_model.dart @@ -62,6 +62,9 @@ abstract class TransactionDetailsViewModelBase with Store { case WalletType.haven: _addHavenListItems(tx, dateFormat); break; + case WalletType.salvium: + _addSalviumListItems(tx, dateFormat); + break; case WalletType.ethereum: _addEthereumListItems(tx, dateFormat); break; @@ -166,6 +169,8 @@ abstract class TransactionDetailsViewModelBase with Store { return 'https://blockchair.com/bitcoin-cash/transaction/${txId}'; case WalletType.haven: return 'https://explorer.havenprotocol.org/search?value=${txId}'; + case WalletType.salvium: + return 'https://explorer.salvium.com/search?value=${txId}'; case WalletType.ethereum: return 'https://etherscan.io/tx/${txId}'; case WalletType.nano: @@ -196,6 +201,8 @@ abstract class TransactionDetailsViewModelBase with Store { return S.current.view_transaction_on + 'Blockchair.com'; case WalletType.haven: return S.current.view_transaction_on + 'explorer.havenprotocol.org'; + case WalletType.salvium: + return S.current.view_transaction_on + 'explorer.salvium.com'; case WalletType.ethereum: return S.current.view_transaction_on + 'etherscan.io'; case WalletType.nano: @@ -355,6 +362,37 @@ abstract class TransactionDetailsViewModelBase with Store { ]); } + void _addSalviumListItems(TransactionInfo tx, DateFormat dateFormat) { + items.addAll([ + StandartListItem( + title: S.current.transaction_details_transaction_id, + value: tx.txHash, + key: ValueKey('standard_list_item_transaction_details_id_key'), + ), + StandartListItem( + title: S.current.transaction_details_date, + value: dateFormat.format(tx.date), + key: ValueKey('standard_list_item_transaction_details_date_key'), + ), + StandartListItem( + title: S.current.transaction_details_height, + value: '${tx.height}', + key: ValueKey('standard_list_item_transaction_details_height_key'), + ), + StandartListItem( + title: S.current.transaction_details_amount, + value: tx.amountFormatted(), + key: ValueKey('standard_list_item_transaction_details_amount_key'), + ), + if (tx.feeFormatted()?.isNotEmpty ?? false) + StandartListItem( + title: S.current.transaction_details_fee, + value: tx.feeFormatted()!, + key: ValueKey('standard_list_item_transaction_details_fee_key'), + ), + ]); + } + void _addEthereumListItems(TransactionInfo tx, DateFormat dateFormat) { final _items = [ StandartListItem( diff --git a/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart index d365c8e006..b1af814706 100644 --- a/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart @@ -5,6 +5,7 @@ import 'package:cw_core/wallet_base.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/salvium/salvium.dart'; import 'package:cw_core/wallet_type.dart'; part 'wallet_address_edit_or_create_view_model.g.dart'; @@ -104,6 +105,11 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store { label: label); await wallet.save(); } + + if (wallet.type == WalletType.salvium) { + await salvium!.getSubaddressList(wallet).addSubaddress(wallet, accountIndex: salvium!.getCurrentAccount(wallet).id, label: label); + await wallet.save(); + } } Future _update() async { @@ -130,6 +136,13 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store { label: label); await wallet.save(); } + if (wallet.type == WalletType.salvium) { + await salvium!.getSubaddressList(wallet).setLabelSubaddress(wallet, + accountIndex: salvium!.getCurrentAccount(wallet).id, + addressIndex: index, + label: label); + await wallet.save(); + } } } } 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 3e399266a6..bdf69eb60e 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 @@ -9,6 +9,7 @@ import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/salvium/salvium.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/solana/solana.dart'; @@ -71,6 +72,21 @@ class HavenURI extends PaymentURI { } } +class SalviumURI extends PaymentURI { + SalviumURI({required super.amount, required super.address}); + + @override + String toString() { + var base = 'salvium:$address'; + + if (amount.isNotEmpty) { + base += '?tx_amount=${amount.replaceAll(',', '.')}'; + } + + return base; + } +} + class BitcoinURI extends PaymentURI { BitcoinURI({required super.amount, required super.address}); @@ -214,7 +230,7 @@ abstract class WalletAddressListViewModelBase }) : _baseItems = [], selectedCurrency = walletTypeToCryptoCurrency(appStore.wallet!.type), _cryptoNumberFormat = NumberFormat(_cryptoNumberPattern), - hasAccounts = [WalletType.monero, WalletType.wownero, WalletType.haven] + hasAccounts = [WalletType.monero, WalletType.wownero, WalletType.haven, WalletType.salvium] .contains(appStore.wallet!.type), amount = '', _settingsStore = appStore.settingsStore, @@ -227,7 +243,7 @@ abstract class WalletAddressListViewModelBase _init(); selectedCurrency = walletTypeToCryptoCurrency(wallet.type); - hasAccounts = [WalletType.monero, WalletType.wownero, WalletType.haven] + hasAccounts = [WalletType.monero, WalletType.wownero, WalletType.haven, WalletType.salvium] .contains(wallet.type); } @@ -278,6 +294,8 @@ abstract class WalletAddressListViewModelBase return MoneroURI(amount: amount, address: address.address); case WalletType.haven: return HavenURI(amount: amount, address: address.address); + case WalletType.salvium: + return SalviumURI(amount: amount, address: address.address); case WalletType.bitcoin: return BitcoinURI(amount: amount, address: address.address); case WalletType.litecoin: @@ -363,6 +381,21 @@ abstract class WalletAddressListViewModelBase addressList.addAll(addressItems); } + if (wallet.type == WalletType.salvium) { + final primaryAddress = salvium!.getSubaddressList(wallet).subaddresses.first; + final addressItems = salvium!.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 (isElectrumWallet) { if (bitcoin!.hasSelectedSilentPayments(wallet)) { final addressItems = @@ -511,6 +544,8 @@ abstract class WalletAddressListViewModelBase haven! .getSubaddressList(wallet) .update(wallet, accountIndex: haven!.getCurrentAccount(wallet).id); + } else if (wallet.type == WalletType.salvium) { + salvium!.getSubaddressList(wallet).update(wallet, accountIndex: salvium!.getCurrentAccount(wallet).id); } } @@ -526,6 +561,8 @@ abstract class WalletAddressListViewModelBase wownero!.getCurrentAccount(wallet).label; case WalletType.haven: return haven!.getCurrentAccount(wallet).label; + case WalletType.salvium: + return salvium!.getCurrentAccount(wallet).label; default: return ''; } @@ -537,6 +574,7 @@ abstract class WalletAddressListViewModelBase WalletType.monero, WalletType.wownero, WalletType.haven, + WalletType.salvium, WalletType.bitcoinCash, WalletType.bitcoin, WalletType.litecoin @@ -598,6 +636,7 @@ abstract class WalletAddressListViewModelBase WalletType.monero, WalletType.wownero, WalletType.haven, + WalletType.salvium, ].contains(wallet.type)) { _baseItems.add(WalletAccountListHeader()); } diff --git a/lib/view_model/wallet_keys_view_model.dart b/lib/view_model/wallet_keys_view_model.dart index 81eda7cc87..9133472e6d 100644 --- a/lib/view_model/wallet_keys_view_model.dart +++ b/lib/view_model/wallet_keys_view_model.dart @@ -1,5 +1,6 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/salvium/salvium.dart'; 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'; @@ -37,6 +38,7 @@ abstract class WalletKeysViewModelBase with Store { if (_appStore.wallet!.type == WalletType.monero || _appStore.wallet!.type == WalletType.haven || + _appStore.wallet!.type == WalletType.salvium || _appStore.wallet!.type == WalletType.wownero) { final accountTransactions = _getWalletTransactions(_appStore.wallet!); if (accountTransactions.isNotEmpty) { @@ -172,6 +174,47 @@ abstract class WalletKeysViewModelBase with Store { ]); } + if (_appStore.wallet!.type == WalletType.salvium) { + final keys = salvium!.getKeys(_appStore.wallet!); + + items.addAll([ + if (keys['primaryAddress'] != null) + StandartListItem( + title: S.current.primary_address, + value: keys['primaryAddress']!), + if (keys['publicSpendKey'] != null) + StandartListItem( + key: ValueKey('${_walletName}_wallet_public_spend_key_item_key'), + title: S.current.spend_key_public, + value: keys['publicSpendKey']!, + ), + if (keys['privateSpendKey'] != null) + StandartListItem( + key: ValueKey('${_walletName}_wallet_private_spend_key_item_key'), + title: S.current.spend_key_private, + value: keys['privateSpendKey']!, + ), + if (keys['publicViewKey'] != null) + StandartListItem( + key: ValueKey('${_walletName}_wallet_public_view_key_item_key'), + title: S.current.view_key_public, + value: keys['publicViewKey']!, + ), + if (keys['privateViewKey'] != null) + StandartListItem( + key: ValueKey('${_walletName}_wallet_private_view_key_item_key'), + title: S.current.view_key_private, + value: keys['privateViewKey']!, + ), + if (_appStore.wallet!.seed!.isNotEmpty) + StandartListItem( + key: ValueKey('${_walletName}_wallet_seed_item_key'), + title: S.current.wallet_seed, + value: _appStore.wallet!.seed!, + ), + ]); + } + if (_appStore.wallet!.type == WalletType.wownero) { final keys = wownero!.getKeys(_appStore.wallet!); @@ -295,6 +338,9 @@ abstract class WalletKeysViewModelBase with Store { if (_appStore.wallet!.type == WalletType.haven) { return await haven!.getCurrentHeight(); } + if (_appStore.wallet!.type == WalletType.salvium) { + return await salvium!.getCurrentHeight(); + } if (_appStore.wallet!.type == WalletType.monero) { return await monero!.getCurrentHeight(); } @@ -314,6 +360,8 @@ abstract class WalletKeysViewModelBase with Store { return 'litecoin-wallet'; case WalletType.haven: return 'haven-wallet'; + case WalletType.salvium: + return 'salvium-wallet'; case WalletType.ethereum: return 'ethereum-wallet'; case WalletType.bitcoinCash: @@ -369,6 +417,8 @@ 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.salvium) { + return salvium!.getTransactionHistory(wallet).transactions.values.toList(); } else if (wallet.type == WalletType.wownero) { return wownero!.getTransactionHistory(wallet).transactions.values.toList(); } @@ -380,6 +430,8 @@ abstract class WalletKeysViewModelBase with Store { return monero!.getHeightByDate(date: date); } else if (type == WalletType.haven) { return haven!.getHeightByDate(date: date); + } else if (type == WalletType.salvium) { + return salvium!.getHeightByDate(date: date); } else if (type == WalletType.wownero) { return wownero!.getHeightByDate(date: date); } diff --git a/lib/view_model/wallet_new_vm.dart b/lib/view_model/wallet_new_vm.dart index be30811d95..25d29c44e1 100644 --- a/lib/view_model/wallet_new_vm.dart +++ b/lib/view_model/wallet_new_vm.dart @@ -5,6 +5,7 @@ import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; import 'package:cake_wallet/core/wallet_creation_service.dart'; import 'package:cake_wallet/entities/seed_type.dart'; import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/salvium/salvium.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/solana/solana.dart'; @@ -46,7 +47,7 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store { String selectedMnemonicLanguage; bool get hasLanguageSelector => - [WalletType.monero, WalletType.haven, WalletType.wownero].contains(type); + [WalletType.monero, WalletType.haven, WalletType.wownero, WalletType.salvium].contains(type); int get seedPhraseWordsLength { switch (type) { @@ -100,6 +101,9 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store { case WalletType.haven: return haven!.createHavenNewWalletCredentials( name: name, language: options!.first as String, password: walletPassword); + case WalletType.salvium: + return salvium!.createSalviumNewWalletCredentials( + name: name, language: options!.first as String, password: walletPassword); case WalletType.ethereum: return ethereum!.createEthereumNewWalletCredentials( name: name, diff --git a/lib/view_model/wallet_restore_view_model.dart b/lib/view_model/wallet_restore_view_model.dart index d37b69f746..a8e889e1c1 100644 --- a/lib/view_model/wallet_restore_view_model.dart +++ b/lib/view_model/wallet_restore_view_model.dart @@ -5,6 +5,7 @@ import 'package:cake_wallet/core/wallet_creation_service.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/salvium/salvium.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/polygon/polygon.dart'; @@ -31,9 +32,9 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { Box walletInfoSource, SeedSettingsViewModel seedSettingsViewModel, {required WalletType type}) : hasSeedLanguageSelector = - type == WalletType.monero || type == WalletType.haven || type == WalletType.wownero, + type == WalletType.monero || type == WalletType.haven || type == WalletType.wownero || type == WalletType.salvium, hasBlockchainHeightLanguageSelector = - type == WalletType.monero || type == WalletType.haven || type == WalletType.wownero, + type == WalletType.monero || type == WalletType.haven || type == WalletType.wownero || type == WalletType.salvium, hasRestoreFromPrivateKey = type == WalletType.ethereum || type == WalletType.polygon || type == WalletType.nano || @@ -54,6 +55,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { case WalletType.tron: case WalletType.wownero: case WalletType.haven: + case WalletType.salvium: case WalletType.ethereum: case WalletType.polygon: availableModes = [WalletRestoreMode.seed, WalletRestoreMode.keys]; @@ -111,6 +113,9 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { case WalletType.haven: return haven!.createHavenRestoreWalletFromSeedCredentials( name: name, height: height, mnemonic: seed, password: password); + case WalletType.salvium: + return salvium!.createSalviumRestoreWalletFromSeedCredentials( + name: name, height: height, mnemonic: seed, password: password); case WalletType.ethereum: return ethereum!.createEthereumRestoreWalletFromSeedCredentials( name: name, @@ -195,6 +200,17 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { language: 'English', ); + case WalletType.salvium: + return salvium!.createSalviumRestoreWalletFromKeysCredentials( + name: name, + height: height, + spendKey: spendKey!, + viewKey: viewKey!, + address: address!, + password: password, + language: 'English', + ); + case WalletType.ethereum: return ethereum!.createEthereumRestoreWalletFromPrivateKey( name: name, diff --git a/lib/wallet_type_utils.dart b/lib/wallet_type_utils.dart index 459ca992b1..7cf5aacc84 100644 --- a/lib/wallet_type_utils.dart +++ b/lib/wallet_type_utils.dart @@ -11,6 +11,11 @@ bool get isHaven { && availableWalletTypes.first == WalletType.haven; } +bool get isSalvium { + return availableWalletTypes.length == 1 + && availableWalletTypes.first == WalletType.salvium; +} + bool get isSingleCoin { return availableWalletTypes.length == 1; @@ -28,6 +33,10 @@ String get approximatedAppName { if (isHaven) { return 'Haven'; } + + if (isSalvium) { + return 'Salvium'; + } return 'Cake Wallet'; } diff --git a/model_generator.sh b/model_generator.sh index 730817c24f..bed02a8e27 100755 --- a/model_generator.sh +++ b/model_generator.sh @@ -6,6 +6,7 @@ cd cw_evm; flutter pub get; dart run build_runner build --delete-conflicting-out cd cw_monero; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .. cd cw_bitcoin; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .. cd cw_haven; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .. +cd cw_salvium; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .. cd cw_nano; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .. cd cw_bitcoin_cash; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .. cd cw_solana; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .. diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 221f1d9bf3..9be9c1f900 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -65,12 +65,12 @@ dependencies: ref: master permission_handler: ^10.0.0 device_display_brightness: - git: - url: https://github.com/MrCyjaneK/device_display_brightness.git - ref: 4cac18c446ce686f3d75b1565badbd7da439bbd9 + git: + url: https://github.com/MrCyjaneK/device_display_brightness.git + ref: 4cac18c446ce686f3d75b1565badbd7da439bbd9 workmanager: ^0.5.2 wakelock_plus: ^1.2.5 - flutter_mailer: + flutter_mailer: git: url: https://github.com/taljacobson/flutter_mailer ref: 2a7d04d61f56e1ca166ab42e91e0daf1bfddfaf2 @@ -98,10 +98,10 @@ dependencies: url: https://github.com/cake-tech/ens_dart.git ref: main fluttertoast: 8.2.8 -# tor: -# git: -# url: https://github.com/cake-tech/tor.git -# ref: main + # tor: + # git: + # url: https://github.com/cake-tech/tor.git + # ref: main socks5_proxy: ^1.0.4 flutter_svg: ^2.0.9 polyseed: ^0.0.6 @@ -164,6 +164,7 @@ flutter: - assets/images/hardware_wallet/ - assets/node_list.yml - assets/haven_node_list.yml + - assets/salvium_node_list.yml - assets/bitcoin_electrum_server_list.yml - assets/litecoin_electrum_server_list.yml - assets/ethereum_server_list.yml diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index 24f1f5a510..9a36a0f6d1 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -10,8 +10,9 @@ APP_ANDROID_SCHEME="" MONERO_COM="monero.com" CAKEWALLET="cakewallet" HAVEN="haven" +SALVIUM="salvium" -TYPES=($MONERO_COM $CAKEWALLET $HAVEN) +TYPES=($MONERO_COM $CAKEWALLET $HAVEN $SALVIUM) APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" @@ -34,6 +35,12 @@ HAVEN_BUILD_NUMBER=1 HAVEN_BUNDLE_ID="com.cakewallet.haven" HAVEN_PACKAGE="com.cakewallet.haven" +SALVIUM_NAME="Salvium" +SALVIUM_VERSION="0.6.4" +SALVIUM_BUILD_NUMBER=1 +SALVIUM_BUNDLE_ID="com.cakewallet.salvium" +SALVIUM_PACKAGE="com.cakewallet.salvium" + if ! [[ " ${TYPES[*]} " =~ " ${APP_ANDROID_TYPE} " ]]; then echo "Wrong app type." return 1 2>/dev/null @@ -64,6 +71,13 @@ case $APP_ANDROID_TYPE in APP_ANDROID_BUNDLE_ID=$HAVEN_BUNDLE_ID APP_ANDROID_PACKAGE=$HAVEN_PACKAGE ;; + $SALVIUM) + APP_ANDROID_NAME=$SALVIUM_NAME + APP_ANDROID_VERSION=$SALVIUM_VERSION + APP_ANDROID_BUILD_NUMBER=$SALVIUM_BUILD_NUMBER + APP_ANDROID_BUNDLE_ID=$SALVIUM_BUNDLE_ID + APP_ANDROID_PACKAGE=$SALVIUM_PACKAGE + ;; esac export APP_ANDROID_TYPE diff --git a/scripts/android/app_icon.sh b/scripts/android/app_icon.sh index 9db01f2272..8b1f5b9809 100755 --- a/scripts/android/app_icon.sh +++ b/scripts/android/app_icon.sh @@ -28,6 +28,11 @@ case $APP_ANDROID_TYPE in ANDROID_ICON=$CAKEWALLET_PATH ANDROID_ICON_SET=$CAKEWALLET_ICON_SET_PATH ;; + "salvium") + APP_LOGO=$ASSETS_DIR/images/salvium_logo.png + ANDROID_ICON=$CAKEWALLET_PATH + ANDROID_ICON_SET=$CAKEWALLET_ICON_SET_PATH + ;; esac rm $APP_LOGO_DEST_PATH diff --git a/scripts/android/build_all.sh b/scripts/android/build_all.sh index ad4ec984be..06e2d34dc3 100755 --- a/scripts/android/build_all.sh +++ b/scripts/android/build_all.sh @@ -11,6 +11,8 @@ case $APP_ANDROID_TYPE in "monero.com") $DIR/build_monero_all.sh ;; "cakewallet") $DIR/build_monero_all.sh $DIR/build_haven_all.sh + $DIR/build_salvium_all.sh $DIR/build_mwebd.sh ;; "haven") $DIR/build_haven_all.sh ;; + "salvium") $DIR/build_salvium_all.sh ;; esac diff --git a/scripts/android/build_salvium.sh b/scripts/android/build_salvium.sh new file mode 100644 index 0000000000..e8dda58dd0 --- /dev/null +++ b/scripts/android/build_salvium.sh @@ -0,0 +1,70 @@ +#!/bin/sh + +. ./config.sh +SALVIUM_VERSION=tags/v0.6.4 +SALVIUM_SRC_DIR=${WORKDIR}/salvium + +git clone https://github.com/salvium/salvium.git ${SALVIUM_SRC_DIR} +cd $SALVIUM_SRC_DIR +git checkout ${SALVIUM_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/salvium +DEST_INCLUDE_DIR=${PREFIX}/include/salvium +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 $SALVIUM_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/android/build_salvium_all.sh b/scripts/android/build_salvium_all.sh new file mode 100644 index 0000000000..72f3563556 --- /dev/null +++ b/scripts/android/build_salvium_all.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +./build_iconv.sh +./build_boost.sh +./build_openssl.sh +./build_sodium.sh +./build_zmq.sh +./build_salvium.sh diff --git a/scripts/android/copy_monero_deps.sh b/scripts/android/copy_monero_deps.sh index ed8181e6be..8338955de2 100755 --- a/scripts/android/copy_monero_deps.sh +++ b/scripts/android/copy_monero_deps.sh @@ -4,6 +4,7 @@ WORKDIR=/opt/android CW_DIR=${WORKDIR}/cake_wallet CW_EXRTERNAL_DIR=${CW_DIR}/cw_shared_external/ios/External/android CW_HAVEN_EXTERNAL_DIR=${CW_DIR}/cw_haven/ios/External/android +CW_SALVIUM_EXTERNAL_DIR=${CW_DIR}/cw_salvium/ios/External/android CW_MONERO_EXTERNAL_DIR=${CW_DIR}/cw_monero/ios/External/android for arch in "aarch" "aarch64" "i686" "x86_64" do @@ -39,6 +40,8 @@ fi done mkdir -p ${CW_HAVEN_EXTERNAL_DIR}/include +mkdir -p ${CW_SALVIUM_EXTERNAL_DIR}/include mkdir -p ${CW_MONERO_EXTERNAL_DIR}/include cp $CW_EXRTERNAL_DIR/x86/include/haven/wallet2_api.h ${CW_HAVEN_EXTERNAL_DIR}/include +cp $CW_EXRTERNAL_DIR/x86/include/salvium/wallet2_api.h ${CW_SALVIUM_EXTERNAL_DIR}/include \ No newline at end of file diff --git a/scripts/android/pubspec_gen.sh b/scripts/android/pubspec_gen.sh index febc4f9e9e..4ae4ee3877 100755 --- a/scripts/android/pubspec_gen.sh +++ b/scripts/android/pubspec_gen.sh @@ -10,7 +10,7 @@ case $APP_ANDROID_TYPE in CONFIG_ARGS="--monero" ;; $CAKEWALLET) - CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero" + CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --salvium" if [ "$CW_WITH_HAVEN" = true ];then CONFIG_ARGS="$CONFIG_ARGS --haven" fi diff --git a/scripts/docker/Dockerfile b/scripts/docker/Dockerfile index a352cdc71f..a5ccf66312 100755 --- a/scripts/docker/Dockerfile +++ b/scripts/docker/Dockerfile @@ -14,6 +14,8 @@ WORKDIR /opt/android/cakewallet/ # build_boost.sh # build_haven.sh # build_haven_all.sh +# build_salvium.sh +# build_salvium_all.sh # build_iconv.sh # build_monero.sh # build_openssl.sh @@ -22,6 +24,7 @@ WORKDIR /opt/android/cakewallet/ # build_zmq.sh # config.sh # copy_haven_deps.sh +# copy_salvium_deps.sh # copy_monero_deps.sh # docker-compose.yml # entrypoint.sh @@ -58,5 +61,7 @@ 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/ +COPY build_salvium.sh /opt/android/cakewallet/ +COPY copy_salvium_deps.sh /opt/android/cakewallet/ ENTRYPOINT ["./entrypoint.sh"] diff --git a/scripts/docker/build_all.sh b/scripts/docker/build_all.sh index a4163c3f43..696e9fb7ee 100755 --- a/scripts/docker/build_all.sh +++ b/scripts/docker/build_all.sh @@ -13,5 +13,7 @@ case $APP_ANDROID_TYPE in "monero.com") $DIR/build_monero_all.sh ;; "cakewallet") $DIR/build_monero_all.sh $DIR/build_haven.sh ;; + $DIR/build_salvium.sh ;; "haven") $DIR/build_haven_all.sh ;; + "salvium") $DIR/build_salvium_all.sh ;; esac diff --git a/scripts/docker/build_salvium.sh b/scripts/docker/build_salvium.sh new file mode 100644 index 0000000000..ea39df7f9c --- /dev/null +++ b/scripts/docker/build_salvium.sh @@ -0,0 +1,71 @@ +#!/bin/sh +set -x -e + +. ./config.sh +SALVIUM_VERSION=tags/v0.6.4 +SALVIUM_SRC_DIR=${WORKDIR}/salvium + +git clone https://github.com/salvium/salvium.git ${SALVIUM_SRC_DIR} +cd $SALVIUM_SRC_DIR +git checkout ${SALVIUM_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/salvium +DEST_INCLUDE_DIR=${PREFIX}/include/salvium +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 $SALVIUM_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_salvium_all.sh b/scripts/docker/build_salvium_all.sh new file mode 100644 index 0000000000..1eb9b0b5bf --- /dev/null +++ b/scripts/docker/build_salvium_all.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -x -e + +./build_iconv.sh +./build_boost.sh +./build_openssl.sh +./build_sodium.sh +./build_zmq.sh +./build_salvium.sh diff --git a/scripts/docker/copy_salvium_deps.sh b/scripts/docker/copy_salvium_deps.sh new file mode 100644 index 0000000000..54b0bafbf9 --- /dev/null +++ b/scripts/docker/copy_salvium_deps.sh @@ -0,0 +1,46 @@ +#!/bin/bash +set -x + +WORKDIR=/opt/android +CW_DIR=${WORKDIR}/cake_wallet +CW_EXRTERNAL_DIR=${CW_DIR}/cw_shared_external/ios/External/android +CW_SALVIUM_EXTERNAL_DIR=${CW_DIR}/cw_salvium/ios/External/android +CW_MONERO_EXTERNAL_DIR=${CW_DIR}/cw_monero/ios/External/android +for arch in "aarch" "aarch64" "i686" "x86_64" +do + +PREFIX=${WORKDIR}/prefix_${arch} +ABI="" + +case $arch in + "aarch" ) + ABI="armeabi-v7a";; + "aarch64" ) + ABI="arm64-v8a";; + "i686" ) + ABI="x86";; + "x86_64" ) + ABI="x86_64";; +esac + +LIB_DIR=${CW_EXRTERNAL_DIR}/${ABI}/lib +INCLUDE_DIR=${CW_EXRTERNAL_DIR}/${ABI}/include +LIBANBOUND_PATH=${PREFIX}/lib/libunbound.a + +mkdir -p $LIB_DIR +mkdir -p $INCLUDE_DIR + +cp -r ${PREFIX}/lib/* $LIB_DIR +cp -r ${PREFIX}/include/* $INCLUDE_DIR + +if [ -f "$LIBANBOUND_PATH" ]; then + cp $LIBANBOUND_PATH ${LIB_DIR}/monero +fi + +done + +mkdir -p ${CW_SALVIUM_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/salvium/wallet2_api.h ${CW_SALVIUM_EXTERNAL_DIR}/include diff --git a/scripts/docker/entrypoint.sh b/scripts/docker/entrypoint.sh index 14f02a1f8f..6fb3fef196 100755 --- a/scripts/docker/entrypoint.sh +++ b/scripts/docker/entrypoint.sh @@ -3,9 +3,11 @@ set -x -e ls /opt/android -rm -rf monero haven +rm -rf monero haven salvium ./build_monero.sh ./build_haven.sh +./build_salvium.sh ./copy_monero_deps.sh ./copy_haven_deps.sh +./copy_salvium_deps.sh diff --git a/scripts/ios/app_config.sh b/scripts/ios/app_config.sh index 396ccd7f0c..074d59dd73 100755 --- a/scripts/ios/app_config.sh +++ b/scripts/ios/app_config.sh @@ -32,7 +32,7 @@ case $APP_IOS_TYPE in CONFIG_ARGS="--monero" ;; $CAKEWALLET) - CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero" + CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --salvium" if [ "$CW_WITH_HAVEN" = true ];then CONFIG_ARGS="$CONFIG_ARGS --haven" fi diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index 816ddd29aa..c8730b90b9 100644 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -8,8 +8,9 @@ APP_IOS_BUNDLE_ID="" MONERO_COM="monero.com" CAKEWALLET="cakewallet" HAVEN="haven" +SALVIUM="salvium" -TYPES=($MONERO_COM $CAKEWALLET $HAVEN) +TYPES=($MONERO_COM $CAKEWALLET $HAVEN $SALVIUM) APP_IOS_TYPE=$1 MONERO_COM_NAME="Monero.com" @@ -27,6 +28,11 @@ HAVEN_VERSION="1.0.0" HAVEN_BUILD_NUMBER=3 HAVEN_BUNDLE_ID="com.cakewallet.haven" +SALVIUM_NAME="Salvium" +SALVIUM_VERSION="0.6.4" +SALVIUM_BUILD_NUMBER=1 +SALVIUM_BUNDLE_ID="com.cakewallet.salvium" + if ! [[ " ${TYPES[*]} " =~ " ${APP_IOS_TYPE} " ]]; then echo "Wrong app type." exit 1 @@ -51,6 +57,12 @@ case $APP_IOS_TYPE in APP_IOS_BUILD_NUMBER=$HAVEN_BUILD_NUMBER APP_IOS_BUNDLE_ID=$HAVEN_BUNDLE_ID ;; + $SALVIUM) + APP_IOS_NAME=$SALVIUM_NAME + APP_IOS_VERSION=$SALVIUM_VERSION + APP_IOS_BUILD_NUMBER=$SALVIUM_BUILD_NUMBER + APP_IOS_BUNDLE_ID=$SALVIUM_BUNDLE_ID + ;; esac export APP_IOS_TYPE diff --git a/scripts/ios/app_icon.sh b/scripts/ios/app_icon.sh index 3914c3756f..2e5e63483f 100755 --- a/scripts/ios/app_icon.sh +++ b/scripts/ios/app_icon.sh @@ -18,6 +18,10 @@ case $APP_IOS_TYPE in ICON_120_PATH=`pwd`/../../assets/images/cakewallet_icon_120.png ICON_180_PATH=`pwd`/../../assets/images/cakewallet_icon_180.png ICON_1024_PATH=`pwd`/../../assets/images/cakewallet_icon_1024.png;; + "salvium") + ICON_120_PATH=`pwd`/../../assets/images/cakewallet_icon_120.png + ICON_180_PATH=`pwd`/../../assets/images/cakewallet_icon_180.png + ICON_1024_PATH=`pwd`/../../assets/images/cakewallet_icon_1024.png;; esac rm $DEST_DIR_PATH/app_icon_120.png diff --git a/scripts/ios/build_all.sh b/scripts/ios/build_all.sh index ba5c55a1f5..d0186e32dc 100755 --- a/scripts/ios/build_all.sh +++ b/scripts/ios/build_all.sh @@ -11,4 +11,5 @@ case $APP_IOS_TYPE in "monero.com") $DIR/build_monero_all.sh ;; "cakewallet") $DIR/build_monero_all.sh && $DIR/build_haven.sh && $DIR/build_mwebd.sh ;; "haven") $DIR/build_haven_all.sh ;; + "salvium") $DIR/build_salvium_all.sh ;; esac diff --git a/scripts/ios/build_salvium.sh b/scripts/ios/build_salvium.sh new file mode 100644 index 0000000000..38356df057 --- /dev/null +++ b/scripts/ios/build_salvium.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +. ./config.sh + +SALVIUM_URL="https://github.com/salvium/salvium.git" +SALVIUM_DIR_PATH="${EXTERNAL_IOS_SOURCE_DIR}/salvium" +SALVIUM_VERSION=tags/v0.6.4 +BUILD_TYPE=release +PREFIX=${EXTERNAL_IOS_DIR} +DEST_LIB_DIR=${EXTERNAL_IOS_LIB_DIR}/salvium +DEST_INCLUDE_DIR=${EXTERNAL_IOS_INCLUDE_DIR}/salvium + +echo "Cloning salvium from - $SALVIUM_URL to - $SALVIUM_DIR_PATH" +git clone $SALVIUM_URL $SALVIUM_DIR_PATH +cd $SALVIUM_DIR_PATH +git checkout $SALVIUM_VERSION +git submodule update --init --force +mkdir -p build +cd .. + +ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +if [ -z $INSTALL_PREFIX ]; then + INSTALL_PREFIX=${ROOT_DIR}/salvium +fi + +for arch in "arm64" #"armv7" "arm64" +do + +echo "Building IOS ${arch}" +export CMAKE_INCLUDE_PATH="${PREFIX}/include" +export CMAKE_LIBRARY_PATH="${PREFIX}/lib" + +case $arch in + "armv7" ) + DEST_LIB=../../lib-armv7;; + "arm64" ) + DEST_LIB=../../lib-armv8-a;; +esac + +rm -rf salvium/build > /dev/null + +mkdir -p salvium/build/${BUILD_TYPE} +pushd salvium/build/${BUILD_TYPE} +cmake -D IOS=ON \ + -DARCH=${arch} \ + -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ + -DSTATIC=ON \ + -DBUILD_GUI_DEPS=ON \ + -DINSTALL_VENDORED_LIBUNBOUND=ON \ + -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} \ + -DUSE_DEVICE_TREZOR=OFF \ + ../.. +make -j4 && make install +cp src/cryptonote_basic/libcryptonote_basic.a ${DEST_LIB} +cp src/offshore/liboffshore.a ${DEST_LIB} +popd + +done + +#only for arm64 +mkdir -p $DEST_LIB_DIR +mkdir -p $DEST_INCLUDE_DIR +cp ${SALVIUM_DIR_PATH}/lib-armv8-a/* $DEST_LIB_DIR +cp ${SALVIUM_DIR_PATH}/include/wallet/api/* $DEST_INCLUDE_DIR diff --git a/scripts/ios/build_salvium_all.sh b/scripts/ios/build_salvium_all.sh new file mode 100644 index 0000000000..4e76b327fa --- /dev/null +++ b/scripts/ios/build_salvium_all.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +. ./config.sh +./install_missing_headers.sh +./build_openssl.sh +./build_boost.sh +./build_sodium.sh +./build_zmq.sh +./build_salvium.sh \ No newline at end of file diff --git a/scripts/ios/setup.sh b/scripts/ios/setup.sh index abe8435aec..3f6e4d1c1e 100755 --- a/scripts/ios/setup.sh +++ b/scripts/ios/setup.sh @@ -12,16 +12,21 @@ fi libtool -static -o libboost.a ./libboost_*.a libtool -static -o libhaven.a ./haven/*.a +libtool -static -o libsalvium.a ./salvium/*.a libtool -static -o libmonero.a ./monero/*.a CW_HAVEN_EXTERNAL_LIB=../../../../../cw_haven/ios/External/ios/lib -CW_HAVEN_EXTERNAL_INCLUDE=../../../../../cw_haven/ios/External/ios/include +CW_HAVEN_EXTERNAL_INCLUDE=../../../../../cw_haven/ios/External/ios/include +CW_SALVIUM_EXTERNAL_LIB=../../../../../cw_salvium/ios/External/ios/lib +CW_SALVIUM_EXTERNAL_INCLUDE=../../../../../cw_salvium/ios/External/ios/include CW_MONERO_EXTERNAL_LIB=../../../../../cw_monero/ios/External/ios/lib CW_MONERO_EXTERNAL_INCLUDE=../../../../../cw_monero/ios/External/ios/include mkdir -p $CW_HAVEN_EXTERNAL_INCLUDE +mkdir -p $CW_SALVIUM_EXTERNAL_INCLUDE mkdir -p $CW_MONERO_EXTERNAL_INCLUDE mkdir -p $CW_HAVEN_EXTERNAL_LIB +mkdir -p $CW_SALVIUM_EXTERNAL_LIB mkdir -p $CW_MONERO_EXTERNAL_LIB ln ./libboost.a ${CW_HAVEN_EXTERNAL_LIB}/libboost.a @@ -31,6 +36,13 @@ ln ./libsodium.a ${CW_HAVEN_EXTERNAL_LIB}/libsodium.a cp ./libhaven.a $CW_HAVEN_EXTERNAL_LIB cp ../include/haven/* $CW_HAVEN_EXTERNAL_INCLUDE +ln ./libboost.a ${CW_SALVIUM_EXTERNAL_LIB}/libboost.a +ln ./libcrypto.a ${CW_SALVIUM_EXTERNAL_LIB}/libcrypto.a +ln ./libssl.a ${CW_SALVIUM_EXTERNAL_LIB}/libssl.a +ln ./libsodium.a ${CW_SALVIUM_EXTERNAL_LIB}/libsodium.a +cp ./libsalvium.a $CW_SALVIUM_EXTERNAL_LIB +cp ../include/salvium/* $CW_SALVIUM_EXTERNAL_INCLUDE + ln ./libboost.a ${CW_MONERO_EXTERNAL_LIB}/libboost.a ln ./libcrypto.a ${CW_MONERO_EXTERNAL_LIB}/libcrypto.a ln ./libssl.a ${CW_MONERO_EXTERNAL_LIB}/libssl.a diff --git a/scripts/macos/app_config.sh b/scripts/macos/app_config.sh index 452205dd97..4bf7bcd55f 100755 --- a/scripts/macos/app_config.sh +++ b/scripts/macos/app_config.sh @@ -36,7 +36,7 @@ case $APP_MACOS_TYPE in $MONERO_COM) CONFIG_ARGS="--monero";; $CAKEWALLET) - CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero";; #--haven + CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --salvium";; #--haven esac cp -rf pubspec_description.yaml pubspec.yaml diff --git a/scripts/macos/build_salvium.sh b/scripts/macos/build_salvium.sh new file mode 100644 index 0000000000..23e93d038a --- /dev/null +++ b/scripts/macos/build_salvium.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +. ./config.sh + +SALVIUM_URL="https://github.com/salvium/salvium.git" +SALVIUM_DIR_PATH="${EXTERNAL_MACOS_SOURCE_DIR}/salvium" +SALVIUM_VERSION=tags/v0.6.4 +BUILD_TYPE=release +PREFIX=${EXTERNAL_MACOS_DIR} +DEST_LIB_DIR=${EXTERNAL_MACOS_LIB_DIR}/salvium +DEST_INCLUDE_DIR=${EXTERNAL_MACOS_INCLUDE_DIR}/salvium +ARCH=`uname -m` + +echo "Cloning salvium from - $SALVIUM_URL to - $SALVIUM_DIR_PATH" +git clone $SALVIUM_URL $SALVIUM_DIR_PATH +cd $SALVIUM_DIR_PATH +git checkout $SALVIUM_VERSION +git submodule update --init --force +mkdir -p build +cd .. + +ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +if [ -z $INSTALL_PREFIX ]; then + INSTALL_PREFIX=${ROOT_DIR}/salvium +fi + +mkdir -p $DEST_LIB_DIR +mkdir -p $DEST_INCLUDE_DIR + +echo "Building MACOS ${ARCH}" +export CMAKE_INCLUDE_PATH="${PREFIX}/include" +export CMAKE_LIBRARY_PATH="${PREFIX}/lib" +rm -rf salvium/build > /dev/null + +mkdir -p salvium/build/${BUILD_TYPE} +pushd salvium/build/${BUILD_TYPE} +cmake -DARCH=${ARCH} \ + -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ + -DSTATIC=ON \ + -DBUILD_GUI_DEPS=ON \ + -DINSTALL_VENDORED_LIBUNBOUND=ON \ + -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} \ + -DUSE_DEVICE_TREZOR=OFF \ + ../.. +make -j4 && make install +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 +popd + diff --git a/scripts/macos/setup.sh b/scripts/macos/setup.sh index fea2a6c8d8..553adacbeb 100755 --- a/scripts/macos/setup.sh +++ b/scripts/macos/setup.sh @@ -11,17 +11,29 @@ cd $EXTERNAL_MACOS_LIB_DIR # cp $LIBRANDOMX_PATH ./haven # fi +# if [ -f "$LIBRANDOMX_PATH" ]; then +# cp $LIBRANDOMX_PATH ./salvium +# fi + libtool -static -o libboost.a ./libboost_*.a libtool -static -o libmonero.a ./monero/*.a # CW_HAVEN_EXTERNAL_LIB=../../../../../cw_haven/macos/External/macos/lib # CW_HAVEN_EXTERNAL_INCLUDE=../../../../../cw_haven/macos/External/macos/include + +# CW_SALVIUM_EXTERNAL_LIB=../../../../../cw_salvium/macos/External/macos/lib +# CW_SALVIUM_EXTERNAL_INCLUDE=../../../../../cw_salvium/macos/External/macos/include + CW_MONERO_EXTERNAL_LIB=../../../../../cw_monero/macos/External/macos/lib CW_MONERO_EXTERNAL_INCLUDE=../../../../../cw_monero/macos/External/macos/include # mkdir -p $CW_HAVEN_EXTERNAL_INCLUDE -mkdir -p $CW_MONERO_EXTERNAL_INCLUDE # mkdir -p $CW_HAVEN_EXTERNAL_LIB + +# mkdir -p $CW_SALVIUM_EXTERNAL_INCLUDE +# mkdir -p $CW_SALVIUM_EXTERNAL_LIB + +mkdir -p $CW_MONERO_EXTERNAL_INCLUDE mkdir -p $CW_MONERO_EXTERNAL_LIB # ln ./libboost.a ${CW_HAVEN_EXTERNAL_LIB}/libboost.a @@ -31,6 +43,13 @@ mkdir -p $CW_MONERO_EXTERNAL_LIB # cp ./libhaven.a $CW_HAVEN_EXTERNAL_LIB # cp ../include/haven/* $CW_HAVEN_EXTERNAL_INCLUDE +# ln ./libboost.a ${CW_SALVIUM_EXTERNAL_LIB}/libboost.a +# ln ./libcrypto.a ${CW_SALVIUM_EXTERNAL_LIB}/libcrypto.a +# ln ./libssl.a ${CW_SALVIUM_EXTERNAL_LIB}/libssl.a +# ln ./libsodium.a ${CW_SALVIUM_EXTERNAL_LIB}/libsodium.a +# cp ./libsalvium.a $CW_SALVIUM_EXTERNAL_LIB +# cp ../include/salvium/* $CW_SALVIUM_EXTERNAL_INCLUDE + ln ./libboost.a ${CW_MONERO_EXTERNAL_LIB}/libboost.a ln ./libcrypto.a ${CW_MONERO_EXTERNAL_LIB}/libcrypto.a ln ./libssl.a ${CW_MONERO_EXTERNAL_LIB}/libssl.a diff --git a/tool/configure.dart b/tool/configure.dart index 7199b8a821..3e7ca2153d 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -3,6 +3,7 @@ import 'dart:io'; const bitcoinOutputPath = 'lib/bitcoin/bitcoin.dart'; const moneroOutputPath = 'lib/monero/monero.dart'; const havenOutputPath = 'lib/haven/haven.dart'; +const salviumOutputPath = 'lib/salvium/salvium.dart'; const ethereumOutputPath = 'lib/ethereum/ethereum.dart'; const bitcoinCashOutputPath = 'lib/bitcoin_cash/bitcoin_cash.dart'; const nanoOutputPath = 'lib/nano/nano.dart'; @@ -20,6 +21,7 @@ Future main(List args) async { final hasBitcoin = args.contains('${prefix}bitcoin'); final hasMonero = args.contains('${prefix}monero'); final hasHaven = args.contains('${prefix}haven'); + final hasSalvium = args.contains('${prefix}salvium'); final hasEthereum = args.contains('${prefix}ethereum'); final hasBitcoinCash = args.contains('${prefix}bitcoinCash'); final hasNano = args.contains('${prefix}nano'); @@ -33,6 +35,7 @@ Future main(List args) async { await generateBitcoin(hasBitcoin); await generateMonero(hasMonero); await generateHaven(hasHaven); + await generateSalvium(hasSalvium); await generateEthereum(hasEthereum); await generateBitcoinCash(hasBitcoinCash); await generateNano(hasNano); @@ -46,6 +49,7 @@ Future main(List args) async { hasMonero: hasMonero, hasBitcoin: hasBitcoin, hasHaven: hasHaven, + hasSalvium: hasSalvium, hasEthereum: hasEthereum, hasNano: hasNano, hasBanano: hasBanano, @@ -60,6 +64,7 @@ Future main(List args) async { hasMonero: hasMonero, hasBitcoin: hasBitcoin, hasHaven: hasHaven, + hasSalvium: hasSalvium, hasEthereum: hasEthereum, hasNano: hasNano, hasBanano: hasBanano, @@ -819,6 +824,185 @@ abstract class HavenAccountList { await outputFile.writeAsString(output); } +Future generateSalvium(bool hasImplementation) async { + final outputFile = File(salviumOutputPath); + const salviumCommonHeaders = """ +import 'package:mobx/mobx.dart'; +import 'package:flutter/foundation.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:cw_core/crypto_currency.dart';"""; + const salviumCWHeaders = """ +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_salvium/salvium_wallet_service.dart'; +import 'package:cw_salvium/salvium_wallet.dart'; +import 'package:cw_salvium/salvium_transaction_info.dart'; +import 'package:cw_salvium/salvium_transaction_history.dart'; +import 'package:cw_core/account.dart' as monero_account; +import 'package:cw_salvium/api/wallet.dart' as monero_wallet_api; +import 'package:cw_salvium/mnemonics/english.dart'; +import 'package:cw_salvium/mnemonics/chinese_simplified.dart'; +import 'package:cw_salvium/mnemonics/dutch.dart'; +import 'package:cw_salvium/mnemonics/german.dart'; +import 'package:cw_salvium/mnemonics/japanese.dart'; +import 'package:cw_salvium/mnemonics/russian.dart'; +import 'package:cw_salvium/mnemonics/spanish.dart'; +import 'package:cw_salvium/mnemonics/portuguese.dart'; +import 'package:cw_salvium/mnemonics/french.dart'; +import 'package:cw_salvium/mnemonics/italian.dart'; +import 'package:cw_salvium/salvium_transaction_creation_credentials.dart'; +import 'package:cw_salvium/api/balance_list.dart'; +"""; + const salviumCwPart = "part 'cw_salvium.dart';"; + const salviumContent = """ +class Account { + Account({required this.id, required this.label}); + final int id; + final String label; +} + +class Subaddress { + Subaddress({ + required this.id, + required this.label, + required this.address}); + final int id; + final String label; + final String address; +} + +class SalviumBalance extends Balance { + SalviumBalance({required this.fullBalance, required this.unlockedBalance}) + : formattedFullBalance = salvium!.formatterMoneroAmountToString(amount: fullBalance), + formattedUnlockedBalance = + salvium!.formatterMoneroAmountToString(amount: unlockedBalance), + super(unlockedBalance, fullBalance); + + SalviumBalance.fromString( + {required this.formattedFullBalance, + required this.formattedUnlockedBalance}) + : fullBalance = salvium!.formatterMoneroParseAmount(amount: formattedFullBalance), + unlockedBalance = salvium!.formatterMoneroParseAmount(amount: formattedUnlockedBalance), + super(salvium!.formatterMoneroParseAmount(amount: formattedUnlockedBalance), + salvium!.formatterMoneroParseAmount(amount: formattedFullBalance)); + + final int fullBalance; + final int unlockedBalance; + final String formattedFullBalance; + final String formattedUnlockedBalance; + + @override + String get formattedAvailableBalance => formattedUnlockedBalance; + + @override + String get formattedAdditionalBalance => formattedFullBalance; +} + +class AssetRate { + AssetRate(this.asset, this.rate); + + final String asset; + final int rate; +} + +abstract class SalviumWalletDetails { + // FIX-ME: it's abstract class + @observable + late Account account; + // FIX-ME: it's abstract class + @observable + late SalviumBalance balance; +} + +abstract class Salvium { + SalviumAccountList getAccountList(Object wallet); + + MoneroSubaddressList getSubaddressList(Object wallet); + + TransactionHistoryBase getTransactionHistory(Object wallet); + + SalviumWalletDetails getMoneroWalletDetails(Object wallet); + + String getTransactionAddress(Object wallet, int accountIndex, int addressIndex); + + int getHeightByDate({required DateTime date}); + Future getCurrentHeight(); + TransactionPriority getDefaultTransactionPriority(); + TransactionPriority deserializeMoneroTransactionPriority({required int raw}); + List getTransactionPriorities(); + List getMoneroWordList(String language); + + WalletCredentials createSalviumRestoreWalletFromKeysCredentials({ + required String name, + required String spendKey, + required String viewKey, + required String address, + required String password, + required String language, + required int height}); + WalletCredentials createSalviumRestoreWalletFromSeedCredentials({required String name, required String password, required int height, required String mnemonic}); + WalletCredentials createSalviumNewWalletCredentials({required String name, required String language, String? password}); + Map getKeys(Object wallet); + Object createSalviumTransactionCreationCredentials({required List outputs, required TransactionPriority priority, required String assetType}); + String formatterMoneroAmountToString({required int amount}); + double formatterMoneroAmountToDouble({required int amount}); + int formatterMoneroParseAmount({required String amount}); + Account getCurrentAccount(Object wallet); + void setCurrentAccount(Object wallet, int id, String label); + void onStartup(); + int getTransactionInfoAccountId(TransactionInfo tx); + WalletService createSalviumWalletService(Box walletInfoSource); + CryptoCurrency assetOfTransaction(TransactionInfo tx); + List getAssetRate(); +} + +abstract class MoneroSubaddressList { + 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 SalviumAccountList { + 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 salviumEmptyDefinition = 'Salvium? salvium;\n'; + const salviumCWDefinition = 'Salvium? salvium = CWSalvium();\n'; + + final output = '$salviumCommonHeaders\n' + + (hasImplementation ? '$salviumCWHeaders\n' : '\n') + + (hasImplementation ? '$salviumCwPart\n\n' : '\n') + + (hasImplementation ? salviumCWDefinition : salviumEmptyDefinition) + + '\n' + + salviumContent; + + if (outputFile.existsSync()) { + await outputFile.delete(); + } + + await outputFile.writeAsString(output); +} + Future generateEthereum(bool hasImplementation) async { final outputFile = File(ethereumOutputPath); const ethereumCommonHeaders = """ From e0c55fc7b1083d97da4327db8f8d37d68ff33b18 Mon Sep 17 00:00:00 2001 From: 0xskydb Date: Tue, 10 Dec 2024 12:54:13 -0500 Subject: [PATCH 2/4] update salvium params --- README.md | 2 +- assets/images/salvium_logo.png | Bin 0 -> 29467 bytes assets/images/salvium_menu.png.png | Bin 0 -> 17258 bytes assets/salvium_node_list.yml | 2 +- cw_core/lib/crypto_currency.dart | 4 +- cw_core/lib/currency_for_wallet_type.dart | 4 +- cw_core/lib/get_height_by_date.dart | 7 +- cw_core/lib/wallet_info.dart | 2 +- cw_core/lib/wallet_type.dart | 2 +- cw_shared_external/README.md | 2 +- .../ios/cw_shared_external.podspec | 4 +- cw_shared_external/pubspec.yaml | 2 +- lib/entities/default_settings_migration.dart | 8 +- lib/entities/parse_address_from_domain.dart | 2 +- lib/src/screens/dashboard/dashboard_page.dart | 21 --- .../dashboard/pages/cake_features_page.dart | 1 - .../new_wallet/new_wallet_type_page.dart | 2 +- .../widgets/salvium_wallet_removal_popup.dart | 91 ------------- lib/store/settings_store.dart | 5 +- .../dashboard/dashboard_view_model.dart | 14 +- .../dashboard/receive_option_view_model.dart | 3 - .../dashboard/transaction_list_item.dart | 69 ++++++---- .../exchange/exchange_view_model.dart | 3 +- lib/view_model/send/output.dart | 12 +- .../send/send_template_view_model.dart | 1 - lib/view_model/send/send_view_model.dart | 4 +- .../transaction_details_view_model.dart | 4 +- macos/Podfile.lock | 2 +- macos/Runner.xcodeproj/project.pbxproj | 24 ++-- scripts/android/pubspec_gen.sh | 4 + scripts/ios/app_config.sh | 6 +- scripts/ios/build_all.sh | 2 +- tool/configure.dart | 121 ++++++++++++------ 33 files changed, 180 insertions(+), 250 deletions(-) create mode 100644 assets/images/salvium_logo.png create mode 100644 assets/images/salvium_menu.png.png delete mode 100644 lib/src/widgets/salvium_wallet_removal_popup.dart diff --git a/README.md b/README.md index 1b5f11ec6f..fe548966ad 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Cake Wallet includes support for several cryptocurrencies, including: - Solana (SOL) - Nano (XNO) - Haven (XHV) -- Salvium (XRM) +- Salvium (SAL) ## Features diff --git a/assets/images/salvium_logo.png b/assets/images/salvium_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..96bb225576b4478a34e6d8d1f3e0aa3d633e269a GIT binary patch literal 29467 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 literal 0 HcmV?d00001 diff --git a/assets/images/salvium_menu.png.png b/assets/images/salvium_menu.png.png new file mode 100644 index 0000000000000000000000000000000000000000..3d9c2b3c90e8350c570fb8ca6e81249defec59c4 GIT binary patch literal 17258 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 HvylG-Nmsv$ literal 0 HcmV?d00001 diff --git a/assets/salvium_node_list.yml b/assets/salvium_node_list.yml index 8a5d47a9b0..2d2c414f62 100644 --- a/assets/salvium_node_list.yml +++ b/assets/salvium_node_list.yml @@ -1,3 +1,3 @@ -- uri: nodes.salvium.org:443 +- uri: seed01.salvium.io:19081 useSSL: true is_default: true diff --git a/cw_core/lib/crypto_currency.dart b/cw_core/lib/crypto_currency.dart index ad6bc31a17..b6e6a90798 100644 --- a/cw_core/lib/crypto_currency.dart +++ b/cw_core/lib/crypto_currency.dart @@ -107,7 +107,7 @@ class CryptoCurrency extends EnumerableItem with Serializable implemen CryptoCurrency.tbtc, CryptoCurrency.wow, CryptoCurrency.ton, - CryptoCurrency.salvium, + CryptoCurrency.sal, ]; static const havenCurrencies = [ @@ -227,7 +227,7 @@ class CryptoCurrency extends EnumerableItem with Serializable implemen static const wow = CryptoCurrency(title: 'WOW', fullName: 'Wownero', raw: 94, name: 'wow', iconPath: 'assets/images/wownero_icon.png', decimals: 11); static const ton = CryptoCurrency(title: 'TON', fullName: 'Toncoin', raw: 95, name: 'ton', iconPath: 'assets/images/ton_icon.png', decimals: 8); - static const salvium = CryptoCurrency(title: 'SAL', fullName: 'Salvium', raw: 96, name: 'sal', iconPath: 'assets/images/sal_logo.png', decimals: 12); + static const sal = CryptoCurrency(title: 'SAL', fullName: 'Salvium', raw: 96, name: 'sal', iconPath: 'assets/images/salvium_logo.png', decimals: 8); 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 fb5d753ea5..c1cd9523c0 100644 --- a/cw_core/lib/currency_for_wallet_type.dart +++ b/cw_core/lib/currency_for_wallet_type.dart @@ -15,7 +15,7 @@ CryptoCurrency currencyForWalletType(WalletType type, {bool? isTestnet}) { case WalletType.haven: return CryptoCurrency.xhv; case WalletType.salvium: - return CryptoCurrency.salvium; + return CryptoCurrency.sal; case WalletType.ethereum: return CryptoCurrency.eth; case WalletType.bitcoinCash: @@ -48,7 +48,7 @@ WalletType? walletTypeForCurrency(CryptoCurrency currency) { return WalletType.litecoin; case CryptoCurrency.xhv: return WalletType.haven; - case CryptoCurrency.salvium: + case CryptoCurrency.sal: return WalletType.salvium; case CryptoCurrency.eth: return WalletType.ethereum; diff --git a/cw_core/lib/get_height_by_date.dart b/cw_core/lib/get_height_by_date.dart index c6ca028eb3..7e249a3f84 100644 --- a/cw_core/lib/get_height_by_date.dart +++ b/cw_core/lib/get_height_by_date.dart @@ -245,7 +245,10 @@ Future getHavenCurrentHeight() async { } const salviumDates = { - + "2024-11": 86752 + "2024-10": 65675 + "2024-09": 44680 + "2024-08": 22339 }; int getSalviumHeightByDate({required DateTime date}) { @@ -256,7 +259,7 @@ int getSalviumHeightByDate({required DateTime date}) { } Future getSalviumCurrentHeight() async { - final response = await http.get(Uri.parse('https://explorer.salvium.org/api/networkinfo')); + final response = await http.get(Uri.parse('https://explorer.salvium.io/api/networkinfo')); if (response.statusCode == 200) { final info = jsonDecode(response.body); diff --git a/cw_core/lib/wallet_info.dart b/cw_core/lib/wallet_info.dart index 375163e5c8..bd035e30a7 100644 --- a/cw_core/lib/wallet_info.dart +++ b/cw_core/lib/wallet_info.dart @@ -210,7 +210,7 @@ class WalletInfo extends HiveObject { bool get isShowIntroCakePayCard { if (showIntroCakePayCard == null) { - return type != WalletType.haven && type != WalletType.salvium; + return type != WalletType.haven; } return showIntroCakePayCard!; } diff --git a/cw_core/lib/wallet_type.dart b/cw_core/lib/wallet_type.dart index 53fd64d1de..9689dd4ba3 100644 --- a/cw_core/lib/wallet_type.dart +++ b/cw_core/lib/wallet_type.dart @@ -226,7 +226,7 @@ CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = fal case WalletType.wownero: return CryptoCurrency.wow; case WalletType.salvium: - return CryptoCurrency.salvium; + return CryptoCurrency.sal; case WalletType.none: throw Exception( 'Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency'); diff --git a/cw_shared_external/README.md b/cw_shared_external/README.md index 6ab2a2cf7a..1480b797ce 100644 --- a/cw_shared_external/README.md +++ b/cw_shared_external/README.md @@ -1,6 +1,6 @@ # cw_shared_external -Part of Cake Wallet. Shared external libraries for cw_monero, cw_haven and cw_salvium. +Part of Cake Wallet. Shared external libraries for cw_monero, cw_salvium and cw_haven. Libraries: - Boost diff --git a/cw_shared_external/ios/cw_shared_external.podspec b/cw_shared_external/ios/cw_shared_external.podspec index c519aa19ed..d2d9f51a8f 100644 --- a/cw_shared_external/ios/cw_shared_external.podspec +++ b/cw_shared_external/ios/cw_shared_external.podspec @@ -5,8 +5,8 @@ Pod::Spec.new do |s| s.name = 'cw_shared_external' s.version = '0.0.1' - s.summary = 'Shared libraries for monero, haven and salvium.' - s.description = 'Shared libraries for monero, haven and salvium.' + s.summary = 'Shared libraries for monero, salvium and haven.' + s.description = 'Shared libraries for monero, salvium and haven.' s.homepage = 'http://cakewallet.com' s.license = { :file => '../LICENSE' } s.author = { 'Cake Wallet' => 'm@cakewallet.com' } diff --git a/cw_shared_external/pubspec.yaml b/cw_shared_external/pubspec.yaml index b2112b8ebb..622dc9f9e7 100644 --- a/cw_shared_external/pubspec.yaml +++ b/cw_shared_external/pubspec.yaml @@ -1,5 +1,5 @@ name: cw_shared_external -description: Shared external libraries for monero, haven and salvium +description: Shared external libraries for monero, salvium and haven version: 0.0.1 author: Cake Walelt homepage: https://cakewallet.com diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index 094bba6345..0fc2b1680f 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -33,7 +33,7 @@ const publicBitcoinTestnetElectrumUri = '$publicBitcoinTestnetElectrumAddress:$publicBitcoinTestnetElectrumPort'; const cakeWalletLitecoinElectrumUri = 'ltc-electrum.cakewallet.com:50002'; const havenDefaultNodeUri = 'nodes.havenprotocol.org:443'; -const salviumDefaultNodeUri = 'nodes.salvium.org:443'; +const salviumDefaultNodeUri = 'seed01.salvium.io:19081'; const ethereumDefaultNodeUri = 'ethereum.publicnode.com'; const polygonDefaultNodeUri = 'polygon-bor.publicnode.com'; const cakeWalletBitcoinCashDefaultNodeUri = 'bitcoincash.stackwallet.com:50002'; @@ -296,10 +296,6 @@ Future defaultSettingsMigration( await changeSalviumCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes); await checkCurrentNodes(nodes, powNodes, sharedPreferences); break; - - case 46: - await changeDefaultSalviumNode(nodes); - break; default: break; } @@ -1281,7 +1277,7 @@ Future changeDefaultHavenNode(Box nodeSource) async { } Future changeDefaultSalviumNode(Box nodeSource) async { - const previousSalviumDefaultNodeUri = 'nodes.salvium.org:443'; + const previousSalviumDefaultNodeUri = 'seed01.salvium.io:19081'; final salviumNodes = nodeSource.values.where((node) => node.uriRaw == previousSalviumDefaultNodeUri); salviumNodes.forEach((node) async { node.uriRaw = salviumDefaultNodeUri; diff --git a/lib/entities/parse_address_from_domain.dart b/lib/entities/parse_address_from_domain.dart index a1bfc78519..54fa4e75ae 100644 --- a/lib/entities/parse_address_from_domain.dart +++ b/lib/entities/parse_address_from_domain.dart @@ -215,7 +215,7 @@ class AddressResolver { } if (text.hasOnlyEmojis) { if (settingsStore.lookupsYatService) { - if (walletType != WalletType.haven && walletType != WalletType.salvium) { + if (walletType != WalletType.haven) { final addresses = await yatService.fetchYatAddress(text, ticker); return ParsedAddress.fetchEmojiAddress(addresses: addresses, name: text); } diff --git a/lib/src/screens/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index c644edc083..8c236404d6 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -8,7 +8,6 @@ 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/salvium_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'; @@ -367,8 +366,6 @@ class _DashboardPageView extends BasePage { _showHavenPopup(context); - _showSalviumPopup(context); - var needToPresentYat = false; var isInactive = false; @@ -464,22 +461,4 @@ class _DashboardPageView extends BasePage { ); } } - - void _showSalviumPopup(BuildContext context) async { - final List salviumWalletList = await dashboardViewModel.checkForSalviumWallets(); - - if (salviumWalletList.isNotEmpty) { - Future.delayed( - Duration(seconds: 1), - () { - showPopUp( - context: context, - builder: (BuildContext context) { - return SalviumWalletRemovalPopup(salviumWalletList); - }, - ); - }, - ); - } - } } diff --git a/lib/src/screens/dashboard/pages/cake_features_page.dart b/lib/src/screens/dashboard/pages/cake_features_page.dart index f912015a26..bd96fd5340 100644 --- a/lib/src/screens/dashboard/pages/cake_features_page.dart +++ b/lib/src/screens/dashboard/pages/cake_features_page.dart @@ -115,7 +115,6 @@ class CakeFeaturesPage extends StatelessWidget { switch (walletType) { case WalletType.haven: - case WalletType.salvium: showPopUp( context: context, builder: (BuildContext context) { diff --git a/lib/src/screens/new_wallet/new_wallet_type_page.dart b/lib/src/screens/new_wallet/new_wallet_type_page.dart index 5513638bc7..5500c97fca 100644 --- a/lib/src/screens/new_wallet/new_wallet_type_page.dart +++ b/lib/src/screens/new_wallet/new_wallet_type_page.dart @@ -178,7 +178,7 @@ class WalletTypeFormState extends State { Future onTypeSelected() async { if (selected == null) throw Exception('Wallet Type is not selected yet.'); - if ((selected == WalletType.haven || selected == WalletType.salvium) && widget.isCreate) { + if ((selected == WalletType.haven) && widget.isCreate) { return await showPopUp( context: context, builder: (BuildContext context) => PopUpCancellableAlertDialog( diff --git a/lib/src/widgets/salvium_wallet_removal_popup.dart b/lib/src/widgets/salvium_wallet_removal_popup.dart deleted file mode 100644 index 62950d9091..0000000000 --- a/lib/src/widgets/salvium_wallet_removal_popup.dart +++ /dev/null @@ -1,91 +0,0 @@ -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 SalviumWalletRemovalPopup extends StatelessWidget { - final List affectedWalletNames; - - const SalviumWalletRemovalPopup(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 Salvium wallets in your list. Salvium is getting removed in next release of Cake Wallet, and you currently have Salvium in the following wallets:\n\n[${affectedWalletNames.join(", ")}]\n\nPlease move your funds to other wallet, as you will lose access to your Salvium 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 ef5b19d949..c47e6f5d9a 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -25,6 +25,7 @@ import 'package:cake_wallet/entities/wallet_list_order_types.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart'; import 'package:cake_wallet/monero/monero.dart'; +import 'package:cake_wallet/salvium/salvium.dart'; import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/themes/theme_list.dart'; @@ -885,7 +886,7 @@ abstract class SettingsStoreBase with Store { raw: sharedPreferences.getInt(PreferencesKey.havenTransactionPriority)!); } if (sharedPreferences.getInt(PreferencesKey.salviumTransactionPriority) != null) { - salviumTransactionPriority = monero?.deserializeMoneroTransactionPriority( + salviumTransactionPriority = salvium?.deserializeMoneroTransactionPriority( raw: sharedPreferences.getInt(PreferencesKey.salviumTransactionPriority)!); } if (sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority) != null) { @@ -912,7 +913,7 @@ abstract class SettingsStoreBase with Store { moneroTransactionPriority ??= monero?.getDefaultTransactionPriority(); bitcoinTransactionPriority ??= bitcoin?.getMediumTransactionPriority(); havenTransactionPriority ??= monero?.getDefaultTransactionPriority(); - salviumTransactionPriority ??= monero?.getDefaultTransactionPriority(); + salviumTransactionPriority ??= salvium?.getDefaultTransactionPriority(); litecoinTransactionPriority ??= bitcoin?.getLitecoinTransactionPriorityMedium(); ethereumTransactionPriority ??= ethereum?.getDefaultTransactionPriority(); bitcoinCashTransactionPriority ??= bitcoinCash?.getDefaultTransactionPriority(); diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index fa0cae613e..eccde9e7d3 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -567,9 +567,9 @@ abstract class DashboardViewModelBase with Store { case WalletType.banano: case WalletType.tron: case WalletType.wownero: + case WalletType.salvium: return true; case WalletType.haven: - case WalletType.salvium: case WalletType.none: return false; } @@ -741,8 +741,8 @@ abstract class DashboardViewModelBase with Store { } void updateActions() { - hasExchangeAction = !isHaven || !isSalvium; - hasTradeAction = !isHaven || !isSalvium; + hasExchangeAction = !isHaven; + hasTradeAction = !isHaven; } @computed @@ -765,14 +765,6 @@ abstract class DashboardViewModelBase with Store { .toList(); } - Future> checkForSalviumWallets() async { - final walletInfoSource = await CakeHive.openBox(WalletInfo.boxName); - return walletInfoSource.values - .where((element) => element.type == WalletType.salvium) - .map((e) => e.name) - .toList(); - } - Future> checkAffectedWallets() async { try { // await load file diff --git a/lib/view_model/dashboard/receive_option_view_model.dart b/lib/view_model/dashboard/receive_option_view_model.dart index d800a98d9a..744e4c58d9 100644 --- a/lib/view_model/dashboard/receive_option_view_model.dart +++ b/lib/view_model/dashboard/receive_option_view_model.dart @@ -33,9 +33,6 @@ abstract class ReceiveOptionViewModelBase with Store { case WalletType.haven: _options = [ReceivePageOption.mainnet]; break; - case WalletType.salvium: - _options = [ReceivePageOption.mainnet]; - break; default: _options = ReceivePageOptions; } diff --git a/lib/view_model/dashboard/transaction_list_item.dart b/lib/view_model/dashboard/transaction_list_item.dart index a42b841519..d0d76e3ed0 100644 --- a/lib/view_model/dashboard/transaction_list_item.dart +++ b/lib/view_model/dashboard/transaction_list_item.dart @@ -49,7 +49,9 @@ class TransactionListItem extends ActionListItem with Keyable { balanceViewModel.wallet.type == WalletType.tron; String get formattedCryptoAmount { - return displayMode == BalanceDisplayMode.hiddenBalance ? '---' : transaction.amountFormatted(); + return displayMode == BalanceDisplayMode.hiddenBalance + ? '---' + : transaction.amountFormatted(); } String get formattedTitle { @@ -64,10 +66,6 @@ class TransactionListItem extends ActionListItem with Keyable { switch (balanceViewModel.wallet.type) { case WalletType.monero: case WalletType.haven: - if (transaction.confirmations >= 0 && transaction.confirmations < 10) { - return ' (${transaction.confirmations}/10)'; - } - break; case WalletType.salvium: if (transaction.confirmations >= 0 && transaction.confirmations < 10) { return ' (${transaction.confirmations}/10)'; @@ -79,14 +77,19 @@ class TransactionListItem extends ActionListItem with Keyable { } break; case WalletType.litecoin: - bool isPegIn = (transaction.additionalInfo["isPegIn"] as bool?) ?? false; - bool isPegOut = (transaction.additionalInfo["isPegOut"] as bool?) ?? false; - bool fromPegOut = (transaction.additionalInfo["fromPegOut"] as bool?) ?? false; + bool isPegIn = + (transaction.additionalInfo["isPegIn"] as bool?) ?? false; + bool isPegOut = + (transaction.additionalInfo["isPegOut"] as bool?) ?? false; + bool fromPegOut = + (transaction.additionalInfo["fromPegOut"] as bool?) ?? false; String str = ''; if (transaction.confirmations <= 0) { str = S.current.pending; } - if ((isPegOut || fromPegOut) && transaction.confirmations >= 0 && transaction.confirmations < 6) { + if ((isPegOut || fromPegOut) && + transaction.confirmations >= 0 && + transaction.confirmations < 6) { str = " (${transaction.confirmations}/6)"; } if (isPegIn) { @@ -127,22 +130,26 @@ class TransactionListItem extends ActionListItem with Keyable { CryptoCurrency? get assetOfTransaction { try { if (balanceViewModel.wallet.type == WalletType.ethereum) { - final asset = ethereum!.assetOfTransaction(balanceViewModel.wallet, transaction); + final asset = + ethereum!.assetOfTransaction(balanceViewModel.wallet, transaction); return asset; } if (balanceViewModel.wallet.type == WalletType.polygon) { - final asset = polygon!.assetOfTransaction(balanceViewModel.wallet, transaction); + final asset = + polygon!.assetOfTransaction(balanceViewModel.wallet, transaction); return asset; } if (balanceViewModel.wallet.type == WalletType.solana) { - final asset = solana!.assetOfTransaction(balanceViewModel.wallet, transaction); + final asset = + solana!.assetOfTransaction(balanceViewModel.wallet, transaction); return asset; } if (balanceViewModel.wallet.type == WalletType.tron) { - final asset = tron!.assetOfTransaction(balanceViewModel.wallet, transaction); + final asset = + tron!.assetOfTransaction(balanceViewModel.wallet, transaction); return asset; } } catch (e) { @@ -158,57 +165,68 @@ class TransactionListItem extends ActionListItem with Keyable { switch (balanceViewModel.wallet.type) { case WalletType.monero: amount = calculateFiatAmountRaw( - cryptoAmount: monero!.formatterMoneroAmountToDouble(amount: transaction.amount), + cryptoAmount: monero! + .formatterMoneroAmountToDouble(amount: transaction.amount), price: price); break; case WalletType.wownero: amount = calculateFiatAmountRaw( - cryptoAmount: wownero!.formatterWowneroAmountToDouble(amount: transaction.amount), + cryptoAmount: wownero! + .formatterWowneroAmountToDouble(amount: transaction.amount), price: price); break; case WalletType.bitcoin: case WalletType.litecoin: case WalletType.bitcoinCash: amount = calculateFiatAmountRaw( - cryptoAmount: bitcoin!.formatterBitcoinAmountToDouble(amount: transaction.amount), + cryptoAmount: bitcoin! + .formatterBitcoinAmountToDouble(amount: transaction.amount), price: price); break; case WalletType.haven: final asset = haven!.assetOfTransaction(transaction); final price = balanceViewModel.fiatConvertationStore.prices[asset]; amount = calculateFiatAmountRaw( - cryptoAmount: haven!.formatterMoneroAmountToDouble(amount: transaction.amount), + cryptoAmount: haven! + .formatterMoneroAmountToDouble(amount: transaction.amount), price: price); break; case WalletType.salvium: final asset = salvium!.assetOfTransaction(transaction); final price = balanceViewModel.fiatConvertationStore.prices[asset]; amount = calculateFiatAmountRaw( - cryptoAmount: salvium!.formatterSalviumAmountToDouble(amount: transaction.amount), + cryptoAmount: salvium! + .formatterMoneroAmountToDouble(amount: transaction.amount), price: price); break; case WalletType.ethereum: - final asset = ethereum!.assetOfTransaction(balanceViewModel.wallet, transaction); + final asset = + ethereum!.assetOfTransaction(balanceViewModel.wallet, transaction); final price = balanceViewModel.fiatConvertationStore.prices[asset]; amount = calculateFiatAmountRaw( - cryptoAmount: ethereum!.formatterEthereumAmountToDouble(transaction: transaction), + cryptoAmount: ethereum! + .formatterEthereumAmountToDouble(transaction: transaction), price: price); break; case WalletType.polygon: - final asset = polygon!.assetOfTransaction(balanceViewModel.wallet, transaction); + final asset = + polygon!.assetOfTransaction(balanceViewModel.wallet, transaction); final price = balanceViewModel.fiatConvertationStore.prices[asset]; amount = calculateFiatAmountRaw( - cryptoAmount: polygon!.formatterPolygonAmountToDouble(transaction: transaction), + cryptoAmount: polygon! + .formatterPolygonAmountToDouble(transaction: transaction), price: price); break; case WalletType.nano: amount = calculateFiatAmountRaw( cryptoAmount: double.parse(nanoUtil!.getRawAsUsableString( - nano!.getTransactionAmountRaw(transaction).toString(), nanoUtil!.rawPerNano)), + nano!.getTransactionAmountRaw(transaction).toString(), + nanoUtil!.rawPerNano)), price: price); break; case WalletType.solana: - final asset = solana!.assetOfTransaction(balanceViewModel.wallet, transaction); + final asset = + solana!.assetOfTransaction(balanceViewModel.wallet, transaction); final price = balanceViewModel.fiatConvertationStore.prices[asset]; amount = calculateFiatAmountRaw( cryptoAmount: solana!.getTransactionAmountRaw(transaction), @@ -217,7 +235,8 @@ class TransactionListItem extends ActionListItem with Keyable { break; case WalletType.tron: - final asset = tron!.assetOfTransaction(balanceViewModel.wallet, transaction); + final asset = + tron!.assetOfTransaction(balanceViewModel.wallet, transaction); final price = balanceViewModel.fiatConvertationStore.prices[asset]; final cryptoAmount = tron!.getTransactionAmountRaw(transaction); amount = calculateFiatAmountRaw( diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index a075de28b2..b427c80d90 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -307,9 +307,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with case WalletType.monero: case WalletType.wownero: case WalletType.haven: - return transactionPriority == monero!.getMoneroTransactionPrioritySlow(); case WalletType.salvium: - return transactionPriority == salvium!.getSalviumTransactionPrioritySlow(); + return transactionPriority == monero!.getMoneroTransactionPrioritySlow(); case WalletType.bitcoin: return transactionPriority == bitcoin!.getBitcoinTransactionPrioritySlow(); case WalletType.litecoin: diff --git a/lib/view_model/send/output.dart b/lib/view_model/send/output.dart index 22065c6547..209c587f64 100644 --- a/lib/view_model/send/output.dart +++ b/lib/view_model/send/output.dart @@ -94,6 +94,7 @@ abstract class OutputBase with Store { int _amount = 0; switch (walletType) { case WalletType.monero: + case WalletType.salvium: _amount = monero!.formatterMoneroParseAmount(amount: _cryptoAmount); break; case WalletType.bitcoin: @@ -104,9 +105,6 @@ abstract class OutputBase with Store { case WalletType.haven: _amount = haven!.formatterMoneroParseAmount(amount: _cryptoAmount); break; - case WalletType.salvium: - _amount = salvium!.formatterSalviumParseAmount(amount: _cryptoAmount); - break; case WalletType.ethereum: _amount = ethereum!.formatterEthereumParseAmount(_cryptoAmount); break; @@ -165,7 +163,7 @@ abstract class OutputBase with Store { return bitcoin!.formatterBitcoinAmountToDouble(amount: fee); } - if (_wallet.type == WalletType.monero) { + if (_wallet.type == WalletType.monero || _wallet.type == WalletType.salvium) { return monero!.formatterMoneroAmountToDouble(amount: fee); } @@ -177,10 +175,6 @@ abstract class OutputBase with Store { return haven!.formatterMoneroAmountToDouble(amount: fee); } - if (_wallet.type == WalletType.salvium) { - return salvium!.formatterSalviumAmountToDouble(amount: fee); - } - if (_wallet.type == WalletType.ethereum) { return ethereum!.formatterEthereumAmountToDouble(amount: BigInt.from(fee)); } @@ -304,7 +298,7 @@ abstract class OutputBase with Store { maximumFractionDigits = 12; break; case WalletType.salvium: - maximumFractionDigits = 12; + maximumFractionDigits = 8; break; case WalletType.ethereum: case WalletType.polygon: diff --git a/lib/view_model/send/send_template_view_model.dart b/lib/view_model/send/send_template_view_model.dart index 1c36fc1224..3c78f3000c 100644 --- a/lib/view_model/send/send_template_view_model.dart +++ b/lib/view_model/send/send_template_view_model.dart @@ -52,7 +52,6 @@ abstract class SendTemplateViewModelBase with Store { bool get hasMultiRecipient => _wallet.type != WalletType.haven && - _wallet.type != WalletType.salvium && _wallet.type != WalletType.ethereum && _wallet.type != WalletType.polygon && _wallet.type != WalletType.solana && diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 263def6fee..35ef19f03a 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -278,6 +278,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin || wallet.type == WalletType.monero || + wallet.type == WalletType.salvium || wallet.type == WalletType.wownero || wallet.type == WalletType.bitcoinCash; @@ -692,8 +693,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor } if (walletType == WalletType.ethereum || walletType == WalletType.polygon || - walletType == WalletType.haven || - walletType == WalletType.salvium) { + walletType == WalletType.haven) { if (errorMessage.contains('gas required exceeds allowance') || errorMessage.contains('insufficient funds')) { return S.current.do_not_have_enough_gas_asset(currency.toString()); diff --git a/lib/view_model/transaction_details_view_model.dart b/lib/view_model/transaction_details_view_model.dart index b57024c3cd..fcf7931ae1 100644 --- a/lib/view_model/transaction_details_view_model.dart +++ b/lib/view_model/transaction_details_view_model.dart @@ -171,7 +171,7 @@ abstract class TransactionDetailsViewModelBase with Store { case WalletType.haven: return 'https://explorer.havenprotocol.org/search?value=${txId}'; case WalletType.salvium: - return 'https://explorer.salvium.com/search?value=${txId}'; + return 'https://explorer.salvium.io/search?value=${txId}'; case WalletType.ethereum: return 'https://etherscan.io/tx/${txId}'; case WalletType.nano: @@ -203,7 +203,7 @@ abstract class TransactionDetailsViewModelBase with Store { case WalletType.haven: return S.current.view_transaction_on + 'explorer.havenprotocol.org'; case WalletType.salvium: - return S.current.view_transaction_on + 'explorer.salvium.com'; + return S.current.view_transaction_on + 'explorer.salvium.io'; case WalletType.ethereum: return S.current.view_transaction_on + 'etherscan.io'; case WalletType.nano: diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 4445e59765..689f0ea03f 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -129,4 +129,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 65ec1541137fb5b35d00490dec1bb48d4d9586bb -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 0dccffc4f5..ab9c91fcf1 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -444,13 +444,13 @@ ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = "-"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 10; - DEVELOPMENT_TEAM = 32J6BB6VUS; + DEVELOPMENT_TEAM = 932TLQL94B; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Cake Wallet"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.finance"; @@ -579,13 +579,13 @@ ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = "-"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 10; - DEVELOPMENT_TEAM = 32J6BB6VUS; + DEVELOPMENT_TEAM = 932TLQL94B; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Cake Wallet"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.finance"; @@ -608,13 +608,13 @@ ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = "-"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 10; - DEVELOPMENT_TEAM = 32J6BB6VUS; + DEVELOPMENT_TEAM = 932TLQL94B; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Cake Wallet"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.finance"; diff --git a/scripts/android/pubspec_gen.sh b/scripts/android/pubspec_gen.sh index 4ae4ee3877..ecc8efe0f6 100755 --- a/scripts/android/pubspec_gen.sh +++ b/scripts/android/pubspec_gen.sh @@ -3,6 +3,7 @@ MONERO_COM=monero.com CAKEWALLET=cakewallet HAVEN=haven +SALVIUM=salvium CONFIG_ARGS="" case $APP_ANDROID_TYPE in @@ -18,6 +19,9 @@ case $APP_ANDROID_TYPE in $HAVEN) CONFIG_ARGS="--haven" ;; + $SALVIUM) + CONFIG_ARGS="--salvium" + ;; esac cd ../.. diff --git a/scripts/ios/app_config.sh b/scripts/ios/app_config.sh index 074d59dd73..a8256220ac 100755 --- a/scripts/ios/app_config.sh +++ b/scripts/ios/app_config.sh @@ -4,6 +4,7 @@ set -x -e MONERO_COM="monero.com" CAKEWALLET="cakewallet" HAVEN="haven" +SALVIUM="salvium" DIR=`pwd` if [ -z "$APP_IOS_TYPE" ]; then @@ -38,10 +39,11 @@ case $APP_IOS_TYPE in fi ;; $HAVEN) - - CONFIG_ARGS="--haven" ;; + $SALVIUM) + CONFIG_ARGS="--salvium" + ;; esac cp -rf pubspec_description.yaml pubspec.yaml diff --git a/scripts/ios/build_all.sh b/scripts/ios/build_all.sh index d0186e32dc..9e8fa2c869 100755 --- a/scripts/ios/build_all.sh +++ b/scripts/ios/build_all.sh @@ -9,7 +9,7 @@ DIR=$(dirname "$0") case $APP_IOS_TYPE in "monero.com") $DIR/build_monero_all.sh ;; - "cakewallet") $DIR/build_monero_all.sh && $DIR/build_haven.sh && $DIR/build_mwebd.sh ;; + "cakewallet") $DIR/build_monero_all.sh && $DIR/build_haven.sh && $DIR/build_salvium.sh && $DIR/build_mwebd.sh ;; "haven") $DIR/build_haven_all.sh ;; "salvium") $DIR/build_salvium_all.sh ;; esac diff --git a/tool/configure.dart b/tool/configure.dart index 38b92a9455..0125f64599 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -828,8 +828,9 @@ abstract class HavenAccountList { Future generateSalvium(bool hasImplementation) async { final outputFile = File(salviumOutputPath); const salviumCommonHeaders = """ +import 'package:cw_core/unspent_transaction_output.dart'; +import 'package:cw_core/unspent_coins_info.dart'; import 'package:mobx/mobx.dart'; -import 'package:flutter/foundation.dart'; import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/transaction_priority.dart'; @@ -840,17 +841,22 @@ 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:cw_core/crypto_currency.dart';"""; +import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' as ledger; +import 'package:polyseed/polyseed.dart';"""; const salviumCWHeaders = """ +import 'package:cw_core/account.dart' as salvium_account; 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_core/salvium_amount_format.dart'; +import 'package:cw_core/salvium_transaction_priority.dart'; +import 'package:cw_salvium/api/wallet_manager.dart'; +import 'package:cw_salvium/api/wallet.dart' as salvium_wallet_api; +import 'package:cw_salvium/ledger.dart'; +import 'package:cw_salvium/salvium_unspent.dart'; +import 'package:cw_salvium/api/account_list.dart'; import 'package:cw_salvium/salvium_wallet_service.dart'; import 'package:cw_salvium/salvium_wallet.dart'; import 'package:cw_salvium/salvium_transaction_info.dart'; -import 'package:cw_salvium/salvium_transaction_history.dart'; -import 'package:cw_core/account.dart' as monero_account; -import 'package:cw_salvium/api/wallet.dart' as monero_wallet_api; +import 'package:cw_salvium/salvium_transaction_creation_credentials.dart'; import 'package:cw_salvium/mnemonics/english.dart'; import 'package:cw_salvium/mnemonics/chinese_simplified.dart'; import 'package:cw_salvium/mnemonics/dutch.dart'; @@ -861,25 +867,29 @@ import 'package:cw_salvium/mnemonics/spanish.dart'; import 'package:cw_salvium/mnemonics/portuguese.dart'; import 'package:cw_salvium/mnemonics/french.dart'; import 'package:cw_salvium/mnemonics/italian.dart'; -import 'package:cw_salvium/salvium_transaction_creation_credentials.dart'; -import 'package:cw_salvium/api/balance_list.dart'; +import 'package:cw_salvium/pending_salvium_transaction.dart'; """; const salviumCwPart = "part 'cw_salvium.dart';"; const salviumContent = """ class Account { - Account({required this.id, required this.label}); + 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}); + required this.address, + required this.received, + required this.txCount}); final int id; final String label; final String address; + final String? received; + final int txCount; } class SalviumBalance extends Balance { @@ -909,18 +919,10 @@ class SalviumBalance extends Balance { String get formattedAdditionalBalance => formattedFullBalance; } -class AssetRate { - AssetRate(this.asset, this.rate); - - final String asset; - final int rate; -} - abstract class SalviumWalletDetails { - // FIX-ME: it's abstract class @observable late Account account; - // FIX-ME: it's abstract class + @observable late SalviumBalance balance; } @@ -928,46 +930,67 @@ abstract class SalviumWalletDetails { abstract class Salvium { SalviumAccountList getAccountList(Object wallet); - MoneroSubaddressList getSubaddressList(Object wallet); + SalviumSubaddressList getSubaddressList(Object wallet); TransactionHistoryBase getTransactionHistory(Object wallet); - SalviumWalletDetails getMoneroWalletDetails(Object wallet); + SalviumWalletDetails getSalviumWalletDetails(Object wallet); String getTransactionAddress(Object wallet, int accountIndex, int addressIndex); + String getSubaddressLabel(Object wallet, int accountIndex, int addressIndex); + int getHeightByDate({required DateTime date}); - Future getCurrentHeight(); TransactionPriority getDefaultTransactionPriority(); - TransactionPriority deserializeMoneroTransactionPriority({required int raw}); + TransactionPriority getSalviumTransactionPrioritySlow(); + TransactionPriority getSalviumTransactionPriorityAutomatic(); + TransactionPriority deserializeSalviumTransactionPriority({required int raw}); List getTransactionPriorities(); - List getMoneroWordList(String language); + List getSalviumWordList(String language); + + List getUnspents(Object wallet); + Future updateUnspents(Object wallet); + + Future getCurrentHeight(); + + Future commitTransactionUR(Object wallet, String ur); + + String exportOutputsUR(Object wallet, bool all); + + bool importKeyImagesUR(Object wallet, String ur); WalletCredentials createSalviumRestoreWalletFromKeysCredentials({ - required String name, - required String spendKey, - required String viewKey, - required String address, - required String password, - required String language, - required int height}); + required String name, + required String spendKey, + required String viewKey, + required String address, + required String password, + required String language, + required int height}); WalletCredentials createSalviumRestoreWalletFromSeedCredentials({required String name, required String password, required int height, required String mnemonic}); - WalletCredentials createSalviumNewWalletCredentials({required String name, required String language, String? password}); + WalletCredentials createSalviumRestoreWalletFromHardwareCredentials({required String name, required String password, required int height, required ledger.LedgerConnection ledgerConnection}); + WalletCredentials createSalviumNewWalletCredentials({required String name, required String language, required bool isPolyseed, String? password}); Map getKeys(Object wallet); - Object createSalviumTransactionCreationCredentials({required List outputs, required TransactionPriority priority, required String assetType}); - String formatterMoneroAmountToString({required int amount}); - double formatterMoneroAmountToDouble({required int amount}); - int formatterMoneroParseAmount({required String amount}); + int? getRestoreHeight(Object wallet); + Object createSalviumTransactionCreationCredentials({required List outputs, required TransactionPriority priority}); + Object createSalviumTransactionCreationCredentialsRaw({required List outputs, required TransactionPriority priority}); + String formatterSalviumAmountToString({required int amount}); + double formatterSalviumAmountToDouble({required int amount}); + int formatterSalviumParseAmount({required String amount}); Account getCurrentAccount(Object wallet); - void setCurrentAccount(Object wallet, int id, String label); + void salviumCheck(); + bool isViewOnly(); + void setCurrentAccount(Object wallet, int id, String label, String? balance); void onStartup(); int getTransactionInfoAccountId(TransactionInfo tx); - WalletService createSalviumWalletService(Box walletInfoSource); - CryptoCurrency assetOfTransaction(TransactionInfo tx); - List getAssetRate(); + WalletService createSalviumWalletService(Box walletInfoSource, Box unspentCoinSource); + Map pendingTransactionInfo(Object transaction); + void setLedgerConnection(Object wallet, ledger.LedgerConnection connection); + void resetLedgerConnection(); + void setGlobalLedgerConnection(ledger.LedgerConnection connection); } -abstract class MoneroSubaddressList { +abstract class SalviumSubaddressList { ObservableList get subaddresses; void update(Object wallet, {required int accountIndex}); void refresh(Object wallet, {required int accountIndex}); @@ -1577,6 +1600,7 @@ Future generatePubspec({ required bool hasMonero, required bool hasBitcoin, required bool hasHaven, + required bool hasSalvium, required bool hasEthereum, required bool hasNano, required bool hasBanano, @@ -1603,6 +1627,10 @@ Future generatePubspec({ cw_haven: path: ./cw_haven """; + const cwSalvium = """ + cw_salvium: + path: ./cw_salvium + """; const cwSharedExternal = """ cw_shared_external: path: ./cw_shared_external @@ -1699,6 +1727,10 @@ Future generatePubspec({ output += '\n$cwSharedExternal\n$cwHaven'; } + if (hasSalvium) { + output += '\n$cwSharedExternal\n$cwSalvium'; + } + if (hasFlutterSecureStorage) { output += '\n$flutterSecureStorage\n'; } @@ -1727,6 +1759,7 @@ Future generateWalletTypes({ required bool hasMonero, required bool hasBitcoin, required bool hasHaven, + required bool hasSalvium, required bool hasEthereum, required bool hasNano, required bool hasBanano, @@ -1794,6 +1827,10 @@ Future generateWalletTypes({ outputContent += '\tWalletType.haven,\n'; } + if (hasSalvium) { + outputContent += '\tWalletType.salvium,\n'; + } + outputContent += '];\n'; await walletTypesFile.writeAsString(outputContent); } From 51fe839f9a4fef44f31c12622dfc5aa38874b876 Mon Sep 17 00:00:00 2001 From: 0xskydb Date: Tue, 10 Dec 2024 21:09:57 -0500 Subject: [PATCH 3/4] bug fix --- cw_core/lib/get_height_by_date.dart | 37 +- cw_core/lib/salvium_amount_format.dart | 18 + cw_mweb/lib/cw_mweb.dart | 97 +-- cw_salvium/lib/salvium_unspent.dart | 9 + cw_salvium/lib/salvium_wallet.dart | 102 +-- cw_salvium/lib/update_salvium_rate.dart | 15 - lib/core/seed_validator.dart | 7 +- lib/di.dart | 684 ++++++++++-------- lib/entities/update_salvium_rate.dart | 26 - lib/reactions/fiat_rate_update.dart | 40 +- lib/reactions/on_current_wallet_change.dart | 68 +- .../on_wallet_sync_status_change.dart | 12 +- .../dashboard/transaction_list_item.dart | 4 +- .../monero_account_list_view_model.dart | 76 +- lib/view_model/send/send_view_model.dart | 164 +++-- .../unspent_coins/unspent_coins_item.dart | 3 + .../unspent_coins_list_view_model.dart | 31 +- lib/view_model/wallet_new_vm.dart | 23 +- res/values/strings_en.arb | 2 + tool/configure.dart | 22 +- 20 files changed, 812 insertions(+), 628 deletions(-) create mode 100644 cw_core/lib/salvium_amount_format.dart create mode 100644 cw_salvium/lib/salvium_unspent.dart delete mode 100644 cw_salvium/lib/update_salvium_rate.dart delete mode 100644 lib/entities/update_salvium_rate.dart diff --git a/cw_core/lib/get_height_by_date.dart b/cw_core/lib/get_height_by_date.dart index 7e249a3f84..bcef475c48 100644 --- a/cw_core/lib/get_height_by_date.dart +++ b/cw_core/lib/get_height_by_date.dart @@ -227,14 +227,15 @@ const havenDates = { DateTime formatMapKey(String key) => dateFormat.parse(key); int getHavenHeightByDate({required DateTime date}) { - String closestKey = - havenDates.keys.firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => ''); + String closestKey = havenDates.keys + .firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => ''); return havenDates[closestKey] ?? 0; } Future getHavenCurrentHeight() async { - final response = await http.get(Uri.parse('https://explorer.havenprotocol.org/api/networkinfo')); + final response = await http + .get(Uri.parse('https://explorer.havenprotocol.org/api/networkinfo')); if (response.statusCode == 200) { final info = jsonDecode(response.body); @@ -245,21 +246,22 @@ Future getHavenCurrentHeight() async { } const salviumDates = { - "2024-11": 86752 - "2024-10": 65675 - "2024-09": 44680 + "2024-11": 86752, + "2024-10": 65675, + "2024-09": 44680, "2024-08": 22339 }; int getSalviumHeightByDate({required DateTime date}) { - String closestKey = - salviumDates.keys.firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => ''); + String closestKey = salviumDates.keys + .firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => ''); return salviumDates[closestKey] ?? 0; } Future getSalviumCurrentHeight() async { - final response = await http.get(Uri.parse('https://explorer.salvium.io/api/networkinfo')); + final response = + await http.get(Uri.parse('https://explorer.salvium.io/api/networkinfo')); if (response.statusCode == 200) { final info = jsonDecode(response.body); @@ -305,8 +307,9 @@ Future getBitcoinHeightByDateAPI({required DateTime date}) async { int getBitcoinHeightByDate({required DateTime date}) { String dateKey = '${date.year}-${date.month.toString().padLeft(2, '0')}'; - final closestKey = bitcoinDates.keys - .firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => bitcoinDates.keys.last); + final closestKey = bitcoinDates.keys.firstWhere( + (key) => formatMapKey(key).isBefore(date), + orElse: () => bitcoinDates.keys.last); final beginningBlock = bitcoinDates[dateKey] ?? bitcoinDates[closestKey]!; final startOfMonth = DateTime(date.year, date.month); @@ -319,15 +322,17 @@ int getBitcoinHeightByDate({required DateTime date}) { } DateTime getDateByBitcoinHeight(int height) { - final closestEntry = bitcoinDates.entries - .lastWhere((entry) => entry.value >= height, orElse: () => bitcoinDates.entries.first); + final closestEntry = bitcoinDates.entries.lastWhere( + (entry) => entry.value >= height, + orElse: () => bitcoinDates.entries.first); final beginningBlock = closestEntry.value; final startOfMonth = formatMapKey(closestEntry.key); final blocksDifference = height - beginningBlock; final hoursDifference = blocksDifference / 5.5; - final estimatedDate = startOfMonth.add(Duration(hours: hoursDifference.ceil())); + final estimatedDate = + startOfMonth.add(Duration(hours: hoursDifference.ceil())); if (estimatedDate.isAfter(DateTime.now())) { return DateTime.now(); @@ -413,8 +418,8 @@ const wowDates = { }; int getWowneroHeightByDate({required DateTime date}) { - String closestKey = - wowDates.keys.firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => ''); + String closestKey = wowDates.keys + .firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => ''); return wowDates[closestKey] ?? 0; } diff --git a/cw_core/lib/salvium_amount_format.dart b/cw_core/lib/salvium_amount_format.dart new file mode 100644 index 0000000000..238133139a --- /dev/null +++ b/cw_core/lib/salvium_amount_format.dart @@ -0,0 +1,18 @@ +import 'package:intl/intl.dart'; +import 'package:cw_core/crypto_amount_format.dart'; + +const salviumAmountLength = 8; +const salviumAmountDivider = 100000000; +final salviumAmountFormat = NumberFormat() + ..maximumFractionDigits = salviumAmountLength + ..minimumFractionDigits = 1; + +String salviumAmountToString({required int amount}) => salviumAmountFormat + .format(cryptoAmountToDouble(amount: amount, divider: salviumAmountDivider)) + .replaceAll(',', ''); + +double salviumAmountToDouble({required int amount}) => + cryptoAmountToDouble(amount: amount, divider: salviumAmountDivider); + +int salviumParseAmount({required String amount}) => + (double.parse(amount) * salviumAmountDivider).round(); diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index 5941970184..b352628ea6 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -4,7 +4,7 @@ import 'dart:developer'; import 'dart:io'; import 'dart:typed_data'; -import 'package:cw_core/utils/print_verbose.dart'; +// import 'package:cw_core/utils/print_verbose.dart'; import 'package:grpc/grpc.dart'; import 'package:path_provider/path_provider.dart'; import 'cw_mweb_platform_interface.dart'; @@ -18,7 +18,6 @@ class CwMweb { static Timer? logTimer; static String? nodeUriOverride; - static Future setNodeUriOverride(String uri) async { nodeUriOverride = uri; if (_rpcClient != null) { @@ -40,41 +39,43 @@ class CwMweb { final fileStream = file.openRead(lastLength, currentLength); final newLines = await fileStream.transform(utf8.decoder).join(); lastLength = currentLength; - printV(newLines); + print(newLines); } } on GrpcError catch (e) { - printV('Caught grpc error: ${e.message}'); + print('Caught grpc error: ${e.message}'); } catch (e) { - printV('The mwebd debug log probably is not initialized yet.'); + print('The mwebd debug log probably is not initialized yet.'); } }); } static Future _initializeClient() async { - printV("_initializeClient() called!"); + print("_initializeClient() called!"); final appDir = await getApplicationSupportDirectory(); const ltcNodeUri = "ltc-electrum.cakewallet.com:9333"; String debugLogPath = "${appDir.path}/logs/debug.log"; readFileWithTimer(debugLogPath); - _port = await CwMwebPlatform.instance.start(appDir.path, nodeUriOverride ?? ltcNodeUri); + _port = await CwMwebPlatform.instance + .start(appDir.path, nodeUriOverride ?? ltcNodeUri); if (_port == null || _port == 0) { throw Exception("Failed to start server"); } - printV("Attempting to connect to server on port: $_port"); + print("Attempting to connect to server on port: $_port"); // wait for the server to finish starting up before we try to connect to it: await Future.delayed(const Duration(seconds: 8)); - _clientChannel = ClientChannel('127.0.0.1', port: _port!, channelShutdownHandler: () { + _clientChannel = + ClientChannel('127.0.0.1', port: _port!, channelShutdownHandler: () { _rpcClient = null; - printV("Channel is shutting down!"); + print("Channel is shutting down!"); }, - options: const ChannelOptions( - credentials: ChannelCredentials.insecure(), - keepAlive: ClientKeepAliveOptions(permitWithoutCalls: true), - )); + options: const ChannelOptions( + credentials: ChannelCredentials.insecure(), + keepAlive: ClientKeepAliveOptions(permitWithoutCalls: true), + )); _rpcClient = RpcClient(_clientChannel!); } @@ -84,21 +85,22 @@ class CwMweb { if (_rpcClient == null) { await _initializeClient(); } - final status = await _rpcClient! - .status(StatusRequest(), options: CallOptions(timeout: TIMEOUT_DURATION)); + final status = await _rpcClient!.status(StatusRequest(), + options: CallOptions(timeout: TIMEOUT_DURATION)); if (status.blockTime == 0) { - throw Exception("blockTime shouldn't be 0! (this connection is likely broken)"); + throw Exception( + "blockTime shouldn't be 0! (this connection is likely broken)"); } return _rpcClient!; } on GrpcError catch (e) { - printV("Attempt $i failed: $e"); - printV('Caught grpc error: ${e.message}'); + print("Attempt $i failed: $e"); + print('Caught grpc error: ${e.message}'); _rpcClient = null; // necessary if the database isn't open: await stop(); await Future.delayed(const Duration(seconds: 3)); } catch (e) { - printV("Attempt $i failed: $e"); + print("Attempt $i failed: $e"); _rpcClient = null; await stop(); await Future.delayed(const Duration(seconds: 3)); @@ -112,29 +114,32 @@ class CwMweb { await CwMwebPlatform.instance.stop(); await cleanup(); } on GrpcError catch (e) { - printV('Caught grpc error: ${e.message}'); + print('Caught grpc error: ${e.message}'); } catch (e) { - printV("Error stopping server: $e"); + print("Error stopping server: $e"); } } - static Future address(Uint8List scanSecret, Uint8List spendPub, int index) async { + static Future address( + Uint8List scanSecret, Uint8List spendPub, int index) async { try { - return (await CwMwebPlatform.instance.addresses(scanSecret, spendPub, index, index + 1)) + return (await CwMwebPlatform.instance + .addresses(scanSecret, spendPub, index, index + 1)) ?.split(',') .first; } on GrpcError catch (e) { - printV('Caught grpc error: ${e.message}'); + print('Caught grpc error: ${e.message}'); } catch (e) { - printV("Error getting address: $e"); + print("Error getting address: $e"); } return null; } - static Future?> addresses( - Uint8List scanSecret, Uint8List spendPub, int fromIndex, int toIndex) async { + static Future?> addresses(Uint8List scanSecret, + Uint8List spendPub, int fromIndex, int toIndex) async { try { - return (await CwMwebPlatform.instance.addresses(scanSecret, spendPub, fromIndex, toIndex)) + return (await CwMwebPlatform.instance + .addresses(scanSecret, spendPub, fromIndex, toIndex)) ?.split(','); } on GrpcError catch (e) { log('Caught grpc error: ${e.message}'); @@ -158,11 +163,12 @@ class CwMweb { log("mweb.spent() called"); try { _rpcClient = await stub(); - return await _rpcClient!.spent(request, options: CallOptions(timeout: TIMEOUT_DURATION)); + return await _rpcClient! + .spent(request, options: CallOptions(timeout: TIMEOUT_DURATION)); } on GrpcError catch (e) { - printV('Caught grpc error: ${e.message}'); + print('Caught grpc error: ${e.message}'); } catch (e) { - printV("Error getting spent: $e"); + print("Error getting spent: $e"); } return SpentResponse(); } @@ -171,11 +177,12 @@ class CwMweb { log("mweb.status() called"); try { _rpcClient = await stub(); - return await _rpcClient!.status(request, options: CallOptions(timeout: TIMEOUT_DURATION)); + return await _rpcClient! + .status(request, options: CallOptions(timeout: TIMEOUT_DURATION)); } on GrpcError catch (e) { - printV('Caught grpc error: ${e.message}'); + print('Caught grpc error: ${e.message}'); } catch (e) { - printV("Error getting status: $e"); + print("Error getting status: $e"); } return StatusResponse(); } @@ -184,11 +191,12 @@ class CwMweb { log("mweb.create() called"); try { _rpcClient = await stub(); - return await _rpcClient!.create(request, options: CallOptions(timeout: TIMEOUT_DURATION)); + return await _rpcClient! + .create(request, options: CallOptions(timeout: TIMEOUT_DURATION)); } on GrpcError catch (e) { - printV('Caught grpc error: ${e.message}'); + print('Caught grpc error: ${e.message}'); } catch (e) { - printV("Error getting create: $e"); + print("Error getting create: $e"); } return CreateResponse(); } @@ -197,14 +205,14 @@ class CwMweb { log("mweb.utxos() called"); try { _rpcClient = await stub(); - final resp = _rpcClient! - .utxos(request, options: CallOptions(timeout: const Duration(days: 1000 * 365))); + final resp = _rpcClient!.utxos(request, + options: CallOptions(timeout: const Duration(days: 1000 * 365))); log("got utxo stream"); return resp; } on GrpcError catch (e) { - printV('Caught grpc error: ${e.message}'); + print('Caught grpc error: ${e.message}'); } catch (e) { - printV("Error getting utxos: $e"); + print("Error getting utxos: $e"); } return null; } @@ -213,12 +221,13 @@ class CwMweb { log("mweb.broadcast() called"); try { _rpcClient = await stub(); - return await _rpcClient!.broadcast(request, options: CallOptions(timeout: TIMEOUT_DURATION)); + return await _rpcClient! + .broadcast(request, options: CallOptions(timeout: TIMEOUT_DURATION)); } on GrpcError catch (e) { log('Caught grpc error: ${e.message}'); throw "error from broadcast mweb: $e"; } catch (e) { - printV("Error getting utxos: $e"); + print("Error getting utxos: $e"); rethrow; } } diff --git a/cw_salvium/lib/salvium_unspent.dart b/cw_salvium/lib/salvium_unspent.dart new file mode 100644 index 0000000000..dff82c593a --- /dev/null +++ b/cw_salvium/lib/salvium_unspent.dart @@ -0,0 +1,9 @@ +import 'package:cw_core/unspent_transaction_output.dart'; + +class SalviumUnspent extends Unspent { + SalviumUnspent(String address, String hash, String keyImage, int value, + bool isFrozen, this.isUnlocked) + : super(address, hash, value, 0, keyImage) {} + + final bool isUnlocked; +} diff --git a/cw_salvium/lib/salvium_wallet.dart b/cw_salvium/lib/salvium_wallet.dart index b8279e6333..c3a6a4f378 100644 --- a/cw_salvium/lib/salvium_wallet.dart +++ b/cw_salvium/lib/salvium_wallet.dart @@ -11,7 +11,7 @@ import 'package:cw_salvium/salvium_wallet_addresses.dart'; import 'package:cw_core/monero_wallet_utils.dart'; import 'package:cw_salvium/api/structs/pending_transaction.dart'; import 'package:mobx/mobx.dart'; -import 'package:cw_salvium/api/transaction_history.dart' as salvium_transaction_history; +import 'package:cw_salvium/api/transaction_history.dart' as transaction_history; import 'package:cw_salvium/api/wallet.dart' as salvium_wallet; import 'package:cw_salvium/api/monero_output.dart'; import 'package:cw_salvium/pending_salvium_transaction.dart'; @@ -33,8 +33,8 @@ const moneroBlockSize = 1000; class SalviumWallet = SalviumWalletBase with _$SalviumWallet; -abstract class SalviumWalletBase - extends WalletBase with Store { +abstract class SalviumWalletBase extends WalletBase with Store { SalviumWalletBase({required WalletInfo walletInfo, String? password}) : balance = ObservableMap.of(getSalviumBalance(accountIndex: 0)), _isTransactionUpdating = false, @@ -44,7 +44,8 @@ abstract class SalviumWalletBase syncStatus = NotConnectedSyncStatus(), super(walletInfo) { transactionHistory = SalviumTransactionHistory(); - _onAccountChangeReaction = reaction((_) => walletAddresses.account, (Account? account) { + _onAccountChangeReaction = + reaction((_) => walletAddresses.account, (Account? account) { if (account == null) { return; } @@ -72,7 +73,8 @@ abstract class SalviumWalletBase @override MoneroWalletKeys get keys => MoneroWalletKeys( - primaryAddress: salvium_wallet.getAddress(accountIndex: 0, addressIndex: 0), + primaryAddress: + salvium_wallet.getAddress(accountIndex: 0, addressIndex: 0), privateSpendKey: salvium_wallet.getSecretSpendKey(), privateViewKey: salvium_wallet.getSecretViewKey(), publicSpendKey: salvium_wallet.getPublicSpendKey(), @@ -86,7 +88,8 @@ abstract class SalviumWalletBase Future init() async { await walletAddresses.init(); - balance.addAll(getSalviumBalance(accountIndex: walletAddresses.account?.id ?? 0)); + balance.addAll( + getSalviumBalance(accountIndex: walletAddresses.account?.id ?? 0)); _setListeners(); await updateTransactions(); @@ -94,12 +97,13 @@ abstract class SalviumWalletBase salvium_wallet.setRecoveringFromSeed(isRecovery: walletInfo.isRecovery); if (salvium_wallet.getCurrentHeight() <= 1) { - salvium_wallet.setRefreshFromBlockHeight(height: walletInfo.restoreHeight); + salvium_wallet.setRefreshFromBlockHeight( + height: walletInfo.restoreHeight); } } - _autoSaveTimer = - Timer.periodic(Duration(seconds: _autoSaveInterval), (_) async => await save()); + _autoSaveTimer = Timer.periodic( + Duration(seconds: _autoSaveInterval), (_) async => await save()); } @override @@ -156,8 +160,10 @@ abstract class SalviumWalletBase final _credentials = credentials as SalviumTransactionCreationCredentials; final outputs = _credentials.outputs; final hasMultiDestination = outputs.length > 1; - final assetType = CryptoCurrency.fromString(_credentials.assetType.toLowerCase()); - final balances = getSalviumBalance(accountIndex: walletAddresses.account!.id); + final assetType = + CryptoCurrency.fromString(_credentials.assetType.toLowerCase()); + final balances = + getSalviumBalance(accountIndex: walletAddresses.account!.id); final unlockedBalance = balances[assetType]!.unlockedBalance; PendingTransactionDescription pendingTransactionDescription; @@ -167,13 +173,14 @@ abstract class SalviumWalletBase } if (hasMultiDestination) { - if (outputs.any((item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) { + if (outputs.any( + (item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) { throw SalviumTransactionCreationException( 'You do not have enough coins 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)); if (unlockedBalance < totalAmount) { throw SalviumTransactionCreationException( @@ -182,20 +189,25 @@ abstract class SalviumWalletBase final moneroOutputs = outputs .map((output) => MoneroOutput( - address: output.address, amount: output.cryptoAmount!.replaceAll(',', '.'))) + address: output.address, + amount: output.cryptoAmount!.replaceAll(',', '.'))) .toList(); - pendingTransactionDescription = await transaction_history.createTransactionMultDest( - outputs: moneroOutputs, - priorityRaw: _credentials.priority.serialize(), - accountIndex: walletAddresses.account!.id); + pendingTransactionDescription = + await transaction_history.createTransactionMultDest( + outputs: moneroOutputs, + priorityRaw: _credentials.priority.serialize(), + accountIndex: walletAddresses.account!.id); } else { final output = outputs.first; - final address = output.isParsedAddress && (output.extractedAddress?.isNotEmpty ?? false) + final address = output.isParsedAddress && + (output.extractedAddress?.isNotEmpty ?? false) ? output.extractedAddress! : output.address; - final amount = output.sendAll ? null : output.cryptoAmount!.replaceAll(',', '.'); - final int? formattedAmount = output.sendAll ? null : output.formattedCryptoAmount; + final amount = + output.sendAll ? null : output.cryptoAmount!.replaceAll(',', '.'); + final int? formattedAmount = + output.sendAll ? null : output.formattedCryptoAmount; if ((formattedAmount != null && unlockedBalance < formattedAmount) || (formattedAmount == null && unlockedBalance <= 0)) { @@ -205,12 +217,13 @@ abstract class SalviumWalletBase 'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.'); } - pendingTransactionDescription = await transaction_history.createTransaction( - address: address, - assetType: _credentials.assetType, - amount: amount, - priorityRaw: _credentials.priority.serialize(), - accountIndex: walletAddresses.account!.id); + pendingTransactionDescription = + await transaction_history.createTransaction( + address: address, + assetType: _credentials.assetType, + amount: amount, + priorityRaw: _credentials.priority.serialize(), + accountIndex: walletAddresses.account!.id); } return PendingSalviumTransaction(pendingTransactionDescription, assetType); @@ -298,14 +311,15 @@ abstract class SalviumWalletBase } String getTransactionAddress(int accountIndex, int addressIndex) => - salvium_wallet.getAddress(accountIndex: accountIndex, addressIndex: addressIndex); + salvium_wallet.getAddress( + accountIndex: accountIndex, addressIndex: addressIndex); @override Future> fetchTransactions() async { - salvium_transaction_history.refreshTransactions(); - return _getAllTransactions(null) - .fold>({}, - (Map acc, SalviumTransactionInfo tx) { + transaction_history.refreshTransactions(); + return _getAllTransactions(null).fold>( + {}, + (Map acc, SalviumTransactionInfo tx) { acc[tx.id] = tx; return acc; }); @@ -328,10 +342,11 @@ abstract class SalviumWalletBase } } - List _getAllTransactions(dynamic _) => salvium_transaction_history - .getAllTransations() - .map((row) => SalviumTransactionInfo.fromRow(row)) - .toList(); + List _getAllTransactions(dynamic _) => + transaction_history + .getAllTransations() + .map((row) => SalviumTransactionInfo.fromRow(row)) + .toList(); void _setListeners() { _listener?.stop(); @@ -353,7 +368,8 @@ abstract class SalviumWalletBase } 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; @@ -371,10 +387,11 @@ abstract class SalviumWalletBase return nodeHeight - heightDistance; } - void _askForUpdateBalance() => - balance.addAll(getSalviumBalance(accountIndex: walletAddresses.account!.id)); + void _askForUpdateBalance() => balance + .addAll(getSalviumBalance(accountIndex: walletAddresses.account!.id)); - Future _askForUpdateTransactionHistory() async => await updateTransactions(); + Future _askForUpdateTransactionHistory() async => + await updateTransactions(); void _onNewBlock(int height, int blocksLeft, double ptc) async { try { @@ -424,6 +441,7 @@ abstract class SalviumWalletBase throw UnimplementedError(); @override - Future verifyMessage(String message, String signature, {String? address = null}) => + Future verifyMessage(String message, String signature, + {String? address = null}) => throw UnimplementedError(); } diff --git a/cw_salvium/lib/update_salvium_rate.dart b/cw_salvium/lib/update_salvium_rate.dart deleted file mode 100644 index 929edfc62f..0000000000 --- a/cw_salvium/lib/update_salvium_rate.dart +++ /dev/null @@ -1,15 +0,0 @@ -//import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; -import 'package:cw_core/crypto_currency.dart'; -import 'package:cw_core/monero_amount_format.dart'; -import 'package:cw_salvium/api/balance_list.dart'; - -//Future updateSalviumRate(FiatConversionStore fiatConversionStore) async { -// final rate = getRate(); -// final base = rate.firstWhere((row) => row.getAssetType() == 'XUSD', orElse: () => null); -// rate.forEach((row) { -// final cur = CryptoCurrency.fromString(row.getAssetType()); -// final baseRate = moneroAmountToDouble(amount: base.getRate()); -// final rowRate = moneroAmountToDouble(amount: row.getRate()); -// fiatConversionStore.prices[cur] = baseRate * rowRate; -// }); -//} \ No newline at end of file diff --git a/lib/core/seed_validator.dart b/lib/core/seed_validator.dart index 261485f356..521ce7201f 100644 --- a/lib/core/seed_validator.dart +++ b/lib/core/seed_validator.dart @@ -22,7 +22,8 @@ class SeedValidator extends Validator { final String language; final List _words; - static List getWordList({required WalletType type, required String language}) { + static List getWordList( + {required WalletType type, required String language}) { switch (type) { case WalletType.bitcoin: return getBitcoinWordList(language); @@ -33,7 +34,7 @@ class SeedValidator extends Validator { case WalletType.haven: return haven!.getMoneroWordList(language); case WalletType.salvium: - return salvium!.getMoneroWordList(language); + return salvium!.getSalviumWordList(language); case WalletType.ethereum: return ethereum!.getEthereumWordList(language); case WalletType.bitcoinCash: @@ -48,7 +49,7 @@ class SeedValidator extends Validator { case WalletType.tron: return tron!.getTronWordList(language); case WalletType.wownero: - return wownero!.getWowneroWordList(language); + return wownero!.getWowneroWordList(language); case WalletType.none: return []; } diff --git a/lib/di.dart b/lib/di.dart index bd63142d6e..8ad52b371c 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -301,7 +301,8 @@ Future setup({ _anonpayInvoiceInfoSource = anonpayInvoiceInfoSource; if (!_isSetupFinished) { - getIt.registerSingletonAsync(() => SharedPreferences.getInstance()); + getIt.registerSingletonAsync( + () => SharedPreferences.getInstance()); getIt.registerSingleton(secureStorage); } if (!_isSetupFinished) { @@ -317,7 +318,8 @@ Future setup({ powNodeSource: _powNodeSource, isBitcoinBuyEnabled: isBitcoinBuyEnabled, // Enforce darkTheme on platforms other than mobile till the design for other themes is completed - initialTheme: responsiveLayoutUtil.shouldRenderMobileUI && DeviceInfo.instance.isMobile + initialTheme: responsiveLayoutUtil.shouldRenderMobileUI && + DeviceInfo.instance.isMobile ? null : ThemeList.darkTheme, ); @@ -327,7 +329,8 @@ Future setup({ } getIt.registerFactory>(() => _nodeSource); - getIt.registerFactory>(() => _powNodeSource, instanceName: Node.boxName + "pow"); + getIt.registerFactory>(() => _powNodeSource, + instanceName: Node.boxName + "pow"); getIt.registerSingleton(AuthenticationStore()); getIt.registerSingleton(WalletListStore()); @@ -338,20 +341,23 @@ Future setup({ walletList: getIt.get(), settingsStore: getIt.get(), nodeListStore: getIt.get())); - getIt.registerSingleton( - TradesStore(tradesSource: _tradesSource, settingsStore: getIt.get())); - getIt.registerSingleton( - OrdersStore(ordersSource: _ordersSource, settingsStore: getIt.get())); + getIt.registerSingleton(TradesStore( + tradesSource: _tradesSource, settingsStore: getIt.get())); + getIt.registerSingleton(OrdersStore( + ordersSource: _ordersSource, settingsStore: getIt.get())); getIt.registerSingleton(TradeFilterStore()); getIt.registerSingleton(TransactionFilterStore()); getIt.registerSingleton(FiatConversionStore()); - getIt.registerSingleton(SendTemplateStore(templateSource: _templates)); + getIt.registerSingleton( + SendTemplateStore(templateSource: _templates)); getIt.registerSingleton( ExchangeTemplateStore(templateSource: _exchangeTemplates)); - getIt.registerSingleton( - YatStore(appStore: getIt.get(), secureStorage: getIt.get())..init()); - getIt.registerSingleton( - AnonpayTransactionsStore(anonpayInvoiceInfoSource: _anonpayInvoiceInfoSource)); + getIt.registerSingleton(YatStore( + appStore: getIt.get(), + secureStorage: getIt.get()) + ..init()); + getIt.registerSingleton(AnonpayTransactionsStore( + anonpayInvoiceInfoSource: _anonpayInvoiceInfoSource)); getIt.registerSingleton(SeedSettingsStore()); getIt.registerLazySingleton(() => LedgerViewModel()); @@ -360,18 +366,21 @@ Future setup({ getIt.registerSingleton(secretStore); - getIt.registerFactory(() => KeyService(getIt.get())); + getIt.registerFactory( + () => KeyService(getIt.get())); - getIt.registerFactoryParam((type, _) => - WalletCreationService( + getIt.registerFactoryParam( + (type, _) => WalletCreationService( initialType: type, keyService: getIt.get(), sharedPreferences: getIt.get(), settingsStore: getIt.get(), walletInfoSource: _walletInfoSource)); - getIt.registerFactoryParam( - (type, _) => AdvancedPrivacySettingsViewModel(type, getIt.get())); + getIt.registerFactoryParam( + (type, _) => + AdvancedPrivacySettingsViewModel(type, getIt.get())); getIt.registerFactory(() => WalletLoadingService( getIt.get(), @@ -379,20 +388,23 @@ Future setup({ (WalletType type) => getIt.get(param1: type))); getIt.registerFactoryParam( - (newWalletArgs, _) => WalletNewVM( - getIt.get(), - getIt.get(param1:newWalletArgs.type), - _walletInfoSource, - getIt.get(param1: newWalletArgs.type), - getIt.get(), - newWalletArguments: newWalletArgs,)); - + (newWalletArgs, _) => WalletNewVM( + getIt.get(), + getIt.get(param1: newWalletArgs.type), + _walletInfoSource, + getIt.get( + param1: newWalletArgs.type), + getIt.get(), + newWalletArguments: newWalletArgs, + )); - getIt.registerFactory(() => NewWalletTypeViewModel(_walletInfoSource)); + getIt.registerFactory( + () => NewWalletTypeViewModel(_walletInfoSource)); getIt.registerFactory( () { - final instance = WalletManager(_walletInfoSource, getIt.get()); + final instance = + WalletManager(_walletInfoSource, getIt.get()); instance.updateWalletGroups(); return instance; }, @@ -408,75 +420,81 @@ Future setup({ ), ); - getIt.registerFactoryParam((args, closable) { + getIt.registerFactoryParam( + (args, closable) { return WalletUnlockPage( - getIt.get(param1: args), - args.callback, - args.authPasswordHandler, - closable: closable); + getIt.get(param1: args), + args.callback, + args.authPasswordHandler, + closable: closable); }, instanceName: 'wallet_unlock_loadable'); getIt.registerFactory( - () => getIt.get( - param1: WalletUnlockArguments( - callback: (bool successful, _) { - if (successful) { - final authStore = getIt.get(); - authStore.allowed(); - }}), - param2: false, - instanceName: 'wallet_unlock_loadable'), - instanceName: 'wallet_password_login'); - - getIt.registerFactoryParam((args, closable) { + () => getIt.get( + param1: WalletUnlockArguments(callback: (bool successful, _) { + if (successful) { + final authStore = getIt.get(); + authStore.allowed(); + } + }), + param2: false, + instanceName: 'wallet_unlock_loadable'), + instanceName: 'wallet_password_login'); + + getIt.registerFactoryParam( + (args, closable) { return WalletUnlockPage( - getIt.get(param1: args), - args.callback, - args.authPasswordHandler, - closable: closable); + getIt.get(param1: args), + args.callback, + args.authPasswordHandler, + closable: closable); }, instanceName: 'wallet_unlock_verifiable'); - getIt.registerFactoryParam((args, _) { + getIt.registerFactoryParam((args, _) { final currentWalletName = getIt - .get() - .getString(PreferencesKey.currentWalletName) ?? ''; - final currentWalletTypeRaw = - getIt.get() - .getInt(PreferencesKey.currentWalletType) ?? 0; + .get() + .getString(PreferencesKey.currentWalletName) ?? + ''; + final currentWalletTypeRaw = getIt + .get() + .getInt(PreferencesKey.currentWalletType) ?? + 0; final currentWalletType = deserializeFromInt(currentWalletTypeRaw); return WalletUnlockLoadableViewModel( - getIt.get(), - getIt.get(), - walletName: args.walletName ?? currentWalletName, - walletType: args.walletType ?? currentWalletType); + getIt.get(), getIt.get(), + walletName: args.walletName ?? currentWalletName, + walletType: args.walletType ?? currentWalletType); }); - getIt.registerFactoryParam((args, _) { + getIt.registerFactoryParam((args, _) { final currentWalletName = getIt - .get() - .getString(PreferencesKey.currentWalletName) ?? ''; - final currentWalletTypeRaw = - getIt.get() - .getInt(PreferencesKey.currentWalletType) ?? 0; + .get() + .getString(PreferencesKey.currentWalletName) ?? + ''; + final currentWalletTypeRaw = getIt + .get() + .getInt(PreferencesKey.currentWalletType) ?? + 0; final currentWalletType = deserializeFromInt(currentWalletTypeRaw); - return WalletUnlockVerifiableViewModel( - getIt.get(), - walletName: args.walletName ?? currentWalletName, - walletType: args.walletType ?? currentWalletType); + return WalletUnlockVerifiableViewModel(getIt.get(), + walletName: args.walletName ?? currentWalletName, + walletType: args.walletType ?? currentWalletType); }); - getIt.registerFactoryParam((WalletType type, _) => - WalletRestorationFromQRVM( + getIt.registerFactoryParam( + (WalletType type, _) => WalletRestorationFromQRVM( getIt.get(), getIt.get(param1: type), _walletInfoSource, type, getIt.get())); - getIt.registerFactoryParam((type, _) => - WalletHardwareRestoreViewModel( + getIt.registerFactoryParam( + (type, _) => WalletHardwareRestoreViewModel( getIt.get(), getIt.get(), getIt.get(param1: type), @@ -484,10 +502,11 @@ Future setup({ getIt.get(), type: type)); - getIt.registerFactory(() => WalletAddressListViewModel( - appStore: getIt.get(), - yatStore: getIt.get(), - fiatConversionStore: getIt.get())); + getIt.registerFactory(() => + WalletAddressListViewModel( + appStore: getIt.get(), + yatStore: getIt.get(), + fiatConversionStore: getIt.get())); getIt.registerFactory(() => BalanceViewModel( appStore: getIt.get(), @@ -515,12 +534,16 @@ Future setup({ ), ); - getIt.registerFactory(() => AuthViewModel(getIt.get(), - getIt.get(), getIt.get(), BiometricAuth())); + getIt.registerFactory(() => AuthViewModel( + getIt.get(), + getIt.get(), + getIt.get(), + BiometricAuth())); - getIt.registerFactoryParam( - (onAuthFinished, closable) => AuthPage(getIt.get(), - onAuthenticationFinished: onAuthFinished, closable: closable)); + getIt + .registerFactoryParam( + (onAuthFinished, closable) => AuthPage(getIt.get(), + onAuthenticationFinished: onAuthFinished, closable: closable)); getIt.registerLazySingleton( () => Setup2FAViewModel( @@ -548,7 +571,8 @@ Future setup({ getIt.registerFactory(instanceName: 'login', () { return AuthPage(getIt.get(), closable: false, - onAuthenticationFinished: (isAuthenticated, AuthPageState authPageState) { + onAuthenticationFinished: + (isAuthenticated, AuthPageState authPageState) { if (!isAuthenticated) { return; } @@ -563,8 +587,8 @@ Future setup({ arguments: TotpAuthArgumentsModel( isForSetup: false, isClosable: false, - onTotpAuthenticationFinished: - (bool isAuthenticatedSuccessfully, TotpAuthCodePageState totpAuthPageState) async { + onTotpAuthenticationFinished: (bool isAuthenticatedSuccessfully, + TotpAuthCodePageState totpAuthPageState) async { if (!isAuthenticatedSuccessfully) { return; } @@ -576,7 +600,8 @@ Future setup({ totpAuthPageState.changeProcessText('Loading the wallet'); if (loginError != null) { - totpAuthPageState.changeProcessText('ERROR: ${loginError.toString()}'.trim()); + totpAuthPageState.changeProcessText( + 'ERROR: ${loginError.toString()}'.trim()); } ReactionDisposer? _reaction; @@ -605,7 +630,8 @@ Future setup({ authPageState.changeProcessText('Loading the wallet'); if (loginError != null) { - authPageState.changeProcessText('ERROR: ${loginError.toString()}'.trim()); + authPageState + .changeProcessText('ERROR: ${loginError.toString()}'.trim()); loginError = null; } @@ -625,7 +651,8 @@ Future setup({ } if (loginError != null) { - authPageState.changeProcessText('ERROR: ${loginError.toString()}'.trim()); + authPageState + .changeProcessText('ERROR: ${loginError.toString()}'.trim()); timer.cancel(); } }); @@ -640,8 +667,11 @@ Future setup({ getIt.registerLazySingleton(() => KeyServiceImpl()); getIt.registerLazySingleton(() { - final Web3WalletService web3WalletService = Web3WalletService(getIt.get(), - getIt.get(), appStore, getIt.get()); + final Web3WalletService web3WalletService = Web3WalletService( + getIt.get(), + getIt.get(), + appStore, + getIt.get()); web3WalletService.create(); return web3WalletService; }); @@ -668,7 +698,8 @@ Future setup({ desktopNavigatorKey: _navigatorKey, ); }); - getIt.registerFactoryParam, void>( + getIt.registerFactoryParam< + DesktopDashboardPage, GlobalKey, void>( (desktopKey, _) => DesktopDashboardPage( balancePage: getIt.get(), dashboardViewModel: getIt.get(), @@ -676,8 +707,8 @@ Future setup({ desktopKey: desktopKey, )); - getIt.registerFactory( - () => TransactionsPage(dashboardViewModel: getIt.get())); + getIt.registerFactory(() => + TransactionsPage(dashboardViewModel: getIt.get())); getIt.registerFactory(() => Setup2FAInfoPage()); @@ -694,9 +725,11 @@ Future setup({ () => DesktopSettingsPage(getIt.get())); getIt.registerFactoryParam( - (pageOption, _) => ReceiveOptionViewModel(getIt.get().wallet!, pageOption)); + (pageOption, _) => + ReceiveOptionViewModel(getIt.get().wallet!, pageOption)); - getIt.registerFactoryParam, void>((args, _) { + getIt.registerFactoryParam, void>( + (args, _) { final address = args.first as String; final pageOption = args.last as ReceivePageOption; return AnonInvoicePageViewModel( @@ -710,25 +743,27 @@ Future setup({ ); }); - getIt.registerFactoryParam, void>((List args, _) { + getIt.registerFactoryParam, void>( + (List args, _) { final pageOption = args.last as ReceivePageOption; return AnonPayInvoicePage(getIt.get(param1: args), getIt.get(param1: pageOption)); }); - getIt.registerFactory( - () => ReceivePage(addressListViewModel: getIt.get())); + getIt.registerFactory(() => ReceivePage( + addressListViewModel: getIt.get())); getIt.registerFactory(() => AddressPage( addressListViewModel: getIt.get(), dashboardViewModel: getIt.get(), receiveOptionViewModel: getIt.get())); - getIt.registerFactoryParam( - (WalletAddressListItem? item, _) => - WalletAddressEditOrCreateViewModel(wallet: getIt.get().wallet!, item: item)); + getIt.registerFactoryParam( + (WalletAddressListItem? item, _) => WalletAddressEditOrCreateViewModel( + wallet: getIt.get().wallet!, item: item)); - getIt.registerFactoryParam((dynamic item, _) => - AddressEditOrCreatePage( + getIt.registerFactoryParam( + (dynamic item, _) => AddressEditOrCreatePage( addressEditOrCreateViewModel: getIt.get(param1: item))); @@ -746,7 +781,9 @@ Future setup({ getIt.get(), getIt.get(), _transactionDescriptionBox, - getIt.get().wallet!.isHardwareWallet ? getIt.get() : null, + getIt.get().wallet!.isHardwareWallet + ? getIt.get() + : null, coinTypeToSpendFrom: coinTypeToSpendFrom ?? UnspentCoinType.any, getIt.get(param1: coinTypeToSpendFrom), ), @@ -754,13 +791,14 @@ Future setup({ getIt.registerFactoryParam( (PaymentRequest? initialPaymentRequest, coinTypeToSpendFrom) => SendPage( - sendViewModel: getIt.get(param1: coinTypeToSpendFrom), + sendViewModel: + getIt.get(param1: coinTypeToSpendFrom), authService: getIt.get(), initialPaymentRequest: initialPaymentRequest, )); - getIt.registerFactory( - () => SendTemplatePage(sendTemplateViewModel: getIt.get())); + getIt.registerFactory(() => SendTemplatePage( + sendTemplateViewModel: getIt.get())); if (DeviceInfo.instance.isMobile) { getIt.registerFactory( @@ -799,11 +837,12 @@ Future setup({ ), ); - getIt.registerFactoryParam((arguments, _) { - + getIt.registerFactoryParam( + (arguments, _) { return WalletEditPage( pageArguments: WalletEditPageArguments( - walletEditViewModel: getIt.get(param1: arguments.walletListViewModel), + walletEditViewModel: getIt.get( + param1: arguments.walletListViewModel), authService: getIt.get(), walletNewVM: getIt.get( param1: NewWalletArguments(type: arguments.editingWallet.type), @@ -837,11 +876,11 @@ Future setup({ 'Unexpected wallet type: ${wallet.type} for generate Monero AccountListViewModel'); }); - getIt.registerFactory( - () => MoneroAccountListPage(accountListViewModel: getIt.get())); + getIt.registerFactory(() => MoneroAccountListPage( + accountListViewModel: getIt.get())); - getIt.registerFactory( - () => NanoAccountListPage(accountListViewModel: getIt.get())); + getIt.registerFactory(() => NanoAccountListPage( + accountListViewModel: getIt.get())); /*getIt.registerFactory(() { final wallet = getIt.get().wallet; @@ -858,7 +897,8 @@ Future setup({ moneroAccountCreationViewModel: getIt.get()));*/ - getIt.registerFactoryParam( + getIt.registerFactoryParam( (AccountListItem? account, _) => MoneroAccountEditOrCreateViewModel( monero!.getAccountList(getIt.get().wallet!), wownero?.getAccountList(getIt.get().wallet!), @@ -867,17 +907,19 @@ Future setup({ wallet: getIt.get().wallet!, accountListItem: account)); - getIt.registerFactoryParam( + getIt.registerFactoryParam( (AccountListItem? account, _) => MoneroAccountEditOrCreatePage( moneroAccountCreationViewModel: getIt.get(param1: account))); - getIt.registerFactoryParam( - (NanoAccount? account, _) => - NanoAccountEditOrCreateViewModel(nano!.getAccountList(getIt.get().wallet!), - // banano?.getAccountList(getIt.get().wallet!), - wallet: getIt.get().wallet!, - accountListItem: account)); + getIt.registerFactoryParam( + (NanoAccount? account, _) => NanoAccountEditOrCreateViewModel( + nano!.getAccountList(getIt.get().wallet!), + // banano?.getAccountList(getIt.get().wallet!), + wallet: getIt.get().wallet!, + accountListItem: account)); getIt.registerFactoryParam( (NanoAccount? account, _) => NanoAccountEditOrCreatePage( @@ -888,56 +930,68 @@ Future setup({ return DisplaySettingsViewModel(getIt.get()); }); - getIt.registerFactory(() => - SilentPaymentsSettingsViewModel(getIt.get(), getIt.get().wallet!)); + getIt.registerFactory(() => SilentPaymentsSettingsViewModel( + getIt.get(), getIt.get().wallet!)); - getIt.registerFactory( - () => MwebSettingsViewModel(getIt.get(), getIt.get().wallet!)); + getIt.registerFactory(() => MwebSettingsViewModel( + getIt.get(), getIt.get().wallet!)); getIt.registerFactory(() { - return PrivacySettingsViewModel(getIt.get(), getIt.get().wallet!); + return PrivacySettingsViewModel( + getIt.get(), getIt.get().wallet!); }); - getIt.registerFactory(() => TrocadorProvidersViewModel(getIt.get())); + getIt.registerFactory( + () => TrocadorProvidersViewModel(getIt.get())); getIt.registerFactory(() { - return OtherSettingsViewModel(getIt.get(), getIt.get().wallet!, - getIt.get());}); + return OtherSettingsViewModel(getIt.get(), + getIt.get().wallet!, getIt.get()); + }); getIt.registerFactory(() { return SecuritySettingsViewModel(getIt.get()); }); - getIt.registerFactory(() => WalletSeedViewModel(getIt.get().wallet!)); + getIt.registerFactory( + () => WalletSeedViewModel(getIt.get().wallet!)); - getIt.registerFactory(() => SeedSettingsViewModel(getIt.get(), getIt.get())); + getIt.registerFactory(() => SeedSettingsViewModel( + getIt.get(), getIt.get())); - getIt.registerFactoryParam((bool isWalletCreated, _) => - WalletSeedPage(getIt.get(), isNewWalletCreated: isWalletCreated)); + getIt.registerFactoryParam( + (bool isWalletCreated, _) => WalletSeedPage( + getIt.get(), + isNewWalletCreated: isWalletCreated)); getIt.registerFactory(() => WalletKeysViewModel(getIt.get())); getIt.registerFactory(() => WalletKeysPage(getIt.get())); - + getIt.registerFactory(() => AnimatedURModel(getIt.get())); getIt.registerFactoryParam((String urQr, _) => - AnimatedURPage(getIt.get(), urQr: urQr)); + AnimatedURPage(getIt.get(), urQr: urQr)); getIt.registerFactoryParam( - (ContactRecord? contact, _) => ContactViewModel(_contactSource, contact: contact)); + (ContactRecord? contact, _) => + ContactViewModel(_contactSource, contact: contact)); getIt.registerFactoryParam( - (CryptoCurrency? cur, _) => - ContactListViewModel(_contactSource, _walletInfoSource, cur, getIt.get())); + (CryptoCurrency? cur, _) => ContactListViewModel( + _contactSource, _walletInfoSource, cur, getIt.get())); - getIt.registerFactoryParam((CryptoCurrency? cur, _) => - ContactListPage(getIt.get(param1: cur), getIt.get())); + getIt.registerFactoryParam( + (CryptoCurrency? cur, _) => ContactListPage( + getIt.get(param1: cur), + getIt.get())); getIt.registerFactoryParam( - (ContactRecord? contact, _) => ContactPage(getIt.get(param1: contact))); + (ContactRecord? contact, _) => + ContactPage(getIt.get(param1: contact))); - getIt.registerFactory(() => AddressListPage(getIt.get())); + getIt.registerFactory( + () => AddressListPage(getIt.get())); getIt.registerFactory(() { final appStore = getIt.get(); @@ -949,29 +1003,38 @@ Future setup({ return PowNodeListViewModel(_powNodeSource, appStore); }); - getIt.registerFactory(() => ConnectionSyncPage(getIt.get())); - - getIt.registerFactory(() => SecurityBackupPage(getIt.get(), - getIt.get(), getIt.get().wallet!.isHardwareWallet)); + getIt.registerFactory( + () => ConnectionSyncPage(getIt.get())); - getIt.registerFactory(() => PrivacyPage(getIt.get())); + getIt.registerFactory(() => SecurityBackupPage( + getIt.get(), + getIt.get(), + getIt.get().wallet!.isHardwareWallet)); - getIt.registerFactory(() => TrocadorProvidersPage(getIt.get())); + getIt.registerFactory( + () => PrivacyPage(getIt.get())); - getIt.registerFactory(() => DomainLookupsPage(getIt.get())); + getIt.registerFactory( + () => TrocadorProvidersPage(getIt.get())); - getIt.registerFactory(() => DisplaySettingsPage(getIt.get())); + getIt.registerFactory( + () => DomainLookupsPage(getIt.get())); getIt.registerFactory( - () => SilentPaymentsSettingsPage(getIt.get())); + () => DisplaySettingsPage(getIt.get())); - getIt.registerFactory(() => MwebSettingsPage(getIt.get())); + getIt.registerFactory(() => + SilentPaymentsSettingsPage(getIt.get())); + + getIt.registerFactory( + () => MwebSettingsPage(getIt.get())); getIt.registerFactory(() => MwebLogsPage(getIt.get())); getIt.registerFactory(() => MwebNodePage(getIt.get())); - getIt.registerFactory(() => OtherSettingsPage(getIt.get())); + getIt.registerFactory( + () => OtherSettingsPage(getIt.get())); getIt.registerFactory(() => NanoChangeRepPage( settingsStore: getIt.get().settingsStore, @@ -986,25 +1049,29 @@ Future setup({ getIt.registerFactoryParam( (Node? editingNode, bool? isSelected) => NodeCreateOrEditPage( - nodeCreateOrEditViewModel: getIt.get(param2: false), + nodeCreateOrEditViewModel: + getIt.get(param2: false), editingNode: editingNode, isSelected: isSelected)); getIt.registerFactoryParam( (Node? editingNode, bool? isSelected) => PowNodeCreateOrEditPage( - nodeCreateOrEditViewModel: getIt.get(param2: true), + nodeCreateOrEditViewModel: + getIt.get(param2: true), editingNode: editingNode, isSelected: isSelected)); getIt.registerFactory(() => RobinhoodBuyProvider( wallet: getIt.get().wallet!, - ledgerVM: - getIt.get().wallet!.isHardwareWallet ? getIt.get() : null)); + ledgerVM: getIt.get().wallet!.isHardwareWallet + ? getIt.get() + : null)); getIt.registerFactory(() => DFXBuyProvider( wallet: getIt.get().wallet!, - ledgerVM: - getIt.get().wallet!.isHardwareWallet ? getIt.get() : null)); + ledgerVM: getIt.get().wallet!.isHardwareWallet + ? getIt.get() + : null)); getIt.registerFactory(() => MoonPayProvider( settingsStore: getIt.get().settingsStore, @@ -1018,10 +1085,11 @@ Future setup({ )); getIt.registerFactory(() => MeldBuyProvider( - wallet: getIt.get().wallet!, - )); + wallet: getIt.get().wallet!, + )); - getIt.registerFactoryParam((title, uri) => WebViewPage(title, uri)); + getIt.registerFactoryParam( + (title, uri) => WebViewPage(title, uri)); getIt.registerFactory(() => PayfuraBuyProvider( settingsStore: getIt.get().settingsStore, @@ -1045,24 +1113,30 @@ Future setup({ getIt.registerFactoryParam( (PaymentRequest? paymentRequest, __) { - return ExchangePage(getIt.get(), getIt.get(), paymentRequest); + return ExchangePage(getIt.get(), + getIt.get(), paymentRequest); }); - getIt.registerFactory(() => ExchangeConfirmPage(tradesStore: getIt.get())); - getIt.registerFactory( - () => ExchangeTradePage(exchangeTradeViewModel: getIt.get())); + () => ExchangeConfirmPage(tradesStore: getIt.get())); + + getIt.registerFactory(() => ExchangeTradePage( + exchangeTradeViewModel: getIt.get())); - getIt.registerFactory(() => ExchangeTemplatePage(getIt.get())); + getIt.registerFactory( + () => ExchangeTemplatePage(getIt.get())); - getIt.registerFactoryParam((WalletType param1, __) { + getIt.registerFactoryParam( + (WalletType param1, __) { switch (param1) { case WalletType.haven: return haven!.createHavenWalletService(_walletInfoSource); case WalletType.salvium: - return salvium!.createSalviumWalletService(_walletInfoSource); + return salvium!.createSalviumWalletService( + _walletInfoSource, _unspentCoinsInfoSource); case WalletType.monero: - return monero!.createMoneroWalletService(_walletInfoSource, _unspentCoinsInfoSource); + return monero!.createMoneroWalletService( + _walletInfoSource, _unspentCoinsInfoSource); case WalletType.bitcoin: return bitcoin!.createBitcoinWalletService( _walletInfoSource, @@ -1081,11 +1155,14 @@ Future setup({ return ethereum!.createEthereumWalletService( _walletInfoSource, SettingsStoreBase.walletPasswordDirectInput); case WalletType.bitcoinCash: - return bitcoinCash!.createBitcoinCashWalletService(_walletInfoSource, - _unspentCoinsInfoSource, SettingsStoreBase.walletPasswordDirectInput); + return bitcoinCash!.createBitcoinCashWalletService( + _walletInfoSource, + _unspentCoinsInfoSource, + SettingsStoreBase.walletPasswordDirectInput); case WalletType.nano: case WalletType.banano: - return nano!.createNanoWalletService(_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput); + return nano!.createNanoWalletService( + _walletInfoSource, SettingsStoreBase.walletPasswordDirectInput); case WalletType.polygon: return polygon!.createPolygonWalletService( _walletInfoSource, SettingsStoreBase.walletPasswordDirectInput); @@ -1093,20 +1170,24 @@ Future setup({ return solana!.createSolanaWalletService( _walletInfoSource, SettingsStoreBase.walletPasswordDirectInput); case WalletType.tron: - return tron!.createTronWalletService(_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput); + return tron!.createTronWalletService( + _walletInfoSource, SettingsStoreBase.walletPasswordDirectInput); case WalletType.wownero: - return wownero!.createWowneroWalletService(_walletInfoSource, _unspentCoinsInfoSource); + return wownero!.createWowneroWalletService( + _walletInfoSource, _unspentCoinsInfoSource); case WalletType.none: - throw Exception('Unexpected token: ${param1.toString()} for generating of WalletService'); + throw Exception( + 'Unexpected token: ${param1.toString()} for generating of WalletService'); } }); - getIt.registerFactory( - () => SetupPinCodeViewModel(getIt.get(), getIt.get())); + getIt.registerFactory(() => SetupPinCodeViewModel( + getIt.get(), getIt.get())); - getIt.registerFactoryParam, String), - void>( - (onSuccessfulPinSetup, _) => SetupPinCodePage(getIt.get(), + getIt.registerFactoryParam, String), void>( + (onSuccessfulPinSetup, _) => SetupPinCodePage( + getIt.get(), onSuccessfulPinSetup: onSuccessfulPinSetup)); getIt.registerFactory(() => WelcomePage()); @@ -1117,57 +1198,62 @@ Future setup({ getIt.registerFactory(() => FaqPage(getIt.get())); - getIt.registerFactoryParam((type, _) => - WalletRestoreViewModel(getIt.get(), getIt.get(param1: type), - _walletInfoSource, getIt.get(), + getIt.registerFactoryParam( + (type, _) => WalletRestoreViewModel( + getIt.get(), + getIt.get(param1: type), + _walletInfoSource, + getIt.get(), type: type)); - getIt.registerFactoryParam((type, _) => WalletRestorePage( - getIt.get(param1: type), getIt.get())); - - getIt.registerFactoryParam, void>( - (derivations, _) => WalletRestoreChooseDerivationViewModel(derivationInfos: derivations)); + getIt.registerFactoryParam((type, _) => + WalletRestorePage(getIt.get(param1: type), + getIt.get())); - getIt.registerFactoryParam, void>( + getIt.registerFactoryParam, void>( (derivations, _) => - WalletRestoreChooseDerivationPage(getIt.get( - param1: derivations, - ))); + WalletRestoreChooseDerivationViewModel(derivationInfos: derivations)); + + getIt.registerFactoryParam< + WalletRestoreChooseDerivationPage, + List, + void>((derivations, _) => WalletRestoreChooseDerivationPage( + getIt.get( + param1: derivations, + ))); getIt.registerFactoryParam, void>( - (params, _) { - final transactionInfo = params[0] as TransactionInfo; - final canReplaceByFee = params[1] as bool? ?? false; - final wallet = getIt.get().wallet!; - - return TransactionDetailsViewModel( - transactionInfo: transactionInfo, - transactionDescriptionBox: _transactionDescriptionBox, - wallet: wallet, - settingsStore: getIt.get(), - sendViewModel: getIt.get(), - canReplaceByFee: canReplaceByFee, - ); - } - ); + (params, _) { + final transactionInfo = params[0] as TransactionInfo; + final canReplaceByFee = params[1] as bool? ?? false; + final wallet = getIt.get().wallet!; + + return TransactionDetailsViewModel( + transactionInfo: transactionInfo, + transactionDescriptionBox: _transactionDescriptionBox, + wallet: wallet, + settingsStore: getIt.get(), + sendViewModel: getIt.get(), + canReplaceByFee: canReplaceByFee, + ); + }); getIt.registerFactoryParam( - (TransactionInfo transactionInfo, _) => TransactionDetailsPage( + (TransactionInfo transactionInfo, _) => TransactionDetailsPage( transactionDetailsViewModel: getIt.get( param1: [transactionInfo, false]))); - getIt.registerFactoryParam, void>( - (params, _) { - final transactionInfo = params[0] as TransactionInfo; - final txHex = params[1] as String; - return RBFDetailsPage( - transactionDetailsViewModel: getIt.get( - param1: [transactionInfo, true], - ), - rawTransaction: txHex, - ); - } - ); + getIt.registerFactoryParam, void>((params, _) { + final transactionInfo = params[0] as TransactionInfo; + final txHex = params[1] as String; + return RBFDetailsPage( + transactionDetailsViewModel: getIt.get( + param1: [transactionInfo, true], + ), + rawTransaction: txHex, + ); + }); getIt.registerFactoryParam( (newWalletTypeArguments, _) { @@ -1186,31 +1272,38 @@ Future setup({ trades: _tradesSource, settingsStore: getIt.get())); - getIt.registerFactory(() => CakeFeaturesViewModel(getIt.get())); + getIt.registerFactory( + () => CakeFeaturesViewModel(getIt.get())); - getIt.registerFactory(() => BackupService(getIt.get(), _walletInfoSource, + getIt.registerFactory(() => BackupService( + getIt.get(), + _walletInfoSource, _transactionDescriptionBox, - getIt.get(), getIt.get())); + getIt.get(), + getIt.get())); - getIt.registerFactory(() => BackupViewModel( - getIt.get(), getIt.get(), getIt.get())); + getIt.registerFactory(() => BackupViewModel(getIt.get(), + getIt.get(), getIt.get())); getIt.registerFactory(() => BackupPage(getIt.get())); - getIt.registerFactory( - () => EditBackupPasswordViewModel(getIt.get(), getIt.get())); + getIt.registerFactory(() => EditBackupPasswordViewModel( + getIt.get(), getIt.get())); - getIt.registerFactory(() => EditBackupPasswordPage(getIt.get())); + getIt.registerFactory( + () => EditBackupPasswordPage(getIt.get())); getIt.registerFactoryParam( (bool isNewInstall, _) => RestoreOptionsPage(isNewInstall: isNewInstall)); - getIt.registerFactory(() => RestoreFromBackupViewModel(getIt.get())); + getIt.registerFactory( + () => RestoreFromBackupViewModel(getIt.get())); - getIt.registerFactory(() => RestoreFromBackupPage(getIt.get())); + getIt.registerFactory( + () => RestoreFromBackupPage(getIt.get())); - getIt.registerFactoryParam( - (Trade trade, _) => TradeDetailsPage(getIt.get(param1: trade))); + getIt.registerFactoryParam((Trade trade, _) => + TradeDetailsPage(getIt.get(param1: trade))); getIt.registerFactory(() => BuyAmountViewModel()); @@ -1218,7 +1311,8 @@ Future setup({ getIt.registerFactory(() => BuySellPage(getIt.get())); - getIt.registerFactoryParam, void>((List args, _) { + getIt.registerFactoryParam, void>( + (List args, _) { final items = args.first as List; final pickAnOption = args[1] as void Function(SelectableOption option)?; final confirmOption = args[2] as void Function(BuildContext contex)?; @@ -1226,28 +1320,31 @@ Future setup({ items: items, pickAnOption: pickAnOption, confirmOption: confirmOption); }); - getIt.registerFactoryParam, void>((List args, _) { + getIt.registerFactoryParam, void>( + (List args, _) { final items = args.first as List; final pickAnOption = args[1] as void Function(SelectableOption option)?; - return PaymentMethodOptionsPage( - items: items, pickAnOption: pickAnOption); + return PaymentMethodOptionsPage(items: items, pickAnOption: pickAnOption); }); getIt.registerFactory(() { final wallet = getIt.get().wallet; - return BuyViewModel(_ordersSource, getIt.get(), getIt.get(), - getIt.get(), + return BuyViewModel(_ordersSource, getIt.get(), + getIt.get(), getIt.get(), wallet: wallet!); }); - getIt.registerFactoryParam, void>((List args, _) { + getIt.registerFactoryParam, void>( + (List args, _) { final url = args.first as String; final buyViewModel = args[1] as BuyViewModel; return BuyWebViewPage( - buyViewModel: buyViewModel, ordersStore: getIt.get(), url: url); + buyViewModel: buyViewModel, + ordersStore: getIt.get(), + url: url); }); getIt.registerFactoryParam((order, _) { @@ -1256,17 +1353,18 @@ Future setup({ return OrderDetailsViewModel(wallet: wallet!, orderForDetails: order); }); - getIt.registerFactoryParam( - (Order order, _) => OrderDetailsPage(getIt.get(param1: order))); + getIt.registerFactoryParam((Order order, _) => + OrderDetailsPage(getIt.get(param1: order))); getIt.registerFactory(() => SupportViewModel()); getIt.registerFactory(() => SupportPage(getIt.get())); - getIt.registerFactory(() => - SupportChatPage(getIt.get(), secureStorage: getIt.get())); + getIt.registerFactory(() => SupportChatPage(getIt.get(), + secureStorage: getIt.get())); - getIt.registerFactory(() => SupportOtherLinksPage(getIt.get())); + getIt.registerFactory( + () => SupportOtherLinksPage(getIt.get())); getIt.registerFactoryParam( (coinTypeToSpendFrom, _) { @@ -1281,15 +1379,16 @@ Future setup({ getIt.registerFactoryParam( (coinTypeToSpendFrom, _) => UnspentCoinsListPage( - unspentCoinsListViewModel: - getIt.get(param1: coinTypeToSpendFrom))); + unspentCoinsListViewModel: getIt.get( + param1: coinTypeToSpendFrom))); getIt.registerFactoryParam( - (item, model) => - UnspentCoinsDetailsViewModel(unspentCoinsItem: item, unspentCoinsListViewModel: model)); + (item, model) => UnspentCoinsDetailsViewModel( + unspentCoinsItem: item, unspentCoinsListViewModel: model)); - getIt.registerFactoryParam, void>((List args, _) { + getIt.registerFactoryParam, void>( + (List args, _) { final item = args.first as UnspentCoinsItem; final unspentCoinsListViewModel = args[1] as UnspentCoinsListViewModel; @@ -1312,17 +1411,18 @@ Future setup({ getIt.registerFactory(() => AnyPayApi()); - getIt.registerFactory( - () => CakePayService(getIt.get(), getIt.get())); + getIt.registerFactory(() => + CakePayService(getIt.get(), getIt.get())); - getIt.registerFactory( - () => CakePayCardsListViewModel(cakePayService: getIt.get(), - settingsStore: getIt.get())); + getIt.registerFactory(() => CakePayCardsListViewModel( + cakePayService: getIt.get(), + settingsStore: getIt.get())); - getIt.registerFactory(() => CakePayAuthViewModel(cakePayService: getIt.get())); + getIt.registerFactory( + () => CakePayAuthViewModel(cakePayService: getIt.get())); - getIt.registerFactoryParam( - (PaymentCredential paymentCredential, CakePayCard card) { + getIt.registerFactoryParam((PaymentCredential paymentCredential, CakePayCard card) { return CakePayPurchaseViewModel( cakePayService: getIt.get(), paymentCredential: paymentCredential, @@ -1335,42 +1435,51 @@ Future setup({ return CakePayBuyCardViewModel(vendor: vendor); }); - getIt.registerFactory(() => CakePayAccountViewModel(cakePayService: getIt.get())); + getIt.registerFactory(() => + CakePayAccountViewModel(cakePayService: getIt.get())); - getIt.registerFactory(() => CakePayWelcomePage(getIt.get())); + getIt.registerFactory( + () => CakePayWelcomePage(getIt.get())); - getIt.registerFactoryParam, void>((List args, _) { + getIt.registerFactoryParam, void>( + (List args, _) { final email = args.first as String; final isSignIn = args[1] as bool; - return CakePayVerifyOtpPage(getIt.get(), email, isSignIn); + return CakePayVerifyOtpPage( + getIt.get(), email, isSignIn); }); - getIt.registerFactoryParam, void>((List args, _) { + getIt.registerFactoryParam, void>( + (List args, _) { final vendor = args.first as CakePayVendor; return CakePayBuyCardPage( - getIt.get(param1: vendor), getIt.get()); + getIt.get(param1: vendor), + getIt.get()); }); - getIt - .registerFactoryParam, void>((List args, _) { + getIt.registerFactoryParam, void>( + (List args, _) { final paymentCredential = args.first as PaymentCredential; final card = args[1] as CakePayCard; - return CakePayBuyCardDetailPage( - getIt.get(param1: paymentCredential, param2: card)); + return CakePayBuyCardDetailPage(getIt.get( + param1: paymentCredential, param2: card)); }); - getIt.registerFactory(() => CakePayCardsPage(getIt.get())); + getIt.registerFactory( + () => CakePayCardsPage(getIt.get())); - getIt.registerFactory(() => CakePayAccountPage(getIt.get())); + getIt.registerFactory( + () => CakePayAccountPage(getIt.get())); getIt.registerFactory(() => AnonPayApi( - useTorOnly: getIt.get().exchangeStatus == ExchangeApiMode.torOnly, + useTorOnly: + getIt.get().exchangeStatus == ExchangeApiMode.torOnly, wallet: getIt.get().wallet!)); - getIt.registerFactory(() => - DesktopWalletSelectionDropDown(getIt.get(), getIt.get())); + getIt.registerFactory(() => DesktopWalletSelectionDropDown( + getIt.get(), getIt.get())); getIt.registerFactory(() => DesktopSidebarViewModel()); @@ -1387,15 +1496,19 @@ Future setup({ getIt.registerFactoryParam( (AnonpayInvoiceInfo anonpayInvoiceInfo, _) => AnonpayDetailsPage( - anonpayDetailsViewModel: getIt.get(param1: anonpayInvoiceInfo))); + anonpayDetailsViewModel: + getIt.get(param1: anonpayInvoiceInfo))); - getIt.registerFactoryParam((balanceViewModel, _) => - HomeSettingsPage(getIt.get(param1: balanceViewModel))); + getIt.registerFactoryParam( + (balanceViewModel, _) => HomeSettingsPage( + getIt.get(param1: balanceViewModel))); getIt.registerFactoryParam( - (balanceViewModel, _) => HomeSettingsViewModel(getIt.get(), balanceViewModel)); + (balanceViewModel, _) => + HomeSettingsViewModel(getIt.get(), balanceViewModel)); - getIt.registerFactoryParam>( + getIt.registerFactoryParam>( (homeSettingsViewModel, arguments) => EditTokenPage( homeSettingsViewModel: homeSettingsViewModel, token: arguments['token'] as CryptoCurrency?, @@ -1405,15 +1518,18 @@ Future setup({ getIt.registerFactoryParam((bool isPow, _) { if (isPow) { - return ManageNodesPage(isPow, powNodeListViewModel: getIt.get()); + return ManageNodesPage(isPow, + powNodeListViewModel: getIt.get()); } - return ManageNodesPage(isPow, nodeListViewModel: getIt.get()); + return ManageNodesPage(isPow, + nodeListViewModel: getIt.get()); }); - getIt.registerFactory( - () => WalletConnectConnectionsView(web3walletService: getIt.get())); + getIt.registerFactory(() => WalletConnectConnectionsView( + web3walletService: getIt.get())); - getIt.registerFactory(() => NFTViewModel(appStore, getIt.get())); + getIt.registerFactory( + () => NFTViewModel(appStore, getIt.get())); getIt.registerFactory(() => TorPage(getIt.get())); getIt.registerFactory(() => SignViewModel(getIt.get().wallet!)); diff --git a/lib/entities/update_salvium_rate.dart b/lib/entities/update_salvium_rate.dart deleted file mode 100644 index ed76015efa..0000000000 --- a/lib/entities/update_salvium_rate.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; -import 'package:cw_core/crypto_currency.dart'; -import 'package:cw_core/monero_amount_format.dart'; -import 'package:cake_wallet/salvium/salvium.dart'; - -Future updateSalviumRate(FiatConversionStore fiatConversionStore) async { - try { - final rate = salvium!.getAssetRate(); - final base = rate.firstWhere((row) => row.asset == 'XUSD'); - - rate.forEach((row) { - final cur = CryptoCurrency.fromString(row.asset); - final baseRate = moneroAmountToDouble(amount: base.rate); - final rowRate = moneroAmountToDouble(amount: row.rate); - - if (cur == CryptoCurrency.xusd) { - fiatConversionStore.prices[cur] = 1.0; - return; - } - - fiatConversionStore.prices[cur] = baseRate / rowRate; - }); - } catch(_) { - // FIX-ME: handle exception - } -} \ No newline at end of file diff --git a/lib/reactions/fiat_rate_update.dart b/lib/reactions/fiat_rate_update.dart index 682944253b..708a78d2fa 100644 --- a/lib/reactions/fiat_rate_update.dart +++ b/lib/reactions/fiat_rate_update.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:cake_wallet/core/fiat_conversion_service.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/update_haven_rate.dart'; -import 'package:cake_wallet/entities/update_salvium_rate.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/solana/solana.dart'; @@ -18,22 +17,21 @@ import 'package:mobx/mobx.dart'; Timer? _timer; -Future startFiatRateUpdate( - AppStore appStore, SettingsStore settingsStore, FiatConversionStore fiatConversionStore) async { +Future startFiatRateUpdate(AppStore appStore, SettingsStore settingsStore, + FiatConversionStore fiatConversionStore) async { if (_timer != null) { return; } final _updateFiat = (_) async { try { - if (appStore.wallet == null || settingsStore.fiatApiMode == FiatApiMode.disabled) { + if (appStore.wallet == null || + settingsStore.fiatApiMode == FiatApiMode.disabled) { return; } if (appStore.wallet!.type == WalletType.haven) { await updateHavenRate(fiatConversionStore); - } else if (appStore.wallet!.type == WalletType.salvium) { - await updateSalviumRate(fiatConversionStore); } else { fiatConversionStore.prices[appStore.wallet!.currency] = await FiatConversionService.fetchPrice( @@ -44,33 +42,37 @@ Future startFiatRateUpdate( Iterable? currencies; if (appStore.wallet!.type == WalletType.ethereum) { - currencies = - ethereum!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled); + currencies = ethereum! + .getERC20Currencies(appStore.wallet!) + .where((element) => element.enabled); } if (appStore.wallet!.type == WalletType.polygon) { - currencies = - polygon!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled); + currencies = polygon! + .getERC20Currencies(appStore.wallet!) + .where((element) => element.enabled); } if (appStore.wallet!.type == WalletType.solana) { - currencies = - solana!.getSPLTokenCurrencies(appStore.wallet!).where((element) => element.enabled); + currencies = solana! + .getSPLTokenCurrencies(appStore.wallet!) + .where((element) => element.enabled); } if (appStore.wallet!.type == WalletType.tron) { - currencies = - tron!.getTronTokenCurrencies(appStore.wallet!).where((element) => element.enabled); + currencies = tron! + .getTronTokenCurrencies(appStore.wallet!) + .where((element) => element.enabled); } - if (currencies != null) { for (final currency in currencies) { () async { - fiatConversionStore.prices[currency] = await FiatConversionService.fetchPrice( - crypto: currency, - fiat: settingsStore.fiatCurrency, - torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly); + fiatConversionStore.prices[currency] = + await FiatConversionService.fetchPrice( + crypto: currency, + fiat: settingsStore.fiatCurrency, + torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly); }.call(); } } diff --git a/lib/reactions/on_current_wallet_change.dart b/lib/reactions/on_current_wallet_change.dart index e5183e37c4..198509dff7 100644 --- a/lib/reactions/on_current_wallet_change.dart +++ b/lib/reactions/on_current_wallet_change.dart @@ -1,7 +1,6 @@ import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/update_haven_rate.dart'; -import 'package:cake_wallet/entities/update_salvium_rate.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/solana/solana.dart'; @@ -25,8 +24,8 @@ ReactionDisposer? _onCurrentWalletChangeReaction; ReactionDisposer? _onCurrentWalletChangeFiatRateUpdateReaction; //ReactionDisposer _onCurrentWalletAddressChangeReaction; -void startCurrentWalletChangeReaction( - AppStore appStore, SettingsStore settingsStore, FiatConversionStore fiatConversionStore) { +void startCurrentWalletChangeReaction(AppStore appStore, + SettingsStore settingsStore, FiatConversionStore fiatConversionStore) { _onCurrentWalletChangeReaction?.reaction.dispose(); _onCurrentWalletChangeFiatRateUpdateReaction?.reaction.dispose(); //_onCurrentWalletAddressChangeReaction?.reaction?dispose(); @@ -52,9 +51,9 @@ void startCurrentWalletChangeReaction( //} //}); - _onCurrentWalletChangeReaction = reaction((_) => appStore.wallet, - (WalletBase, TransactionInfo>? - wallet) async { + _onCurrentWalletChangeReaction = reaction((_) => appStore.wallet, (WalletBase< + Balance, TransactionHistoryBase, TransactionInfo>? + wallet) async { try { if (wallet == null) { return; @@ -69,6 +68,7 @@ void startCurrentWalletChangeReaction( if (wallet.type == WalletType.monero || wallet.type == WalletType.wownero || + wallet.type == WalletType.salvium || wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin || wallet.type == WalletType.bitcoinCash) { @@ -85,10 +85,6 @@ void startCurrentWalletChangeReaction( await updateHavenRate(fiatConversionStore); } - if (wallet.type == WalletType.salvium) { - await updateSalviumRate(fiatConversionStore); - } - if (wallet.walletInfo.address.isEmpty) { wallet.walletInfo.address = wallet.walletAddresses.address; @@ -101,8 +97,9 @@ void startCurrentWalletChangeReaction( } }); - _onCurrentWalletChangeFiatRateUpdateReaction = reaction((_) => appStore.wallet, - (WalletBase, TransactionInfo>? + _onCurrentWalletChangeFiatRateUpdateReaction = + reaction((_) => appStore.wallet, (WalletBase, TransactionInfo>? wallet) async { try { if (wallet == null || settingsStore.fiatApiMode == FiatApiMode.disabled) { @@ -110,36 +107,42 @@ void startCurrentWalletChangeReaction( } fiatConversionStore.prices[wallet.currency] = 0; - fiatConversionStore.prices[wallet.currency] = await FiatConversionService.fetchPrice( - crypto: wallet.currency, - fiat: settingsStore.fiatCurrency, - torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly); + fiatConversionStore.prices[wallet.currency] = + await FiatConversionService.fetchPrice( + crypto: wallet.currency, + fiat: settingsStore.fiatCurrency, + torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly); Iterable? currencies; if (wallet.type == WalletType.ethereum) { - currencies = - ethereum!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled); + currencies = ethereum! + .getERC20Currencies(appStore.wallet!) + .where((element) => element.enabled); } if (wallet.type == WalletType.polygon) { - currencies = - polygon!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled); + currencies = polygon! + .getERC20Currencies(appStore.wallet!) + .where((element) => element.enabled); } if (wallet.type == WalletType.solana) { - currencies = - solana!.getSPLTokenCurrencies(appStore.wallet!).where((element) => element.enabled); + currencies = solana! + .getSPLTokenCurrencies(appStore.wallet!) + .where((element) => element.enabled); } if (wallet.type == WalletType.tron) { - currencies = - tron!.getTronTokenCurrencies(appStore.wallet!).where((element) => element.enabled); + currencies = tron! + .getTronTokenCurrencies(appStore.wallet!) + .where((element) => element.enabled); } if (currencies != null) { for (final currency in currencies) { () async { - fiatConversionStore.prices[currency] = await FiatConversionService.fetchPrice( - crypto: currency, - fiat: settingsStore.fiatCurrency, - torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly); + fiatConversionStore.prices[currency] = + await FiatConversionService.fetchPrice( + crypto: currency, + fiat: settingsStore.fiatCurrency, + torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly); }.call(); } } @@ -150,10 +153,13 @@ void startCurrentWalletChangeReaction( } void _setAutoGenerateSubaddressStatus( - WalletBase, TransactionInfo> wallet, + WalletBase, TransactionInfo> + wallet, SettingsStore settingsStore, ) async { wallet.isEnabledAutoGenerateSubaddress = - settingsStore.autoGenerateSubaddressStatus == AutoGenerateSubaddressStatus.enabled || - settingsStore.autoGenerateSubaddressStatus == AutoGenerateSubaddressStatus.initialized; + settingsStore.autoGenerateSubaddressStatus == + AutoGenerateSubaddressStatus.enabled || + settingsStore.autoGenerateSubaddressStatus == + AutoGenerateSubaddressStatus.initialized; } diff --git a/lib/reactions/on_wallet_sync_status_change.dart b/lib/reactions/on_wallet_sync_status_change.dart index 8d28981a4e..5577a407a0 100644 --- a/lib/reactions/on_wallet_sync_status_change.dart +++ b/lib/reactions/on_wallet_sync_status_change.dart @@ -1,5 +1,4 @@ import 'package:cake_wallet/entities/update_haven_rate.dart'; -import 'package:cake_wallet/entities/update_salvium_rate.dart'; import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_core/wallet_type.dart'; @@ -14,10 +13,13 @@ import 'package:wakelock_plus/wakelock_plus.dart'; ReactionDisposer? _onWalletSyncStatusChangeReaction; void startWalletSyncStatusChangeReaction( - WalletBase, TransactionInfo> wallet, + WalletBase, + TransactionInfo> + wallet, FiatConversionStore fiatConversionStore) { _onWalletSyncStatusChangeReaction?.reaction.dispose(); - _onWalletSyncStatusChangeReaction = reaction((_) => wallet.syncStatus, (SyncStatus status) async { + _onWalletSyncStatusChangeReaction = + reaction((_) => wallet.syncStatus, (SyncStatus status) async { try { if (status is ConnectedSyncStatus) { await wallet.startSync(); @@ -25,10 +27,6 @@ void startWalletSyncStatusChangeReaction( if (wallet.type == WalletType.haven) { await updateHavenRate(fiatConversionStore); } - - if (wallet.type == WalletType.salvium) { - await updateSalviumRate(fiatConversionStore); - } } if (status is SyncingSyncStatus) { await WakelockPlus.enable(); diff --git a/lib/view_model/dashboard/transaction_list_item.dart b/lib/view_model/dashboard/transaction_list_item.dart index d0d76e3ed0..56953a6353 100644 --- a/lib/view_model/dashboard/transaction_list_item.dart +++ b/lib/view_model/dashboard/transaction_list_item.dart @@ -192,11 +192,9 @@ class TransactionListItem extends ActionListItem with Keyable { price: price); break; case WalletType.salvium: - final asset = salvium!.assetOfTransaction(transaction); - final price = balanceViewModel.fiatConvertationStore.prices[asset]; amount = calculateFiatAmountRaw( cryptoAmount: salvium! - .formatterMoneroAmountToDouble(amount: transaction.amount), + .formatterSalviumAmountToDouble(amount: transaction.amount), price: price); break; case WalletType.ethereum: diff --git a/lib/view_model/monero_account_list/monero_account_list_view_model.dart b/lib/view_model/monero_account_list/monero_account_list_view_model.dart index 0ad5541dae..9f7425073d 100644 --- a/lib/view_model/monero_account_list/monero_account_list_view_model.dart +++ b/lib/view_model/monero_account_list/monero_account_list_view_model.dart @@ -29,43 +29,49 @@ abstract class MoneroAccountListViewModelBase with Store { @computed List get accounts { if (_wallet.type == WalletType.haven) { - return haven - !.getAccountList(_wallet) - .accounts.map((acc) => AccountListItem( - label: acc.label, - id: acc.id, - isSelected: acc.id == haven!.getCurrentAccount(_wallet).id)) - .toList(); + return haven! + .getAccountList(_wallet) + .accounts + .map((acc) => AccountListItem( + label: acc.label, + id: acc.id, + isSelected: acc.id == haven!.getCurrentAccount(_wallet).id)) + .toList(); } if (_wallet.type == WalletType.salvium) { - return salvium!.getAccountList(_wallet).accounts.map((acc) => AccountListItem( - label: acc.label, - id: acc.id, - isSelected: acc.id == salvium!.getCurrentAccount(_wallet).id)) - .toList(); + return salvium! + .getAccountList(_wallet) + .accounts + .map((acc) => AccountListItem( + label: acc.label, + id: acc.id, + isSelected: acc.id == salvium!.getCurrentAccount(_wallet).id)) + .toList(); } if (_wallet.type == WalletType.monero) { - return monero - !.getAccountList(_wallet) - .accounts.map((acc) => AccountListItem( - label: acc.label, - id: acc.id, - balance: acc.balance, - isSelected: acc.id == monero!.getCurrentAccount(_wallet).id)) - .toList(); + return monero! + .getAccountList(_wallet) + .accounts + .map((acc) => AccountListItem( + label: acc.label, + id: acc.id, + balance: acc.balance, + isSelected: acc.id == monero!.getCurrentAccount(_wallet).id)) + .toList(); } if (_wallet.type == WalletType.wownero) { - return wownero - !.getAccountList(_wallet) - .accounts.map((acc) => AccountListItem( - label: acc.label, - id: acc.id, - balance: acc.balance, - isSelected: acc.id == wownero!.getCurrentAccount(_wallet).id)) - .toList(); + return wownero! + .getAccountList(_wallet) + .accounts + .map((acc) => AccountListItem( + label: acc.label, + id: acc.id, + balance: acc.balance, + isSelected: acc.id == wownero!.getCurrentAccount(_wallet).id)) + .toList(); } throw Exception('Unexpected wallet type: ${_wallet.type}'); @@ -80,7 +86,7 @@ abstract class MoneroAccountListViewModelBase with Store { item.id, item.label, item.balance, - ); + ); } if (_wallet.type == WalletType.wownero) { @@ -89,21 +95,15 @@ abstract class MoneroAccountListViewModelBase with Store { item.id, item.label, item.balance, - ); + ); } if (_wallet.type == WalletType.haven) { - haven!.setCurrentAccount( - _wallet, - item.id, - item.label); + haven!.setCurrentAccount(_wallet, item.id, item.label); } if (_wallet.type == WalletType.salvium) { - salvium!.setCurrentAccount( - _wallet, - item.id, - item.label); + salvium!.setCurrentAccount(_wallet, item.id, item.label, item.balance); } } } diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 35ef19f03a..388e8a4839 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -57,7 +57,8 @@ part 'send_view_model.g.dart'; class SendViewModel = SendViewModelBase with _$SendViewModel; -abstract class SendViewModelBase extends WalletChangeListenerViewModel with Store { +abstract class SendViewModelBase extends WalletChangeListenerViewModel + with Store { @override void onWalletChange(wallet) { currencies = wallet.balance.keys.toList(); @@ -90,17 +91,19 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor fiatFromSettings = appStore.settingsStore.fiatCurrency, super(appStore: appStore) { if (wallet.type == WalletType.bitcoin && - _settingsStore.priority[wallet.type] == bitcoinTransactionPriorityCustom) { + _settingsStore.priority[wallet.type] == + bitcoinTransactionPriorityCustom) { setTransactionPriority(bitcoinTransactionPriorityMedium); } final priority = _settingsStore.priority[wallet.type]; final priorities = priorityForWalletType(wallet.type); - if (!priorityForWalletType(wallet.type).contains(priority) && priorities.isNotEmpty) { + if (!priorityForWalletType(wallet.type).contains(priority) && + priorities.isNotEmpty) { _settingsStore.priority[wallet.type] = priorities.first; } - outputs - .add(Output(wallet, _settingsStore, _fiatConversationStore, () => selectedCryptoCurrency)); + outputs.add(Output(wallet, _settingsStore, _fiatConversationStore, + () => selectedCryptoCurrency)); } @observable @@ -114,8 +117,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor @action void addOutput() { - outputs - .add(Output(wallet, _settingsStore, _fiatConversationStore, () => selectedCryptoCurrency)); + outputs.add(Output(wallet, _settingsStore, _fiatConversationStore, + () => selectedCryptoCurrency)); } @action @@ -137,11 +140,11 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor bool get shouldDisplaySendALL { if (walletType == WalletType.solana) return false; - if (walletType == WalletType.ethereum && selectedCryptoCurrency == CryptoCurrency.eth) - return false; + if (walletType == WalletType.ethereum && + selectedCryptoCurrency == CryptoCurrency.eth) return false; - if (walletType == WalletType.polygon && selectedCryptoCurrency == CryptoCurrency.matic) - return false; + if (walletType == WalletType.polygon && + selectedCryptoCurrency == CryptoCurrency.matic) return false; return true; } @@ -205,8 +208,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor int? getCustomPriorityIndex(List priorities) { if (wallet.type == WalletType.bitcoin) { - final customItem = priorities - .firstWhereOrNull((element) => element == bitcoin!.getBitcoinTransactionPriorityCustom()); + final customItem = priorities.firstWhereOrNull((element) => + element == bitcoin!.getBitcoinTransactionPriorityCustom()); return customItem != null ? priorities.indexOf(customItem) : null; } @@ -223,7 +226,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor @computed int get customBitcoinFeeRate => _settingsStore.customBitcoinFeeRate; - void set customBitcoinFeeRate(int value) => _settingsStore.customBitcoinFeeRate = value; + void set customBitcoinFeeRate(int value) => + _settingsStore.customBitcoinFeeRate = value; CryptoCurrency get currency => wallet.currency; @@ -232,7 +236,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor Validator get allAmountValidator => AllAmountValidator(); - Validator get addressValidator => AddressValidator(type: selectedCryptoCurrency); + Validator get addressValidator => + AddressValidator(type: selectedCryptoCurrency); Validator get textValidator => TextValidator(); @@ -248,7 +253,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor } else if (coinTypeToSpendFrom == UnspentCoinType.nonMweb) { return balanceViewModel.balances.values.first.availableBalance; } - return wallet.balance[selectedCryptoCurrency]!.formattedFullAvailableBalance; + return wallet + .balance[selectedCryptoCurrency]!.formattedFullAvailableBalance; } @computed @@ -266,7 +272,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor bool get isReadyForSend => wallet.syncStatus is SyncedSyncStatus || // If silent payments scanning, can still send payments - (wallet.type == WalletType.bitcoin && wallet.syncStatus is SyncingSyncStatus); + (wallet.type == WalletType.bitcoin && + wallet.syncStatus is SyncingSyncStatus); @computed List