diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 3c6b132bd..7b2b611d3 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -38,6 +38,8 @@ jobs: sudo mkdir -p /opt/android sudo chown $USER /opt/android cd /opt/android + -y curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + cargo install cargo-ndk git clone https://github.com/cake-tech/cake_wallet.git --branch $GITHUB_HEAD_REF cd cake_wallet/scripts/android/ ./install_ndk.sh @@ -134,6 +136,7 @@ jobs: echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart echo "const robinhoodCIdApiSecret = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart + echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> lib/.secrets.g.dart - name: Rename app run: echo -e "id=com.cakewallet.test\nname=$GITHUB_HEAD_REF" > /opt/android/cake_wallet/android/app.properties diff --git a/assets/text/Monerocom_Release_Notes.txt b/assets/text/Monerocom_Release_Notes.txt index 9c09278a5..2830ecdd8 100644 --- a/assets/text/Monerocom_Release_Notes.txt +++ b/assets/text/Monerocom_Release_Notes.txt @@ -1,4 +1,2 @@ -UI enhancements -Privacy settings enhancements -Tablet/iPad fixes -Bug fixes \ No newline at end of file +Monero Polyseed support, under Advanced privacy settings, create and restore from a 16 words phrase and without the need to remember the wallet creation date +Minor bug fixes and enhancements \ No newline at end of file diff --git a/assets/text/Release_Notes.txt b/assets/text/Release_Notes.txt index 8b8ab36a1..accdbb080 100644 --- a/assets/text/Release_Notes.txt +++ b/assets/text/Release_Notes.txt @@ -1,5 +1,3 @@ -WalletConnect enhancements -UI enhancements -Privacy settings enhancements -Tablet/iPad fixes -Bug fixes \ No newline at end of file +Monero Polyseed support, under Advanced privacy settings, create and restore from a 16 words phrase and without the need to remember the wallet creation date +Add Ethereum NFTs tab to see all of your purchased NFTs +Minor bug fixes and enhancements \ No newline at end of file diff --git a/configure_cake_wallet_android.sh b/configure_cake_wallet_android.sh index b8aa433de..6998555d5 100755 --- a/configure_cake_wallet_android.sh +++ b/configure_cake_wallet_android.sh @@ -2,6 +2,7 @@ cd scripts/android source ./app_env.sh cakewallet ./app_config.sh cd ../.. && flutter pub get +flutter packages pub run tool/generate_localization.dart cd cw_core && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. cd cw_monero && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index c89c7901a..34a059f03 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -633,6 +633,14 @@ packages: description: flutter source: sdk version: "0.0.99" + socks5_proxy: + dependency: transitive + description: + name: socks5_proxy + sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a" + url: "https://pub.dev" + source: hosted + version: "1.0.4" source_gen: dependency: transitive description: @@ -713,6 +721,15 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + tor: + dependency: transitive + description: + path: "." + ref: main + resolved-ref: "09ba92cb11d4e3cacf97256e57863b805f79f2e5" + url: "https://github.com/cake-tech/tor.git" + source: git + version: "0.0.1" typed_data: dependency: transitive description: diff --git a/cw_bitcoin_cash/linux/flutter/generated_plugin_registrant.cc b/cw_bitcoin_cash/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index e71a16d23..000000000 --- a/cw_bitcoin_cash/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,11 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - - -void fl_register_plugins(FlPluginRegistry* registry) { -} diff --git a/cw_bitcoin_cash/linux/flutter/generated_plugin_registrant.h b/cw_bitcoin_cash/linux/flutter/generated_plugin_registrant.h deleted file mode 100644 index e0f0a47bc..000000000 --- a/cw_bitcoin_cash/linux/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void fl_register_plugins(FlPluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/cw_bitcoin_cash/linux/flutter/generated_plugins.cmake b/cw_bitcoin_cash/linux/flutter/generated_plugins.cmake deleted file mode 100644 index 2e1de87a7..000000000 --- a/cw_bitcoin_cash/linux/flutter/generated_plugins.cmake +++ /dev/null @@ -1,23 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/cw_bitcoin_cash/macos/Flutter/GeneratedPluginRegistrant.swift b/cw_bitcoin_cash/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index e777c67df..000000000 --- a/cw_bitcoin_cash/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - -import path_provider_foundation - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) -} diff --git a/cw_bitcoin_cash/macos/Flutter/ephemeral/Flutter-Generated.xcconfig b/cw_bitcoin_cash/macos/Flutter/ephemeral/Flutter-Generated.xcconfig deleted file mode 100644 index 2f46994d3..000000000 --- a/cw_bitcoin_cash/macos/Flutter/ephemeral/Flutter-Generated.xcconfig +++ /dev/null @@ -1,11 +0,0 @@ -// This is a generated file; do not edit or check into version control. -FLUTTER_ROOT=C:\Users\borod\flutter -FLUTTER_APPLICATION_PATH=C:\cake_wallet\cw_bitcoin_cash -COCOAPODS_PARALLEL_CODE_SIGN=true -FLUTTER_BUILD_DIR=build -FLUTTER_BUILD_NAME=0.0.1 -FLUTTER_BUILD_NUMBER=0.0.1 -DART_OBFUSCATION=false -TRACK_WIDGET_CREATION=true -TREE_SHAKE_ICONS=false -PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/cw_bitcoin_cash/macos/Flutter/ephemeral/flutter_export_environment.sh b/cw_bitcoin_cash/macos/Flutter/ephemeral/flutter_export_environment.sh deleted file mode 100644 index 2a3bcca5a..000000000 --- a/cw_bitcoin_cash/macos/Flutter/ephemeral/flutter_export_environment.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -# This is a generated file; do not edit or check into version control. -export "FLUTTER_ROOT=C:\Users\borod\flutter" -export "FLUTTER_APPLICATION_PATH=C:\cake_wallet\cw_bitcoin_cash" -export "COCOAPODS_PARALLEL_CODE_SIGN=true" -export "FLUTTER_BUILD_DIR=build" -export "FLUTTER_BUILD_NAME=0.0.1" -export "FLUTTER_BUILD_NUMBER=0.0.1" -export "DART_OBFUSCATION=false" -export "TRACK_WIDGET_CREATION=true" -export "TREE_SHAKE_ICONS=false" -export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/cw_bitcoin_cash/windows/flutter/generated_plugin_registrant.cc b/cw_bitcoin_cash/windows/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 8b6d4680a..000000000 --- a/cw_bitcoin_cash/windows/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,11 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - - -void RegisterPlugins(flutter::PluginRegistry* registry) { -} diff --git a/cw_bitcoin_cash/windows/flutter/generated_plugin_registrant.h b/cw_bitcoin_cash/windows/flutter/generated_plugin_registrant.h deleted file mode 100644 index dc139d85a..000000000 --- a/cw_bitcoin_cash/windows/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void RegisterPlugins(flutter::PluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/cw_bitcoin_cash/windows/flutter/generated_plugins.cmake b/cw_bitcoin_cash/windows/flutter/generated_plugins.cmake deleted file mode 100644 index b93c4c30c..000000000 --- a/cw_bitcoin_cash/windows/flutter/generated_plugins.cmake +++ /dev/null @@ -1,23 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/cw_core/lib/get_height_by_date.dart b/cw_core/lib/get_height_by_date.dart index 819990e0a..a680e6c25 100644 --- a/cw_core/lib/get_height_by_date.dart +++ b/cw_core/lib/get_height_by_date.dart @@ -84,7 +84,41 @@ final dates = { "2020-8": 2153983, "2020-9": 2176466, "2020-10": 2198453, - "2020-11": 2220000 + "2020-11": 2220000, + "2020-12": 2242240, + "2021-1": 2264584, + "2021-2": 2286892, + "2021-3": 2307079, + "2021-4": 2329385, + "2021-5": 2351004, + "2021-6": 2373306, + "2021-7": 2394882, + "2021-8": 2417162, + "2021-9": 2439490, + "2021-10": 2461020, + "2021-11": 2483377, + "2021-12": 2504932, + "2022-1": 2527316, + "2022-2": 2549605, + "2022-3": 2569711, + "2022-4": 2591995, + "2022-5": 2613603, + "2022-6": 2635840, + "2022-7": 2657395, + "2022-8": 2679705, + "2022-9": 2701991, + "2022-10": 2723607, + "2022-11": 2745899, + "2022-12": 2767427, + "2023-1": 2789763, + "2023-2": 2811996, + "2023-3": 2832118, + "2023-4": 2854365, + "2023-5": 2875972, + "2023-6": 2898234, + "2023-7": 2919771, + "2023-8": 2942045, + "2023-9": 2964280 }; int getMoneroHeigthByDate({required DateTime date}) { diff --git a/cw_core/lib/node.dart b/cw_core/lib/node.dart index 5f0951447..484325f91 100644 --- a/cw_core/lib/node.dart +++ b/cw_core/lib/node.dart @@ -6,6 +6,7 @@ import 'package:hive/hive.dart'; import 'package:cw_core/hive_type_ids.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:http/io_client.dart' as ioc; +import 'package:tor/tor.dart'; part 'node.g.dart'; @@ -92,6 +93,8 @@ class Node extends HiveObject with Keyable { } } + bool get isValidProxyAddress => socksProxyAddress?.contains(':') ?? false; + @override bool operator ==(other) => other is Node && @@ -129,9 +132,7 @@ class Node extends HiveObject with Keyable { try { switch (type) { case WalletType.monero: - return useSocksProxy - ? requestNodeWithProxy(socksProxyAddress ?? '') - : requestMoneroNode(); + return requestMoneroNode(); case WalletType.bitcoin: return requestElectrumServer(); case WalletType.litecoin: @@ -154,6 +155,9 @@ class Node extends HiveObject with Keyable { } Future requestMoneroNode() async { + if (uri.toString().contains(".onion") || useSocksProxy) { + return await requestNodeWithProxy(); + } final path = '/json_rpc'; final rpcUri = isSSL ? Uri.https(uri.authority, path) : Uri.http(uri.authority, path); final realm = 'monero-rpc'; @@ -205,11 +209,17 @@ class Node extends HiveObject with Keyable { } } - Future requestNodeWithProxy(String proxy) async { - if (proxy.isEmpty || !proxy.contains(':')) { + Future requestNodeWithProxy() async { + if (!isValidProxyAddress && !Tor.instance.enabled) { return false; } - final proxyAddress = proxy.split(':')[0]; + + String? proxy = socksProxyAddress; + + if ((proxy?.isEmpty ?? true) && Tor.instance.enabled) { + proxy = "${InternetAddress.loopbackIPv4.address}:${Tor.instance.port}"; + } + final proxyAddress = proxy!.split(':')[0]; final proxyPort = int.parse(proxy.split(':')[1]); try { final socket = await Socket.connect(proxyAddress, proxyPort, timeout: Duration(seconds: 5)); diff --git a/cw_core/lib/wallet_base.dart b/cw_core/lib/wallet_base.dart index d15ea42cd..09b423c14 100644 --- a/cw_core/lib/wallet_base.dart +++ b/cw_core/lib/wallet_base.dart @@ -44,6 +44,8 @@ abstract class WalletBase null; + String? get hexSeed => null; + Object get keys; WalletAddresses get walletAddresses; diff --git a/cw_core/pubspec.lock b/cw_core/pubspec.lock index e399526fd..f351759ed 100644 --- a/cw_core/pubspec.lock +++ b/cw_core/pubspec.lock @@ -271,10 +271,10 @@ packages: dependency: "direct dev" description: name: hive_generator - sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938" + sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4" url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "2.0.1" http: dependency: "direct main" description: @@ -407,50 +407,50 @@ packages: dependency: "direct main" description: name: path_provider - sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95 + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa url: "https://pub.dev" source: hosted - version: "2.0.12" + version: "2.1.1" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e + sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 url: "https://pub.dev" source: hosted - version: "2.0.22" + version: "2.2.1" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74" + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.3.1" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379 + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 + sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.1.1" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.2.1" platform: dependency: transitive description: @@ -528,6 +528,14 @@ packages: description: flutter source: sdk version: "0.0.99" + socks5_proxy: + dependency: "direct main" + description: + name: socks5_proxy + sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a" + url: "https://pub.dev" + source: hosted + version: "1.0.4" source_gen: dependency: transitive description: @@ -608,6 +616,15 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + tor: + dependency: "direct main" + description: + path: "." + ref: main + resolved-ref: "09ba92cb11d4e3cacf97256e57863b805f79f2e5" + url: "https://github.com/cake-tech/tor.git" + source: git + version: "0.0.1" typed_data: dependency: transitive description: @@ -666,4 +683,4 @@ packages: version: "3.1.1" sdks: dart: ">=3.0.0 <4.0.0" - flutter: ">=3.0.0" + flutter: ">=3.7.0" diff --git a/cw_core/pubspec.yaml b/cw_core/pubspec.yaml index 9dcb7eaba..533b578ad 100644 --- a/cw_core/pubspec.yaml +++ b/cw_core/pubspec.yaml @@ -19,6 +19,11 @@ dependencies: flutter_mobx: ^2.0.6+1 intl: ^0.18.0 encrypt: ^5.0.1 + socks5_proxy: ^1.0.4 + tor: + git: + url: https://github.com/cake-tech/tor.git + ref: main dev_dependencies: flutter_test: @@ -26,7 +31,7 @@ dev_dependencies: build_runner: ^2.1.11 build_resolvers: ^2.0.9 mobx_codegen: ^2.0.7 - hive_generator: ^1.1.3 + hive_generator: ^2.0.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/cw_ethereum/pubspec.yaml b/cw_ethereum/pubspec.yaml index 5d19589f3..6946a43a1 100644 --- a/cw_ethereum/pubspec.yaml +++ b/cw_ethereum/pubspec.yaml @@ -17,7 +17,6 @@ dependencies: mobx: ^2.0.7+4 bip39: ^1.0.6 bip32: ^2.0.0 - ed25519_hd_key: ^2.2.0 hex: ^0.2.0 http: ^1.1.0 shared_preferences: ^2.0.15 diff --git a/cw_haven/pubspec.lock b/cw_haven/pubspec.lock index a63aa3237..525e8e5fa 100644 --- a/cw_haven/pubspec.lock +++ b/cw_haven/pubspec.lock @@ -414,50 +414,50 @@ packages: dependency: "direct main" description: name: path_provider - sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95 + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa url: "https://pub.dev" source: hosted - version: "2.0.12" + version: "2.1.1" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e + sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 url: "https://pub.dev" source: hosted - version: "2.0.22" + version: "2.2.1" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74" + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.3.1" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379 + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 + sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.1.1" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130 + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.2.1" platform: dependency: transitive description: @@ -535,6 +535,14 @@ packages: description: flutter source: sdk version: "0.0.99" + socks5_proxy: + dependency: transitive + description: + name: socks5_proxy + sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a" + url: "https://pub.dev" + source: hosted + version: "1.0.4" source_gen: dependency: transitive description: @@ -615,6 +623,15 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + tor: + dependency: transitive + description: + path: "." + ref: main + resolved-ref: "09ba92cb11d4e3cacf97256e57863b805f79f2e5" + url: "https://github.com/cake-tech/tor.git" + source: git + version: "0.0.1" typed_data: dependency: transitive description: @@ -673,4 +690,4 @@ packages: version: "3.1.1" sdks: dart: ">=3.0.0 <4.0.0" - flutter: ">=3.0.0" + flutter: ">=3.7.0" diff --git a/cw_monero/example/pubspec.lock b/cw_monero/example/pubspec.lock index 1aae6b0c9..3dd2b5b61 100644 --- a/cw_monero/example/pubspec.lock +++ b/cw_monero/example/pubspec.lock @@ -237,50 +237,50 @@ packages: dependency: transitive description: name: path_provider - sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95 + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa url: "https://pub.dev" source: hosted - version: "2.0.12" + version: "2.1.1" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e + sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 url: "https://pub.dev" source: hosted - version: "2.0.22" + version: "2.2.1" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74" + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.3.1" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379 + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 + sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.1.1" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130 + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.2.1" platform: dependency: transitive description: @@ -301,10 +301,19 @@ packages: dependency: transitive description: name: pointycastle - sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346 + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" url: "https://pub.dev" source: hosted - version: "3.6.2" + version: "3.7.3" + polyseed: + dependency: transitive + description: + path: "." + ref: HEAD + resolved-ref: "504d58a5b147fccd3bc85a25f2e72fb32771ddd7" + url: "https://github.com/cake-tech/polyseed_dart.git" + source: git + version: "0.0.1" process: dependency: transitive description: @@ -318,6 +327,14 @@ packages: description: flutter source: sdk version: "0.0.99" + socks5_proxy: + dependency: transitive + description: + name: socks5_proxy + sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a" + url: "https://pub.dev" + source: hosted + version: "1.0.4" source_span: dependency: transitive description: @@ -366,6 +383,15 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.1" + tor: + dependency: transitive + description: + path: "." + ref: main + resolved-ref: "09ba92cb11d4e3cacf97256e57863b805f79f2e5" + url: "https://github.com/cake-tech/tor.git" + source: git + version: "0.0.1" typed_data: dependency: transitive description: @@ -399,5 +425,5 @@ packages: source: hosted version: "0.2.0+3" sdks: - dart: ">=3.0.0 <4.0.0" - flutter: ">=3.0.0" + dart: ">=3.0.6 <4.0.0" + flutter: ">=3.7.0" diff --git a/cw_monero/ios/Classes/monero_api.cpp b/cw_monero/ios/Classes/monero_api.cpp index a0712255a..7ad873647 100644 --- a/cw_monero/ios/Classes/monero_api.cpp +++ b/cw_monero/ios/Classes/monero_api.cpp @@ -374,6 +374,35 @@ extern "C" return true; } + bool restore_wallet_from_spend_key(char *path, char *password, char *seed, char *language, char *spendKey, int32_t networkType, uint64_t restoreHeight, char *error) + { + Monero::NetworkType _networkType = static_cast(networkType); + Monero::Wallet *wallet = Monero::WalletManagerFactory::getWalletManager()->createDeterministicWalletFromSpendKey( + std::string(path), + std::string(password), + std::string(language), + _networkType, + (uint64_t)restoreHeight, + std::string(spendKey)); + + int status; + std::string errorString; + + wallet->statusWithErrorString(status, errorString); + + if (status != Monero::Wallet::Status_Ok || !errorString.empty()) + { + error = strdup(errorString.c_str()); + return false; + } + + // Cache Raw to support Polyseed + wallet->setCacheAttribute("cakewallet.seed", std::string(seed)); + + change_current_wallet(wallet); + return true; + } + bool load_wallet(char *path, char *password, int32_t nettype) { nice(19); @@ -438,6 +467,11 @@ extern "C" const char *seed() { + std::string _rawSeed = get_current_wallet()->getCacheAttribute("cakewallet.seed"); + if (!_rawSeed.empty()) + { + return strdup(_rawSeed.c_str()); + } return strdup(get_current_wallet()->seed().c_str()); } diff --git a/cw_monero/lib/api/signatures.dart b/cw_monero/lib/api/signatures.dart index 9be828df0..17099db91 100644 --- a/cw_monero/lib/api/signatures.dart +++ b/cw_monero/lib/api/signatures.dart @@ -14,6 +14,9 @@ typedef restore_wallet_from_seed = Int8 Function( typedef restore_wallet_from_keys = Int8 Function(Pointer, Pointer, Pointer, Pointer, Pointer, Pointer, Int32, Int64, Pointer); +typedef restore_wallet_from_spend_key = Int8 Function(Pointer, Pointer, Pointer, + Pointer, Pointer, Int32, Int64, Pointer); + typedef is_wallet_exist = Int8 Function(Pointer); typedef load_wallet = Int8 Function(Pointer, Pointer, Int8); diff --git a/cw_monero/lib/api/types.dart b/cw_monero/lib/api/types.dart index 4c0c980dc..1d3904870 100644 --- a/cw_monero/lib/api/types.dart +++ b/cw_monero/lib/api/types.dart @@ -14,6 +14,9 @@ typedef RestoreWalletFromSeed = int Function( typedef RestoreWalletFromKeys = int Function(Pointer, Pointer, Pointer, Pointer, Pointer, Pointer, int, int, Pointer); +typedef RestoreWalletFromSpendKey = int Function(Pointer, Pointer, Pointer, + Pointer, Pointer, int, int, Pointer); + typedef IsWalletExist = int Function(Pointer); typedef LoadWallet = int Function(Pointer, Pointer, int); diff --git a/cw_monero/lib/api/wallet_manager.dart b/cw_monero/lib/api/wallet_manager.dart index 093d7e63b..260b420c5 100644 --- a/cw_monero/lib/api/wallet_manager.dart +++ b/cw_monero/lib/api/wallet_manager.dart @@ -5,7 +5,6 @@ import 'package:cw_monero/api/convert_utf8_to_string.dart'; import 'package:cw_monero/api/signatures.dart'; import 'package:cw_monero/api/types.dart'; import 'package:cw_monero/api/monero_api.dart'; -import 'package:cw_monero/api/wallet.dart'; import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart'; import 'package:cw_monero/api/exceptions/wallet_creation_exception.dart'; import 'package:cw_monero/api/exceptions/wallet_restore_from_keys_exception.dart'; @@ -25,6 +24,11 @@ final restoreWalletFromKeysNative = moneroApi 'restore_wallet_from_keys') .asFunction(); +final restoreWalletFromSpendKeyNative = moneroApi + .lookup>( + 'restore_wallet_from_spend_key') + .asFunction(); + final isWalletExistNative = moneroApi .lookup>('is_wallet_exist') .asFunction(); @@ -141,6 +145,42 @@ void restoreWalletFromKeysSync( } } +void restoreWalletFromSpendKeySync( + {required String path, + required String password, + required String seed, + required String language, + required String spendKey, + int nettype = 0, + int restoreHeight = 0}) { + final pathPointer = path.toNativeUtf8(); + final passwordPointer = password.toNativeUtf8(); + final seedPointer = seed.toNativeUtf8(); + final languagePointer = language.toNativeUtf8(); + final spendKeyPointer = spendKey.toNativeUtf8(); + final errorMessagePointer = ''.toNativeUtf8(); + final isWalletRestored = restoreWalletFromSpendKeyNative( + pathPointer, + passwordPointer, + seedPointer, + languagePointer, + spendKeyPointer, + nettype, + restoreHeight, + errorMessagePointer) != + 0; + + calloc.free(pathPointer); + calloc.free(passwordPointer); + calloc.free(languagePointer); + calloc.free(spendKeyPointer); + + if (!isWalletRestored) { + throw WalletRestoreFromKeysException( + message: convertUTF8ToString(pointer: errorMessagePointer)); + } +} + void loadWallet({ required String path, required String password, @@ -194,6 +234,23 @@ void _restoreFromKeys(Map args) { spendKey: spendKey); } +void _restoreFromSpendKey(Map args) { + final path = args['path'] as String; + final password = args['password'] as String; + final seed = args['seed'] as String; + final language = args['language'] as String; + final spendKey = args['spendKey'] as String; + final restoreHeight = args['restoreHeight'] as int; + + restoreWalletFromSpendKeySync( + path: path, + password: password, + seed: seed, + language: language, + restoreHeight: restoreHeight, + spendKey: spendKey); +} + Future _openWallet(Map args) async => loadWallet(path: args['path'] as String, password: args['password'] as String); @@ -251,4 +308,22 @@ Future restoreFromKeys( 'restoreHeight': restoreHeight }); +Future restoreFromSpendKey( + {required String path, + required String password, + required String seed, + required String language, + required String spendKey, + int nettype = 0, + int restoreHeight = 0}) async => + compute, void>(_restoreFromSpendKey, { + 'path': path, + 'password': password, + 'seed': seed, + 'language': language, + 'spendKey': spendKey, + 'nettype': nettype, + 'restoreHeight': restoreHeight + }); + Future isWalletExist({required String path}) => compute(_isWalletExist, path); diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index d7e66ecec..71c7e3967 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -29,6 +29,7 @@ import 'package:cw_monero/monero_transaction_info.dart'; import 'package:cw_monero/monero_unspent.dart'; import 'package:cw_monero/monero_wallet_addresses.dart'; import 'package:cw_monero/pending_monero_transaction.dart'; +import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; @@ -78,6 +79,8 @@ abstract class MoneroWalletBase Box unspentCoinsInfo; + void Function(FlutterErrorDetails)? _onError; + @override late MoneroWalletAddresses walletAddresses; @@ -388,46 +391,59 @@ abstract class MoneroWalletBase } Future updateUnspent() async { - refreshCoins(walletAddresses.account!.id); + try { + refreshCoins(walletAddresses.account!.id); - unspentCoins.clear(); + unspentCoins.clear(); - final coinCount = countOfCoins(); - for (var i = 0; i < coinCount; i++) { - final coin = getCoin(i); - if (coin.spent == 0) { - final unspent = MoneroUnspent.fromCoinsInfoRow(coin); - unspent.isChange = transaction_history.getTransaction(unspent.hash).direction == 1; - unspentCoins.add(unspent); - } - } - - if (unspentCoinsInfo.isEmpty) { - unspentCoins.forEach((coin) => _addCoinInfo(coin)); - return; - } - - if (unspentCoins.isNotEmpty) { - unspentCoins.forEach((coin) { - final coinInfoList = unspentCoinsInfo.values.where((element) => - element.walletId.contains(id) && - element.accountIndex == walletAddresses.account!.id && - element.keyImage!.contains(coin.keyImage!)); - - if (coinInfoList.isNotEmpty) { - final coinInfo = coinInfoList.first; - - coin.isFrozen = coinInfo.isFrozen; - coin.isSending = coinInfo.isSending; - coin.note = coinInfo.note; - } else { - _addCoinInfo(coin); + final coinCount = countOfCoins(); + for (var i = 0; i < coinCount; i++) { + final coin = getCoin(i); + if (coin.spent == 0) { + final unspent = MoneroUnspent.fromCoinsInfoRow(coin); + if (unspent.hash.isNotEmpty) { + unspent.isChange = transaction_history + .getTransaction(unspent.hash) + .direction == 1; + } + unspentCoins.add(unspent); } - }); - } + } - await _refreshUnspentCoinsInfo(); - _askForUpdateBalance(); + if (unspentCoinsInfo.isEmpty) { + unspentCoins.forEach((coin) => _addCoinInfo(coin)); + return; + } + + if (unspentCoins.isNotEmpty) { + unspentCoins.forEach((coin) { + final coinInfoList = unspentCoinsInfo.values.where((element) => + element.walletId.contains(id) && + element.accountIndex == walletAddresses.account!.id && + element.keyImage!.contains(coin.keyImage!)); + + if (coinInfoList.isNotEmpty) { + final coinInfo = coinInfoList.first; + + coin.isFrozen = coinInfo.isFrozen; + coin.isSending = coinInfo.isSending; + coin.note = coinInfo.note; + } else { + _addCoinInfo(coin); + } + }); + } + + await _refreshUnspentCoinsInfo(); + _askForUpdateBalance(); + } catch (e, s) { + print(e.toString()); + _onError?.call(FlutterErrorDetails( + exception: e, + stack: s, + library: this.runtimeType.toString(), + )); + } } Future _addCoinInfo(MoneroUnspent coin) async { @@ -632,4 +648,7 @@ abstract class MoneroWalletBase walletAddresses.updateSubaddressList(accountIndex: account?.id ?? 0); } } + + @override + void setExceptionHandler(void Function(FlutterErrorDetails) onError) => _onError = onError; } diff --git a/cw_monero/lib/monero_wallet_service.dart b/cw_monero/lib/monero_wallet_service.dart index 12db1f752..8b6a1d7ba 100644 --- a/cw_monero/lib/monero_wallet_service.dart +++ b/cw_monero/lib/monero_wallet_service.dart @@ -7,16 +7,20 @@ import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_type.dart'; +import 'package:cw_core/get_height_by_date.dart'; import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart'; import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager; import 'package:cw_monero/monero_wallet.dart'; import 'package:hive/hive.dart'; +import 'package:polyseed/polyseed.dart'; +import 'package:polyseed/src/utils/key_utils.dart'; class MoneroNewWalletCredentials extends WalletCredentials { - MoneroNewWalletCredentials({required String name, required this.language, String? password}) + MoneroNewWalletCredentials({required String name, required this.language, required this.isPolyseed, String? password}) : super(name: name, password: password); final String language; + final bool isPolyseed; } class MoneroRestoreWalletFromSeedCredentials extends WalletCredentials { @@ -68,10 +72,17 @@ class MoneroWalletService extends WalletService< Future create(MoneroNewWalletCredentials credentials, {bool? isTestnet}) async { try { final path = await pathForWallet(name: credentials.name, type: getType()); + + if (credentials.isPolyseed) { + final polyseed = Polyseed.create(); + final lang = PolyseedLang.getByEnglishName(credentials.language); + + return _restoreFromPolyseed( + path, credentials.password!, polyseed, credentials.walletInfo!, lang); + } + await monero_wallet_manager.createWallet( - path: path, - password: credentials.password!, - language: credentials.language); + path: path, password: credentials.password!, language: credentials.language); final wallet = MoneroWallet( walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource); await wallet.init(); @@ -215,6 +226,12 @@ class MoneroWalletService extends WalletService< @override Future restoreFromSeed( MoneroRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async { + + // Restore from Polyseed + if (Polyseed.isValidSeed(credentials.mnemonic)) { + return restoreFromPolyseed(credentials); + } + try { final path = await pathForWallet(name: credentials.name, type: getType()); await monero_wallet_manager.restoreFromSeed( @@ -234,6 +251,45 @@ class MoneroWalletService extends WalletService< } } + Future restoreFromPolyseed(MoneroRestoreWalletFromSeedCredentials credentials) async { + try { + final path = await pathForWallet(name: credentials.name, type: getType()); + final polyseedCoin = PolyseedCoin.POLYSEED_MONERO; + final lang = PolyseedLang.getByPhrase(credentials.mnemonic); + final polyseed = Polyseed.decode(credentials.mnemonic, lang, polyseedCoin); + + return _restoreFromPolyseed(path, credentials.password!, polyseed, credentials.walletInfo!, lang); + } catch (e) { + // TODO: Implement Exception for wallet list service. + print('MoneroWalletsManager Error: $e'); + rethrow; + } + } + + Future _restoreFromPolyseed(String path, String password, Polyseed polyseed, + WalletInfo walletInfo, PolyseedLang lang, + {PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO}) async { + final height = getMoneroHeigthByDate( + date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000)); + final spendKey = keyToHexString(polyseed.generateKey(coin, 32)); + + walletInfo.isRecovery = true; + walletInfo.restoreHeight = height; + + await monero_wallet_manager.restoreFromSpendKey( + path: path, + password: password, + seed: polyseed.encode(lang, coin), + language: lang.nameEnglish, + restoreHeight: height, + spendKey: spendKey); + final wallet = MoneroWallet( + walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource); + await wallet.init(); + + return wallet; + } + Future repairOldAndroidWallet(String name) async { try { if (!Platform.isAndroid) { diff --git a/cw_monero/macos/Classes/monero_api.cpp b/cw_monero/macos/Classes/monero_api.cpp index ac8a64861..a5ca13822 100644 --- a/cw_monero/macos/Classes/monero_api.cpp +++ b/cw_monero/macos/Classes/monero_api.cpp @@ -234,7 +234,6 @@ extern "C" } void setUnlocked(bool unlocked); - }; Monero::Coins *m_coins; @@ -375,6 +374,35 @@ extern "C" return true; } + bool restore_wallet_from_spend_key(char *path, char *password, char *seed, char *language, char *spendKey, int32_t networkType, uint64_t restoreHeight, char *error) + { + Monero::NetworkType _networkType = static_cast(networkType); + Monero::Wallet *wallet = Monero::WalletManagerFactory::getWalletManager()->createDeterministicWalletFromSpendKey( + std::string(path), + std::string(password), + std::string(language), + _networkType, + (uint64_t)restoreHeight, + std::string(spendKey)); + + int status; + std::string errorString; + + wallet->statusWithErrorString(status, errorString); + + if (status != Monero::Wallet::Status_Ok || !errorString.empty()) + { + error = strdup(errorString.c_str()); + return false; + } + + // Cache Raw to support Polyseed + wallet->setCacheAttribute("cakewallet.seed", std::string(seed)); + + change_current_wallet(wallet); + return true; + } + bool load_wallet(char *path, char *password, int32_t nettype) { nice(19); @@ -439,6 +467,11 @@ extern "C" const char *seed() { + std::string _rawSeed = get_current_wallet()->getCacheAttribute("cakewallet.seed"); + if (!_rawSeed.empty()) + { + return strdup(_rawSeed.c_str()); + } return strdup(get_current_wallet()->seed().c_str()); } @@ -842,6 +875,12 @@ extern "C" return m_transaction_history->count(); } + TransactionInfoRow* get_transaction(char * txId) + { + Monero::TransactionInfo *row = m_transaction_history->transaction(std::string(txId)); + return new TransactionInfoRow(row); + } + int LedgerExchange( unsigned char *command, unsigned int cmd_len, @@ -971,6 +1010,15 @@ extern "C" return result; } + void freeze_coin(int index) + { + m_coins->setFrozen(index); + } + + void thaw_coin(int index) + { + m_coins->thaw(index); + } #ifdef __cplusplus } diff --git a/cw_monero/pubspec.lock b/cw_monero/pubspec.lock index 37e08e7ca..46ebe8028 100644 --- a/cw_monero/pubspec.lock +++ b/cw_monero/pubspec.lock @@ -414,50 +414,50 @@ packages: dependency: "direct main" description: name: path_provider - sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95 + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa url: "https://pub.dev" source: hosted - version: "2.0.12" + version: "2.1.1" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e + sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 url: "https://pub.dev" source: hosted - version: "2.0.22" + version: "2.2.1" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74" + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.3.1" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379 + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 + sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.1.1" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130 + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.2.1" platform: dependency: transitive description: @@ -478,10 +478,19 @@ packages: dependency: transitive description: name: pointycastle - sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346 + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" url: "https://pub.dev" source: hosted - version: "3.6.2" + version: "3.7.3" + polyseed: + dependency: "direct main" + description: + path: "." + ref: HEAD + resolved-ref: "504d58a5b147fccd3bc85a25f2e72fb32771ddd7" + url: "https://github.com/cake-tech/polyseed_dart.git" + source: git + version: "0.0.1" pool: dependency: transitive description: @@ -535,6 +544,14 @@ packages: description: flutter source: sdk version: "0.0.99" + socks5_proxy: + dependency: transitive + description: + name: socks5_proxy + sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a" + url: "https://pub.dev" + source: hosted + version: "1.0.4" source_gen: dependency: transitive description: @@ -615,6 +632,15 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + tor: + dependency: transitive + description: + path: "." + ref: main + resolved-ref: "09ba92cb11d4e3cacf97256e57863b805f79f2e5" + url: "https://github.com/cake-tech/tor.git" + source: git + version: "0.0.1" typed_data: dependency: transitive description: @@ -672,5 +698,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=3.0.0 <4.0.0" - flutter: ">=3.0.0" + dart: ">=3.0.6 <4.0.0" + flutter: ">=3.7.0" diff --git a/cw_monero/pubspec.yaml b/cw_monero/pubspec.yaml index cf2993ef3..55e9b354c 100644 --- a/cw_monero/pubspec.yaml +++ b/cw_monero/pubspec.yaml @@ -19,6 +19,9 @@ dependencies: flutter_mobx: ^2.0.6+1 intl: ^0.18.0 encrypt: ^5.0.1 + polyseed: + git: + url: https://github.com/cake-tech/polyseed_dart.git cw_core: path: ../cw_core diff --git a/cw_nano/lib/nano_wallet.dart b/cw_nano/lib/nano_wallet.dart index 44d564fc6..b0d98efec 100644 --- a/cw_nano/lib/nano_wallet.dart +++ b/cw_nano/lib/nano_wallet.dart @@ -58,13 +58,13 @@ abstract class NanoWalletBase } } - final String _mnemonic; + String _mnemonic; final String _password; - final DerivationType _derivationType; + DerivationType _derivationType; String? _privateKey; String? _publicAddress; - String? _seedKey; + String? _hexSeed; String? _representativeAddress; Timer? _receiveTimer; @@ -85,22 +85,26 @@ abstract class NanoWalletBase // initialize the different forms of private / public key we'll need: Future init() async { + if (_derivationType == DerivationType.unknown) { + _derivationType = DerivationType.nano; + } final String type = (_derivationType == DerivationType.nano) ? "standard" : "hd"; - // our "mnemonic" is actually a seedkey: + // our "mnemonic" is actually a hex form seed: if (!_mnemonic.contains(' ')) { - _seedKey = _mnemonic; + _hexSeed = _mnemonic; + _mnemonic = ""; } - if (_seedKey == null) { + if (_hexSeed == null) { if (_derivationType == DerivationType.nano) { - _seedKey = bip39.mnemonicToEntropy(_mnemonic).toUpperCase(); + _hexSeed = bip39.mnemonicToEntropy(_mnemonic).toUpperCase(); } else { - _seedKey = await NanoUtil.hdMnemonicListToSeed(_mnemonic.split(' ')); + _hexSeed = await NanoUtil.hdMnemonicListToSeed(_mnemonic.split(' ')); } } - _privateKey = await NanoUtil.uniSeedToPrivate(_seedKey!, 0, type); - _publicAddress = await NanoUtil.uniSeedToAddress(_seedKey!, 0, type); + _privateKey = await NanoUtil.uniSeedToPrivate(_hexSeed!, 0, type); + _publicAddress = await NanoUtil.uniSeedToAddress(_hexSeed!, 0, type); this.walletInfo.address = _publicAddress!; await walletAddresses.init(); @@ -182,9 +186,9 @@ abstract class NanoWalletBase final block = await _client.constructSendBlock( amountRaw: amt.toString(), - destinationAddress: credentials.outputs.first.isParsedAddress - ? credentials.outputs.first.extractedAddress! - : credentials.outputs.first.address, + destinationAddress: txOut.isParsedAddress + ? txOut.extractedAddress! + : txOut.address, privateKey: _privateKey!, balanceAfterTx: runningBalance, previousHash: previousHash, @@ -275,11 +279,11 @@ abstract class NanoWalletBase @override NanoWalletKeys get keys { - return NanoWalletKeys(seedKey: _seedKey!); + return NanoWalletKeys(seedKey: _hexSeed!); } @override - String? get privateKey => _seedKey!; + String? get privateKey => _privateKey!; @override Future rescan({required int height}) async { @@ -297,7 +301,9 @@ abstract class NanoWalletBase } @override - String get seed => _mnemonic; + String? get seed => _mnemonic.isNotEmpty ? _mnemonic : null; + + String get hexSeed => _hexSeed!; String get representative => _representativeAddress ?? ""; @@ -330,7 +336,7 @@ abstract class NanoWalletBase Future makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type); String toJSON() => json.encode({ - 'seedKey': _seedKey, + 'seedKey': _hexSeed, 'mnemonic': _mnemonic, 'currentBalance': balance[currency]?.currentBalance.toString() ?? "0", 'receivableBalance': balance[currency]?.receivableBalance.toString() ?? "0", @@ -351,9 +357,9 @@ abstract class NanoWalletBase formattedCurrentBalance: data['currentBalance'] as String? ?? "0", formattedReceivableBalance: data['receivableBalance'] as String? ?? "0"); - DerivationType derivationType = DerivationType.bip39; - if (data['derivationType'] == "DerivationType.nano") { - derivationType = DerivationType.nano; + DerivationType derivationType = DerivationType.nano; + if (data['derivationType'] == "DerivationType.bip39") { + derivationType = DerivationType.bip39; } walletInfo.derivationType = derivationType; @@ -390,9 +396,9 @@ abstract class NanoWalletBase Future regenerateAddress() async { final String type = (_derivationType == DerivationType.nano) ? "standard" : "hd"; _privateKey = - await NanoUtil.uniSeedToPrivate(_seedKey!, this.walletAddresses.account!.id, type); + await NanoUtil.uniSeedToPrivate(_hexSeed!, this.walletAddresses.account!.id, type); _publicAddress = - await NanoUtil.uniSeedToAddress(_seedKey!, this.walletAddresses.account!.id, type); + await NanoUtil.uniSeedToAddress(_hexSeed!, this.walletAddresses.account!.id, type); this.walletInfo.address = _publicAddress!; this.walletAddresses.address = _publicAddress!; diff --git a/cw_nano/pubspec.lock b/cw_nano/pubspec.lock index d28558422..4a5496fa6 100644 --- a/cw_nano/pubspec.lock +++ b/cw_nano/pubspec.lock @@ -491,50 +491,50 @@ packages: dependency: transitive description: name: path_provider - sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.1.1" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86" + sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 url: "https://pub.dev" source: hosted - version: "2.0.27" + version: "2.2.1" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297" + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" url: "https://pub.dev" source: hosted - version: "2.2.4" + version: "2.3.1" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57 + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 url: "https://pub.dev" source: hosted - version: "2.1.11" + version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.1.1" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96" + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.2.1" pinenacl: dependency: transitive description: @@ -676,6 +676,14 @@ packages: description: flutter source: sdk version: "0.0.99" + socks5_proxy: + dependency: transitive + description: + name: socks5_proxy + sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a" + url: "https://pub.dev" + source: hosted + version: "1.0.4" source_gen: dependency: transitive description: @@ -756,6 +764,15 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + tor: + dependency: transitive + description: + path: "." + ref: main + resolved-ref: "09ba92cb11d4e3cacf97256e57863b805f79f2e5" + url: "https://github.com/cake-tech/tor.git" + source: git + version: "0.0.1" typed_data: dependency: transitive description: diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 970ee3f80..0ecb48591 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -63,8 +63,6 @@ PODS: - Flutter - device_display_brightness (0.0.1): - Flutter - - device_info (0.0.1): - - Flutter - device_info_plus (0.0.1): - Flutter - devicelocale (0.0.1): @@ -124,15 +122,13 @@ PODS: - Flutter - MTBBarcodeScanner (5.0.11) - OrderedSet (5.0.0) - - package_info (0.0.1): + - package_info_plus (0.4.5): - Flutter - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - permission_handler_apple (9.1.1): - Flutter - - platform_device_id (0.0.1): - - Flutter - ReachabilitySwift (5.0.0) - SDWebImage (5.16.0): - SDWebImage/Core (= 5.16.0) @@ -147,6 +143,8 @@ PODS: - SwiftProtobuf (1.22.0) - SwiftyGif (5.4.4) - Toast (4.0.0) + - tor (0.0.1): + - Flutter - uni_links (0.0.1): - Flutter - UnstoppableDomainsResolution (4.0.0): @@ -154,7 +152,7 @@ PODS: - CryptoSwift - url_launcher_ios (0.0.1): - Flutter - - wakelock (0.0.1): + - wakelock_plus (0.0.1): - Flutter - workmanager (0.0.1): - Flutter @@ -167,7 +165,6 @@ DEPENDENCIES: - cw_monero (from `.symlinks/plugins/cw_monero/ios`) - cw_shared_external (from `.symlinks/plugins/cw_shared_external/ios`) - device_display_brightness (from `.symlinks/plugins/device_display_brightness/ios`) - - device_info (from `.symlinks/plugins/device_info/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - devicelocale (from `.symlinks/plugins/devicelocale/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`) @@ -178,17 +175,17 @@ DEPENDENCIES: - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - in_app_review (from `.symlinks/plugins/in_app_review/ios`) - local_auth_ios (from `.symlinks/plugins/local_auth_ios/ios`) - - package_info (from `.symlinks/plugins/package_info/ios`) + - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - - platform_device_id (from `.symlinks/plugins/platform_device_id/ios`) - sensitive_clipboard (from `.symlinks/plugins/sensitive_clipboard/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) + - tor (from `.symlinks/plugins/tor/ios`) - uni_links (from `.symlinks/plugins/uni_links/ios`) - UnstoppableDomainsResolution (~> 4.0.0) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - - wakelock (from `.symlinks/plugins/wakelock/ios`) + - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`) - workmanager (from `.symlinks/plugins/workmanager/ios`) SPEC REPOS: @@ -219,8 +216,6 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/cw_shared_external/ios" device_display_brightness: :path: ".symlinks/plugins/device_display_brightness/ios" - device_info: - :path: ".symlinks/plugins/device_info/ios" device_info_plus: :path: ".symlinks/plugins/device_info_plus/ios" devicelocale: @@ -241,26 +236,26 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/in_app_review/ios" local_auth_ios: :path: ".symlinks/plugins/local_auth_ios/ios" - package_info: - :path: ".symlinks/plugins/package_info/ios" + package_info_plus: + :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" permission_handler_apple: :path: ".symlinks/plugins/permission_handler_apple/ios" - platform_device_id: - :path: ".symlinks/plugins/platform_device_id/ios" sensitive_clipboard: :path: ".symlinks/plugins/sensitive_clipboard/ios" share_plus: :path: ".symlinks/plugins/share_plus/ios" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" + tor: + :path: ".symlinks/plugins/tor/ios" uni_links: :path: ".symlinks/plugins/uni_links/ios" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" - wakelock: - :path: ".symlinks/plugins/wakelock/ios" + wakelock_plus: + :path: ".symlinks/plugins/wakelock_plus/ios" workmanager: :path: ".symlinks/plugins/workmanager/ios" @@ -273,12 +268,11 @@ SPEC CHECKSUMS: cw_monero: 4cf3b96f2da8e95e2ef7d6703dd4d2c509127b7d cw_shared_external: 2972d872b8917603478117c9957dfca611845a92 device_display_brightness: 1510e72c567a1f6ce6ffe393dcd9afd1426034f7 - device_info: d7d233b645a32c40dfdc212de5cf646ca482f175 - device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed + device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 devicelocale: b22617f40038496deffba44747101255cee005b0 DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 - file_picker: ce3938a0df3cc1ef404671531facef740d03f920 + file_picker: 15fd9539e4eb735dc54bae8c0534a7a9511a03de Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 flutter_inappwebview: 3d32228f1304635e7c028b0d4252937730bbc6cf flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83 @@ -288,10 +282,9 @@ SPEC CHECKSUMS: local_auth_ios: c6cf091ded637a88f24f86a8875d8b0f526e2605 MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c - package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 + package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 - platform_device_id: 81b3e2993881f87d0c82ef151dc274df4869aef5 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 SDWebImage: 2aea163b50bfcb569a2726b6a754c54a4506fcf6 sensitive_clipboard: d4866e5d176581536c27bb1618642ee83adca986 @@ -300,10 +293,11 @@ SPEC CHECKSUMS: SwiftProtobuf: 40bd808372cb8706108f22d28f8ab4a6b9bc6989 SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 + tor: 662a9f5b980b5c86decb8ba611de9bcd4c8286eb uni_links: d97da20c7701486ba192624d99bffaaffcfc298a UnstoppableDomainsResolution: c3c67f4d0a5e2437cb00d4bd50c2e00d6e743841 - url_launcher_ios: 68d46cc9766d0c41dbdc884310529557e3cd7a86 - wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f + url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b + wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47 workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6 PODFILE CHECKSUM: 09df1114e7c360f55770d35a79356bf5446e0100 diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index b13a01889..7dd0b50f3 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -436,6 +436,7 @@ class BackupService { final backupPasswordKey = generateStoreKeyFor(key: SecretStoreKey.backupPassword); final backupPassword = keychainJSON[backupPasswordKey] as String; + await _flutterSecureStorage.delete(key: backupPasswordKey); await _flutterSecureStorage.write(key: backupPasswordKey, value: backupPassword); keychainWalletsInfo.forEach((dynamic rawInfo) async { @@ -443,6 +444,7 @@ class BackupService { await importWalletKeychainInfo(info); }); + await _flutterSecureStorage.delete(key: pinCodeKey); await _flutterSecureStorage.write(key: pinCodeKey, value: encodedPinCode(pin: decodedPin)); keychainDumpFile.deleteSync(); @@ -462,6 +464,7 @@ class BackupService { final backupPasswordKey = generateStoreKeyFor(key: SecretStoreKey.backupPassword); final backupPassword = keychainJSON[backupPasswordKey] as String; + await _flutterSecureStorage.delete(key: backupPasswordKey); await _flutterSecureStorage.write(key: backupPasswordKey, value: backupPassword); keychainWalletsInfo.forEach((dynamic rawInfo) async { @@ -469,6 +472,7 @@ class BackupService { await importWalletKeychainInfo(info); }); + await _flutterSecureStorage.delete(key: pinCodeKey); await _flutterSecureStorage.write(key: pinCodeKey, value: encodedPinCode(pin: decodedPin)); keychainDumpFile.deleteSync(); diff --git a/lib/core/key_service.dart b/lib/core/key_service.dart index 337f1ef21..fce254ea2 100644 --- a/lib/core/key_service.dart +++ b/lib/core/key_service.dart @@ -19,6 +19,7 @@ class KeyService { key: SecretStoreKey.moneroWalletPassword, walletName: walletName); final encodedPassword = encodeWalletPassword(password: password); + await _secureStorage.delete(key: key); await _secureStorage.write(key: key, value: encodedPassword); } diff --git a/lib/di.dart b/lib/di.dart index 48ec1c429..120e780b2 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -27,7 +27,7 @@ import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sideba import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart'; import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart'; import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart'; import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart'; import 'package:cake_wallet/src/screens/nano_accounts/nano_account_edit_or_create_page.dart'; import 'package:cake_wallet/src/screens/nano_accounts/nano_account_list_page.dart'; @@ -46,6 +46,7 @@ import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page. import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart'; import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart'; import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart'; +import 'package:cake_wallet/src/screens/settings/tor_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart'; @@ -64,6 +65,7 @@ import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart'; import 'package:cake_wallet/view_model/anonpay_details_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_buy_card_view_model.dart'; @@ -79,7 +81,7 @@ import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_cards_page.dar import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_tip_page.dart'; import 'package:cake_wallet/src/screens/ionia/ionia.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/balance_page.dart'; import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart'; @@ -211,7 +213,7 @@ import 'package:cake_wallet/store/templates/exchange_template_store.dart'; import 'package:cake_wallet/entities/template.dart'; import 'package:cake_wallet/exchange/exchange_template.dart'; import 'package:cake_wallet/.secrets.g.dart' as secrets; -import 'package:cake_wallet/src/screens/dashboard/widgets/address_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart'; import 'package:cake_wallet/anypay/anypay_api.dart'; import 'package:cake_wallet/view_model/ionia/ionia_gift_card_details_view_model.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart'; @@ -482,6 +484,7 @@ Future setup({ }); getIt.registerFactory(() => BalancePage( + nftViewModel: getIt.get(), dashboardViewModel: getIt.get(), settingsStore: getIt.get())); @@ -1177,5 +1180,9 @@ Future setup({ getIt.registerFactory( () => WalletConnectConnectionsView(web3walletService: getIt.get())); + getIt.registerFactory(() => NFTViewModel(appStore, getIt.get())); + + getIt.registerFactory(() => TorPage(getIt.get())); + _isSetupFinished = true; } diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index 47092c22f..a65742e99 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -496,6 +496,7 @@ Future generateBackupPassword(FlutterSecureStorage secureStorage) async { } final password = encrypt.Key.fromSecureRandom(32).base16; + await secureStorage.delete(key: key); await secureStorage.write(key: key, value: password); } diff --git a/lib/entities/fs_migration.dart b/lib/entities/fs_migration.dart index e3088ff54..4280949cd 100644 --- a/lib/entities/fs_migration.dart +++ b/lib/entities/fs_migration.dart @@ -147,6 +147,7 @@ Future ios_migrate_pin() async { final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword); final encodedPassword = encodedPinCode(pin: pinPassword); + await flutterSecureStorage.delete(key: key); await flutterSecureStorage.write(key: key, value: encodedPassword); await prefs.setBool('ios_migration_pin_completed', true); } diff --git a/lib/entities/get_encryption_key.dart b/lib/entities/get_encryption_key.dart index e67380bb8..a32d4e311 100644 --- a/lib/entities/get_encryption_key.dart +++ b/lib/entities/get_encryption_key.dart @@ -9,7 +9,9 @@ Future> getEncryptionKey( if (stringifiedKey == null) { key = CakeHive.generateSecureKey(); final keyStringified = key.join(','); - await secureStorage.write(key: 'transactionDescriptionsBoxKey', value: keyStringified); + String storageKey = 'transactionDescriptionsBoxKey'; + await secureStorage.delete(key: storageKey); + await secureStorage.write(key: storageKey, value: keyStringified); } else { key = stringifiedKey.split(',').map((i) => int.parse(i)).toList(); } diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index 85df63b69..bdd6c24a7 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -64,6 +64,7 @@ class PreferencesKey { static const exchangeProvidersSelection = 'exchange-providers-selection'; static const autoGenerateSubaddressStatusKey = 'auto_generate_subaddress_status'; + static const moneroSeedType = 'monero_seed_type'; static const clearnetDonationLink = 'clearnet_donation_link'; static const onionDonationLink = 'onion_donation_link'; static const lastSeenAppVersion = 'last_seen_app_version'; diff --git a/lib/entities/seed_type.dart b/lib/entities/seed_type.dart new file mode 100644 index 000000000..0a1a191c6 --- /dev/null +++ b/lib/entities/seed_type.dart @@ -0,0 +1,36 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cw_core/enumerable_item.dart'; + +class SeedType extends EnumerableItem with Serializable { + const SeedType({required String title, required int raw}) : super(title: title, raw: raw); + + static const all = [SeedType.legacy, SeedType.polyseed]; + + static const defaultSeedType = legacy; + + static const legacy = SeedType(raw: 0, title: 'Legacy (25 words)'); + static const polyseed = SeedType(raw: 1, title: 'Polyseed (16 words)'); + + static SeedType deserialize({required int raw}) { + switch (raw) { + case 0: + return legacy; + case 1: + return polyseed; + default: + throw Exception('Unexpected token: $raw for SeedType deserialize'); + } + } + + @override + String toString() { + switch (this) { + case SeedType.legacy: + return S.current.seedtype_legacy; + case SeedType.polyseed: + return S.current.seedtype_polyseed; + default: + return ''; + } + } +} diff --git a/lib/entities/wallet_nft_response.dart b/lib/entities/wallet_nft_response.dart new file mode 100644 index 000000000..74a7508ab --- /dev/null +++ b/lib/entities/wallet_nft_response.dart @@ -0,0 +1,95 @@ +class WalletNFTsResponseModel { + final int? page; + final int? pageSize; + + final List? result; + final String? status; + + WalletNFTsResponseModel({this.page, this.pageSize, this.result, this.status}); + + factory WalletNFTsResponseModel.fromJson(Map json) { + return WalletNFTsResponseModel( + page: json['page'] as int?, + pageSize: json['page_size'] as int?, + result: (json['result'] as List?) + ?.map((x) => NFTAssetModel.fromJson(x as Map)) + .toList(), + status: json['status'] as String?, + ); + } +} + +class NFTAssetModel { + final String? tokenAddress; + final String? tokenId; + final String? contractType; + final String? name; + final String? symbol; + NormalizedMetadata? normalizedMetadata; + + NFTAssetModel( + {this.tokenAddress, + this.tokenId, + this.contractType, + this.name, + this.symbol, + this.normalizedMetadata}); + + factory NFTAssetModel.fromJson(Map json) { + return NFTAssetModel( + tokenAddress: json['token_address'] as String?, + tokenId: json['token_id'] as String?, + contractType: json['contract_type'] as String?, + name: json['name'] as String?, + symbol: json['symbol'] as String?, + normalizedMetadata: json['normalized_metadata'] != null + ? new NormalizedMetadata.fromJson( + json['normalized_metadata'] as Map) + : null, + ); + } +} + +class NormalizedMetadata { + final String? name; + final String? description; + final String? image; + NormalizedMetadata({ + this.name, + this.description, + this.image, + }); + + factory NormalizedMetadata.fromJson(Map json) { + return NormalizedMetadata( + name: json['name'] as String?, + description: json['description'] as String?, + image: json['image'] as String?, + ); + + } + + String? get imageUrl { + if (image == null) return image; + + if (image!.contains('ipfs.io')) return image; + + if (!image!.contains('ipfs')) return image; + + // IPFS public gateway provided by Cloudflare is https://cloudflare-ipfs.com/ipfs/ + // + // Here is an example of an ipfs image link: + // + // [ipfs://bafkreia2i2ctfexpovgzfff66wqhbmwwpvqjvozan7ioifzcnq76jharwu] + + //https://ipfs.io/ipfs/QmTRcRXo6cXByjHYHTVxGpag6vpocrG3rxjPC9PxKAArR9/1620.png + + const String ipfsPublicGateway = 'https://cloudflare-ipfs.com/ipfs/'; + + final ipfsPath = image?.split('//')[1]; + + final imageLink = '$ipfsPublicGateway$ipfsPath'; + + return imageLink; + } +} diff --git a/lib/monero/cw_monero.dart b/lib/monero/cw_monero.dart index 9ae248ca0..947d274a8 100644 --- a/lib/monero/cw_monero.dart +++ b/lib/monero/cw_monero.dart @@ -166,6 +166,10 @@ class CWMonero extends Monero { @override List getMoneroWordList(String language) { + if (language.startsWith("POLYSEED_")) { + final lang = language.replaceAll("POLYSEED_", ""); + return PolyseedLang.getByEnglishName(lang).words; + } switch (language.toLowerCase()) { case 'english': return EnglishMnemonics.words; @@ -223,8 +227,10 @@ class CWMonero extends Monero { WalletCredentials createMoneroNewWalletCredentials({ required String name, required String language, + required bool isPolyseed, String? password}) => - MoneroNewWalletCredentials(name: name, password: password, language: language); + MoneroNewWalletCredentials( + name: name, password: password, language: language, isPolyseed: isPolyseed); @override Map getKeys(Object wallet) { diff --git a/lib/router.dart b/lib/router.dart index 8174e0be8..313751ab3 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -5,6 +5,7 @@ import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart'; import 'package:cake_wallet/entities/contact_record.dart'; import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/entities/qr_view_data.dart'; +import 'package:cake_wallet/entities/wallet_nft_response.dart'; import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart'; import 'package:cake_wallet/src/screens/backup/backup_page.dart'; import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart'; @@ -13,6 +14,7 @@ import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart'; import 'package:cake_wallet/src/screens/buy/webview_page.dart'; import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart'; import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/nft_details_page.dart'; import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart'; import 'package:cake_wallet/src/screens/nano_accounts/nano_account_edit_or_create_page.dart'; import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart'; @@ -20,7 +22,7 @@ import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart'; import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart'; import 'package:cake_wallet/src/screens/restore/wallet_restore_choose_derivation.dart'; import 'package:cake_wallet/src/screens/settings/desktop_settings/desktop_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/display_settings_page.dart'; @@ -43,6 +45,7 @@ import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart'; import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart'; import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart'; import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart'; +import 'package:cake_wallet/src/screens/settings/tor_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart'; @@ -53,8 +56,10 @@ import 'package:cake_wallet/src/screens/support_other_links/support_other_links_ import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart'; import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart'; import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart'; +import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/utils/payment_request.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart'; import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; @@ -83,7 +88,6 @@ import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart'; import 'package:cake_wallet/src/screens/restore/restore_options_page.dart'; import 'package:cake_wallet/src/screens/send/send_page.dart'; import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart'; -import 'package:cake_wallet/src/screens/seed_language/seed_language_page.dart'; import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart'; import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_create_page.dart'; import 'package:cake_wallet/src/screens/contact/contact_list_page.dart'; @@ -101,7 +105,7 @@ import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dar import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart'; import 'package:flutter/services.dart'; import 'package:cake_wallet/wallet_types.g.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/address_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart'; import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart'; import 'package:cake_wallet/src/screens/ionia/ionia.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart'; @@ -110,6 +114,8 @@ import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/node.dart'; +import 'src/screens/dashboard/pages/nft_import_page.dart'; + late RouteSettings currentRouteSettings; Route createRoute(RouteSettings settings) { @@ -141,8 +147,9 @@ Route createRoute(RouteSettings settings) { case Routes.newWallet: final type = settings.arguments as WalletType; final walletNewVM = getIt.get(param1: type); + final settingsStore = getIt.get(); - return CupertinoPageRoute(builder: (_) => NewWalletPage(walletNewVM)); + return CupertinoPageRoute(builder: (_) => NewWalletPage(walletNewVM, settingsStore)); case Routes.setupPin: Function(PinCodeState, String)? callback; @@ -258,17 +265,6 @@ Route createRoute(RouteSettings settings) { case Routes.changeRep: return CupertinoPageRoute(builder: (_) => getIt.get()); - case Routes.seedLanguage: - final args = settings.arguments as List; - final type = args.first as WalletType; - final redirectRoute = args[1] as String; - - return CupertinoPageRoute(builder: (_) { - return SeedLanguage( - onConfirm: (context, lang) => - Navigator.of(context).popAndPushNamed(redirectRoute, arguments: [type, lang])); - }); - case Routes.walletList: return MaterialPageRoute( fullscreenDialog: true, builder: (_) => getIt.get()); @@ -615,6 +611,25 @@ Route createRoute(RouteSettings settings) { web3walletService: getIt.get(), launchUri: settings.arguments as Uri?, )); + + case Routes.nftDetailsPage: + return MaterialPageRoute( + builder: (_) => NFTDetailsPage( + nftAsset: settings.arguments as NFTAssetModel, + dashboardViewModel: getIt.get(), + ), + ); + + case Routes.importNFTPage: + return MaterialPageRoute( + builder: (_) => ImportNFTPage( + nftViewModel: settings.arguments as NFTViewModel, + ), + ); + + case Routes.torPage: + return MaterialPageRoute(builder: (_) => getIt.get()); + default: return MaterialPageRoute( builder: (_) => Scaffold( diff --git a/lib/routes.dart b/lib/routes.dart index 8b521247a..60801297a 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -7,7 +7,8 @@ class Routes { static const restoreOptions = '/restore_options'; static const restoreWalletFromSeedKeys = '/restore_wallet_from_seeds_keys'; static const restoreWalletTypeFromQR = '/restore_wallet_from_qr_code'; - static const restoreWalletChooseDerivation = '/restore_wallet_choose_derivation'; + static const restoreWalletChooseDerivation = + '/restore_wallet_choose_derivation'; static const dashboard = '/dashboard'; static const send = '/send'; static const transactionDetails = '/transaction_info'; @@ -17,7 +18,6 @@ class Routes { static const disclaimer = '/disclaimer'; static const readDisclaimer = '/read_disclaimer'; static const changeRep = '/change_representative'; - static const seedLanguage = '/seed_language'; static const walletList = '/view_model.wallet_list'; static const auth = '/auth'; static const newNode = '/new_node_list'; @@ -100,5 +100,9 @@ class Routes { static const editToken = '/edit_token'; static const manageNodes = '/manage_nodes'; static const managePowNodes = '/manage_pow_nodes'; - static const walletConnectConnectionsListing = '/wallet-connect-connections-listing'; + static const walletConnectConnectionsListing = + '/wallet-connect-connections-listing'; + static const nftDetailsPage = '/nft_details_page'; + static const importNFTPage = '/import_nft_page'; + static const torPage = '/tor_page'; } diff --git a/lib/src/screens/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index d4662c625..2552f4a4a 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -1,11 +1,10 @@ import 'dart:async'; import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart'; -import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/entities/main_actions.dart'; import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/market_place_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/themes/extensions/sync_indicator_theme.dart'; @@ -15,7 +14,6 @@ import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/yat_emoji_id.dart'; -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; @@ -23,8 +21,8 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/menu_widget.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/action_button.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/balance_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; diff --git a/lib/src/screens/dashboard/desktop_dashboard_page.dart b/lib/src/screens/dashboard/desktop_dashboard_page.dart index ed9fa6912..216ea152d 100644 --- a/lib/src/screens/dashboard/desktop_dashboard_page.dart +++ b/lib/src/screens/dashboard/desktop_dashboard_page.dart @@ -1,17 +1,14 @@ import 'dart:async'; import 'package:cake_wallet/entities/preferences_key.dart'; -import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/release_notes/release_notes_screen.dart'; import 'package:cake_wallet/src/screens/yat_emoji_id.dart'; -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/utils/version_comparator.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/balance_page.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart'; -import 'package:mobx/mobx.dart'; import 'package:cake_wallet/main.dart'; import 'package:cake_wallet/router.dart' as Router; import 'package:shared_preferences/shared_preferences.dart'; diff --git a/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart b/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart index 5e1f8d16a..20ddea361 100644 --- a/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart +++ b/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart @@ -1,7 +1,7 @@ import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/entities/main_actions.dart'; import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_action_button.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/market_place_page.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart'; import 'package:flutter/material.dart'; diff --git a/lib/src/screens/dashboard/widgets/address_page.dart b/lib/src/screens/dashboard/pages/address_page.dart similarity index 96% rename from lib/src/screens/dashboard/widgets/address_page.dart rename to lib/src/screens/dashboard/pages/address_page.dart index 0a924c504..f548a38c4 100644 --- a/lib/src/screens/dashboard/widgets/address_page.dart +++ b/lib/src/screens/dashboard/pages/address_page.dart @@ -6,10 +6,8 @@ import 'package:cake_wallet/anonpay/anonpay_donation_link_info.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/receive_page_option.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/present_receive_option_picker.dart'; -import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/gradient_background.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; -import 'package:cake_wallet/themes/extensions/receive_page_theme.dart'; import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; @@ -26,7 +24,6 @@ import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:keyboard_actions/keyboard_actions.dart'; import 'package:mobx/mobx.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; import 'package:cake_wallet/themes/extensions/balance_page_theme.dart'; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; @@ -192,8 +189,7 @@ class AddressPage extends BasePage { ? S.of(context).accounts_subaddresses : S.of(context).addresses; - if (!addressListViewModel.hasSilentAddresses && - dashboardViewModel.isAutoGenerateSubaddressesEnabled) { + if (dashboardViewModel.isAutoGenerateSubaddressesEnabled) { label = addressListViewModel.hasAccounts ? S.of(context).accounts : S.of(context).account; diff --git a/lib/src/screens/dashboard/widgets/balance_page.dart b/lib/src/screens/dashboard/pages/balance_page.dart similarity index 77% rename from lib/src/screens/dashboard/widgets/balance_page.dart rename to lib/src/screens/dashboard/pages/balance_page.dart index 41d4e7bb0..057352456 100644 --- a/lib/src/screens/dashboard/widgets/balance_page.dart +++ b/lib/src/screens/dashboard/pages/balance_page.dart @@ -1,6 +1,7 @@ import 'package:auto_size_text/auto_size_text.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/nft_listing_page.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/home_screen_account_widget.dart'; import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart'; import 'package:cake_wallet/src/widgets/introducing_card.dart'; @@ -11,15 +12,80 @@ import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; import 'package:cake_wallet/utils/feature_flag.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; +import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; class BalancePage extends StatelessWidget { - BalancePage({required this.dashboardViewModel, required this.settingsStore}); + BalancePage({ + required this.dashboardViewModel, + required this.settingsStore, + required this.nftViewModel, + }); final DashboardViewModel dashboardViewModel; + final NFTViewModel nftViewModel; final SettingsStore settingsStore; + @override + Widget build(BuildContext context) { + return Observer( + builder: (context) { + final isEthereumWallet = dashboardViewModel.type == WalletType.ethereum; + return DefaultTabController( + length: isEthereumWallet ? 2 : 1, + child: Column( + children: [ + if (isEthereumWallet) + Align( + alignment: Alignment.centerLeft, + child: Padding( + padding: const EdgeInsets.only(left: 8), + child: TabBar( + indicatorSize: TabBarIndicatorSize.label, + isScrollable: true, + physics: NeverScrollableScrollPhysics(), + labelStyle: TextStyle( + fontSize: 18, + fontFamily: 'Lato', + fontWeight: FontWeight.w600, + color: + Theme.of(context).extension()!.pageTitleTextColor, + height: 1, + ), + tabs: [ + Tab(text: 'My Crypto'), + Tab(text: 'My NFTs'), + ], + ), + ), + ), + Expanded( + child: TabBarView( + physics: NeverScrollableScrollPhysics(), + children: [ + CryptoBalanceWidget(dashboardViewModel: dashboardViewModel), + if (isEthereumWallet) NFTListingPage(nftViewModel: nftViewModel) + ], + ), + ), + ], + ), + ); + }, + ); + } +} + +class CryptoBalanceWidget extends StatelessWidget { + const CryptoBalanceWidget({ + super.key, + required this.dashboardViewModel, + }); + + final DashboardViewModel dashboardViewModel; + @override Widget build(BuildContext context) { return GestureDetector( @@ -38,7 +104,7 @@ class BalancePage extends StatelessWidget { accountName: dashboardViewModel.subname) : Column( children: [ - SizedBox(height: 56), + SizedBox(height: 16), Container( margin: const EdgeInsets.only(left: 24, bottom: 16), child: Observer( @@ -105,8 +171,7 @@ class BalancePage extends StatelessWidget { itemBuilder: (__, index) { final balance = dashboardViewModel.balanceViewModel.formattedBalances.elementAt(index); - return buildBalanceRow( - context, + return BalanceRowWidget( availableBalanceLabel: '${dashboardViewModel.balanceViewModel.availableBalanceLabel}', availableBalance: balance.availableBalance, @@ -130,20 +195,44 @@ class BalancePage extends StatelessWidget { ), ); } +} - Widget buildBalanceRow( - BuildContext context, { - required String availableBalanceLabel, - required String availableBalance, - required String availableFiatBalance, - required String additionalBalanceLabel, - required String additionalBalance, - required String additionalFiatBalance, - required String frozenBalance, - required String frozenFiatBalance, - required String currency, - required bool hasAdditionalBalance, - }) { +class BalanceRowWidget extends StatelessWidget { + const BalanceRowWidget({ + required this.availableBalanceLabel, + required this.availableBalance, + required this.availableFiatBalance, + required this.additionalBalanceLabel, + required this.additionalBalance, + required this.additionalFiatBalance, + required this.frozenBalance, + required this.frozenFiatBalance, + required this.currency, + required this.hasAdditionalBalance, + super.key, + }); + + final String availableBalanceLabel; + final String availableBalance; + final String availableFiatBalance; + final String additionalBalanceLabel; + final String additionalBalance; + final String additionalFiatBalance; + final String frozenBalance; + final String frozenFiatBalance; + final String currency; + final bool hasAdditionalBalance; + + // void _showBalanceDescription(BuildContext context) { + // showPopUp( + // context: context, + // builder: (_) => + // InformationPage(information: S.current.available_balance_description), + // ); + // } + + @override + Widget build(BuildContext context) { return Container( margin: const EdgeInsets.only(left: 16, right: 16), decoration: BoxDecoration( @@ -165,8 +254,9 @@ class BalancePage extends StatelessWidget { children: [ GestureDetector( behavior: HitTestBehavior.opaque, - onTap: hasAdditionalBalance ? () => - _showBalanceDescription(context, S.current.available_balance_description) + onTap: hasAdditionalBalance + ? () => + _showBalanceDescription(context, S.current.available_balance_description) : null, child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -229,8 +319,9 @@ class BalancePage extends StatelessWidget { if (frozenBalance.isNotEmpty) GestureDetector( behavior: HitTestBehavior.opaque, - onTap: hasAdditionalBalance ? - () => _showBalanceDescription(context, S.current.unavailable_balance_description) + onTap: hasAdditionalBalance + ? () => + _showBalanceDescription(context, S.current.unavailable_balance_description) : null, child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -253,9 +344,8 @@ class BalancePage extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 4), child: Icon(Icons.help_outline, size: 16, - color: Theme.of(context) - .extension()! - .labelTextColor), + color: + Theme.of(context).extension()!.labelTextColor), ), ], ), @@ -337,8 +427,6 @@ class BalancePage extends StatelessWidget { } void _showBalanceDescription(BuildContext context, String content) { - showPopUp( - context: context, - builder: (_) => InformationPage(information: content)); + showPopUp(context: context, builder: (_) => InformationPage(information: content)); } } diff --git a/lib/src/screens/dashboard/widgets/market_place_page.dart b/lib/src/screens/dashboard/pages/market_place_page.dart similarity index 100% rename from lib/src/screens/dashboard/widgets/market_place_page.dart rename to lib/src/screens/dashboard/pages/market_place_page.dart diff --git a/lib/src/screens/dashboard/pages/nft_details_page.dart b/lib/src/screens/dashboard/pages/nft_details_page.dart new file mode 100644 index 000000000..4bddb550b --- /dev/null +++ b/lib/src/screens/dashboard/pages/nft_details_page.dart @@ -0,0 +1,178 @@ +import 'package:cake_wallet/entities/wallet_nft_response.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/widgets/menu_widget.dart'; +import 'package:cake_wallet/src/screens/dashboard/widgets/nft_image_tile_widget.dart'; +import 'package:cake_wallet/src/widgets/gradient_background.dart'; +import 'package:cake_wallet/themes/extensions/balance_page_theme.dart'; +import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; +import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; +import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; +import 'package:flutter/material.dart'; + +class NFTDetailsPage extends BasePage { + NFTDetailsPage({required this.dashboardViewModel, required this.nftAsset}); + + final DashboardViewModel dashboardViewModel; + final NFTAssetModel nftAsset; + + @override + bool get gradientBackground => true; + + @override + Widget Function(BuildContext, Widget) get rootWrapper => + (BuildContext context, Widget scaffold) => + GradientBackground(scaffold: scaffold); + + @override + bool get resizeToAvoidBottomInset => false; + + @override + Widget get endDrawer => MenuWidget(dashboardViewModel); + + @override + Widget trailing(BuildContext context) { + final menuButton = Image.asset( + 'assets/images/menu.png', + color: + Theme.of(context).extension()!.pageTitleTextColor, + ); + + return Container( + alignment: Alignment.centerRight, + width: 40, + child: TextButton( + // FIX-ME: Style + //highlightColor: Colors.transparent, + //splashColor: Colors.transparent, + //padding: EdgeInsets.all(0), + onPressed: () => onOpenEndDrawer(), + child: Semantics(label: S.of(context).wallet_menu, child: menuButton), + ), + ); + } + + @override + Widget body(BuildContext context) { + return SingleChildScrollView( + child: Column( + children: [ + Container( + width: double.infinity, + margin: const EdgeInsets.all(16), + padding: const EdgeInsets.symmetric(vertical: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + border: Border.all( + color: Theme.of(context) + .extension()! + .cardBorderColor, + width: 1, + ), + color: Theme.of(context) + .extension()! + .syncedBackgroundColor, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: MediaQuery.sizeOf(context).height / 2.5, + width: double.infinity, + clipBehavior: Clip.hardEdge, + margin: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16.0), + border: Border.all( + color: Theme.of(context) + .extension()! + .cardBorderColor, + width: 1, + ), + color: Theme.of(context) + .extension()! + .syncedBackgroundColor, + + ), + child: NFTImageWidget( + imageUrl: nftAsset.normalizedMetadata?.imageUrl, + ), + ), + SizedBox(height: 16), + _NFTSingleInfoTile( + infoType: S.current.name, + infoValue: nftAsset.normalizedMetadata?.name ?? '', + ), + + if (nftAsset.normalizedMetadata?.description != null) ...[ + SizedBox(height: 16), + _NFTSingleInfoTile( + infoType: 'Description', + infoValue: nftAsset.normalizedMetadata?.description ?? '', + ), + ], + + SizedBox(height: 16), + _NFTSingleInfoTile( + infoType: 'Contract Name', + infoValue: nftAsset.name ?? '', + ), + SizedBox(height: 8), + _NFTSingleInfoTile( + infoType: 'Contract Symbol', + infoValue: nftAsset.symbol ?? '', + ), + ], + ), + ), + ], + ), + ); + } +} + +class _NFTSingleInfoTile extends StatelessWidget { + const _NFTSingleInfoTile({ + required this.infoType, + required this.infoValue, + }); + + final String infoType; + final String infoValue; + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( +infoType, + style: TextStyle( + fontSize: 12, + fontFamily: 'Lato', + fontWeight: FontWeight.w400, + color: Theme.of(context) + .extension()! + .labelTextColor, + height: 1, + ), + ), + SizedBox(height: 8), + Text( + infoValue, + style: TextStyle( + fontSize: 16, + fontFamily: 'Lato', + fontWeight: FontWeight.w600, + color: Theme.of(context) + .extension()! + .assetTitleColor, + height: 1, + ), + ), + ], + ), + ); + } +} diff --git a/lib/src/screens/dashboard/pages/nft_import_page.dart b/lib/src/screens/dashboard/pages/nft_import_page.dart new file mode 100644 index 000000000..6b40b46cc --- /dev/null +++ b/lib/src/screens/dashboard/pages/nft_import_page.dart @@ -0,0 +1,168 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/palette.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/widgets/address_text_field.dart'; +import 'package:cake_wallet/src/widgets/primary_button.dart'; +import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; +import 'package:cake_wallet/themes/extensions/seed_widget_theme.dart'; +import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; + +class ImportNFTPage extends StatefulWidget { + const ImportNFTPage({required this.nftViewModel, super.key}); + + final NFTViewModel nftViewModel; + + @override + State createState() => _ImportNFTPageState(); +} + +class _ImportNFTPageState extends State { + late TextEditingController tokenAddressController; + late TextEditingController tokenIDController; + + @override + void initState() { + super.initState(); + tokenAddressController = TextEditingController(); + tokenIDController = TextEditingController(); + } + + @override + void dispose() { + tokenAddressController.dispose(); + tokenIDController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return _ImportNFTPage( + nftViewModel: widget.nftViewModel, + tokenAddressController: tokenAddressController, + tokenIDController: tokenIDController, + ); + } +} + +class _ImportNFTPage extends BasePage { + _ImportNFTPage({ + required this.tokenIDController, + required this.tokenAddressController, + required this.nftViewModel, + }); + + final NFTViewModel nftViewModel; + final TextEditingController tokenIDController; + final TextEditingController tokenAddressController; + + @override + String? get title => S.current.import; + + @override + Widget body(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + + Text( + S.current.address, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + fontFamily: 'Lato', + fontWeight: FontWeight.w800, + color: + Theme.of(context).extension()!.hintTextColor, + height: 1, + ), + ), + AddressTextField( + controller: tokenAddressController, + options: [AddressTextFieldOption.paste], + onPushPasteButton: (context) async { + final clipboard = await Clipboard.getData('text/plain'); + final tokenAddress = clipboard?.text ?? ''; + + if (tokenAddress.isNotEmpty) { + tokenAddressController.text = tokenAddress; + } + }, + borderColor: Theme.of(context) + .extension()! + .textfieldUnderlineColor, + iconColor: Theme.of(context).primaryColor, + placeholder: '0x...', + textStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: PaletteDark.darkCyanBlue, + ), + hintStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: PaletteDark.darkCyanBlue, + ), + ), + + SizedBox(height: 48), + Text( + S.current.tokenID, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + fontFamily: 'Lato', + fontWeight: FontWeight.w800, + color: Theme.of(context).extension()!.hintTextColor, + height: 1, + ), + ), + AddressTextField( + controller: tokenIDController, + options: [AddressTextFieldOption.paste], + onPushPasteButton: (context) async { + final clipboard = await Clipboard.getData('text/plain'); + final tokenID = clipboard?.text ?? ''; + + if (tokenID.isNotEmpty) { + tokenIDController.text = tokenID; + } + }, + borderColor: Theme.of(context) + .extension()! + .textfieldUnderlineColor, + iconColor: Theme.of(context).primaryColor, + placeholder: S.current.enterTokenID, + textStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: PaletteDark.darkCyanBlue, + ), + hintStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: PaletteDark.darkCyanBlue, + ), + ), + Spacer(), + Observer(builder: (context) { + return LoadingPrimaryButton( + isLoading: nftViewModel.isImportNFTLoading, + text: S.current.import, + color: Theme.of(context).primaryColor, + textColor: Colors.white, + onPressed: () async { + await nftViewModel.importNFT(tokenAddressController.text, tokenIDController.text); + Navigator.pop(context); + }, + ); + }), + ], + ), + ); + } +} diff --git a/lib/src/screens/dashboard/pages/nft_listing_page.dart b/lib/src/screens/dashboard/pages/nft_listing_page.dart new file mode 100644 index 000000000..46690a969 --- /dev/null +++ b/lib/src/screens/dashboard/pages/nft_listing_page.dart @@ -0,0 +1,84 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/dashboard/widgets/nft_tile_widget.dart'; +import 'package:cake_wallet/src/widgets/primary_button.dart'; +import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; +import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart'; +import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; +import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; + +class NFTListingPage extends StatelessWidget { + final NFTViewModel nftViewModel; + + const NFTListingPage({super.key, required this.nftViewModel}); + @override + Widget build(BuildContext context) { + return Observer( + builder: (context) { + return Column( + children: [ + SizedBox(height: 16), + Padding( + padding: EdgeInsets.only(left: 16, right: 16, bottom: 16), + child: PrimaryButton( + text: S.current.import, + color: Theme.of(context) + .extension()! + .syncedBackgroundColor, + textColor: Colors.white, + onPressed: () => Navigator.pushNamed( + context, + Routes.importNFTPage, + arguments: nftViewModel, + ), + ), + ), + if (nftViewModel.isLoading) + Expanded( + child: Center( + child: CircularProgressIndicator( + backgroundColor: Theme.of(context).extension()!.textColor, + valueColor: AlwaysStoppedAnimation( + Theme.of(context) + .extension()! + .firstGradientBottomPanelColor, + ), + ), + ), + ), + if (!nftViewModel.isLoading) + Expanded( + child: nftViewModel.nftAssetByWalletModels.isEmpty + ? Center( + child: Text( + S.current.noNFTYet, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 20, + fontFamily: 'Lato', + fontWeight: FontWeight.w600, + color: Theme.of(context) + .extension()! + .pageTitleTextColor, + height: 1, + ), + ), + ) + : ListView.separated( + padding: EdgeInsets.symmetric(horizontal: 4, vertical: 16), + separatorBuilder: (context, index) => SizedBox(height: 8), + itemCount: nftViewModel.nftAssetByWalletModels.length, + itemBuilder: (context, index) { + final nftAsset = nftViewModel.nftAssetByWalletModels[index]; + return NFTTileWidget(nftAsset: nftAsset); + }, + ), + ) + ], + ); + }, + ); + } +} diff --git a/lib/src/screens/dashboard/widgets/transactions_page.dart b/lib/src/screens/dashboard/pages/transactions_page.dart similarity index 77% rename from lib/src/screens/dashboard/widgets/transactions_page.dart rename to lib/src/screens/dashboard/pages/transactions_page.dart index 5c7b78f3a..1be8a2a65 100644 --- a/lib/src/screens/dashboard/widgets/transactions_page.dart +++ b/lib/src/screens/dashboard/pages/transactions_page.dart @@ -46,9 +46,11 @@ class TransactionsPage extends StatelessWidget { return Padding( padding: const EdgeInsets.fromLTRB(24, 0, 24, 8), child: DashBoardRoundedCardWidget( - onTap: () => Navigator.of(context).pushNamed( - Routes.webViewPage, - arguments: ['', Uri.parse('https://guides.cakewallet.com/docs/bugs-service-status/why_are_my_funds_not_appearing/')]), + onTap: () => Navigator.of(context).pushNamed(Routes.webViewPage, arguments: [ + '', + Uri.parse( + 'https://guides.cakewallet.com/docs/bugs-service-status/why_are_my_funds_not_appearing/') + ]), title: S.of(context).syncing_wallet_alert_title, subTitle: S.of(context).syncing_wallet_alert_content, ), @@ -76,40 +78,34 @@ class TransactionsPage extends StatelessWidget { return Observer( builder: (_) => TransactionRow( - onTap: () => Navigator.of(context).pushNamed( - Routes.transactionDetails, - arguments: transaction), + onTap: () => Navigator.of(context) + .pushNamed(Routes.transactionDetails, arguments: transaction), direction: transaction.direction, - formattedDate: DateFormat('HH:mm') - .format(transaction.date), + formattedDate: DateFormat('HH:mm').format(transaction.date), formattedAmount: item.formattedCryptoAmount, - formattedFiatAmount: dashboardViewModel - .balanceViewModel.isFiatDisabled - ? '' - : item.formattedFiatAmount, + formattedFiatAmount: + dashboardViewModel.balanceViewModel.isFiatDisabled + ? '' + : item.formattedFiatAmount, isPending: transaction.isPending, - title: item.formattedTitle + - item.formattedStatus)); + title: item.formattedTitle + item.formattedStatus)); } if (item is AnonpayTransactionListItem) { final transactionInfo = item.transaction; return AnonpayTransactionRow( - onTap: () => Navigator.of(context).pushNamed( - Routes.anonPayDetailsPage, - arguments: transactionInfo), + onTap: () => Navigator.of(context) + .pushNamed(Routes.anonPayDetailsPage, arguments: transactionInfo), currency: transactionInfo.fiatAmount != null ? transactionInfo.fiatEquiv ?? '' - : CryptoCurrency.fromFullName( - transactionInfo.coinTo) + : CryptoCurrency.fromFullName(transactionInfo.coinTo) .name .toUpperCase(), provider: transactionInfo.provider, amount: transactionInfo.fiatAmount?.toString() ?? (transactionInfo.amountTo?.toString() ?? ''), - createdAt: DateFormat('HH:mm') - .format(transactionInfo.createdAt), + createdAt: DateFormat('HH:mm').format(transactionInfo.createdAt), ); } @@ -118,17 +114,14 @@ class TransactionsPage extends StatelessWidget { return Observer( builder: (_) => TradeRow( - onTap: () => Navigator.of(context).pushNamed( - Routes.tradeDetails, - arguments: trade), + onTap: () => Navigator.of(context) + .pushNamed(Routes.tradeDetails, arguments: trade), provider: trade.provider, from: trade.from, to: trade.to, - createdAtFormattedDate: - trade.createdAt != null - ? DateFormat('HH:mm') - .format(trade.createdAt!) - : null, + createdAtFormattedDate: trade.createdAt != null + ? DateFormat('HH:mm').format(trade.createdAt!) + : null, formattedAmount: item.tradeFormattedAmount)); } @@ -138,13 +131,12 @@ class TransactionsPage extends StatelessWidget { return Observer( builder: (_) => OrderRow( onTap: () => Navigator.of(context) - .pushNamed(Routes.orderDetails, - arguments: order), + .pushNamed(Routes.orderDetails, arguments: order), provider: order.provider, from: order.from!, to: order.to!, - createdAtFormattedDate: DateFormat('HH:mm') - .format(order.createdAt), + createdAtFormattedDate: + DateFormat('HH:mm').format(order.createdAt), formattedAmount: item.orderFormattedAmount, )); } diff --git a/lib/src/screens/dashboard/widgets/nft_image_tile_widget.dart b/lib/src/screens/dashboard/widgets/nft_image_tile_widget.dart new file mode 100644 index 000000000..d34ff02cb --- /dev/null +++ b/lib/src/screens/dashboard/widgets/nft_image_tile_widget.dart @@ -0,0 +1,37 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class NFTImageWidget extends StatelessWidget { + const NFTImageWidget({ + required this.imageUrl, + }); + + final String? imageUrl; + + @override + Widget build(BuildContext context) { + try { + if (imageUrl == null) return Icon(Icons.error); + + if (imageUrl!.contains('.svg')) { + return SvgPicture.network(imageUrl!); + } + + return Image.network( + imageUrl!, + fit: BoxFit.cover, + loadingBuilder: (BuildContext _, Widget child, ImageChunkEvent? loadingProgress) { + if (loadingProgress == null) { + return child; + } else { + return CupertinoActivityIndicator(animating: true); + } + }, + errorBuilder: (_, __, ___) => Icon(Icons.error), + ); + } catch (_) { + return Icon(Icons.error); + } + } +} diff --git a/lib/src/screens/dashboard/widgets/nft_tile_widget.dart b/lib/src/screens/dashboard/widgets/nft_tile_widget.dart new file mode 100644 index 000000000..0be027f44 --- /dev/null +++ b/lib/src/screens/dashboard/widgets/nft_tile_widget.dart @@ -0,0 +1,95 @@ +import 'package:cake_wallet/entities/wallet_nft_response.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/dashboard/widgets/nft_image_tile_widget.dart'; +import 'package:cake_wallet/themes/extensions/balance_page_theme.dart'; +import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class NFTTileWidget extends StatelessWidget { + const NFTTileWidget({super.key, required this.nftAsset}); + + final NFTAssetModel nftAsset; + @override + Widget build(BuildContext context) { + return InkWell( + onTap: () => Navigator.pushNamed(context, Routes.nftDetailsPage, + arguments: nftAsset), + child: Container( + width: double.infinity, + margin: const EdgeInsets.only(left: 16, right: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + border: Border.all( + color: Theme.of(context) + .extension()! + .cardBorderColor, + width: 1, + ), + color: Theme.of(context) + .extension()! + .syncedBackgroundColor, + ), + child: Row( + children: [ + Container( + height: 100, + width: 100, + clipBehavior: Clip.hardEdge, + margin: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16.0), + border: Border.all( + color: Theme.of(context) + .extension()! + .cardBorderColor, + width: 1, + ), + color: Theme.of(context) + .extension()! + .syncedBackgroundColor, + ), + child: NFTImageWidget( + imageUrl: nftAsset.normalizedMetadata?.imageUrl, + ), + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${nftAsset.name ?? ''} - ${nftAsset.symbol ?? ''}', + style: TextStyle( + fontSize: 12, + fontFamily: 'Lato', + fontWeight: FontWeight.w400, + color: Theme.of(context) + .extension()! + .labelTextColor, + height: 1, + ), + ), + SizedBox(height: 8), + Text( + nftAsset.normalizedMetadata?.name ?? nftAsset.name ?? "", + style: TextStyle( + fontSize: 20, + fontFamily: 'Lato', + fontWeight: FontWeight.w900, + color: Theme.of(context) + .extension()! + .assetTitleColor, + height: 1, + ), + ), + ], + ), + ) + ], + ), + ), + ); + } +} + diff --git a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart index 66a28042d..4b61e7889 100644 --- a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart +++ b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart @@ -1,6 +1,7 @@ import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/seed_phrase_length.dart'; +import 'package:cake_wallet/entities/seed_type.dart'; import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart'; @@ -97,16 +98,27 @@ class _AdvancedPrivacySettingsBodyState extends State( - title: S.current.seed_phrase_length, - items: SeedPhraseLength.values, - selectedItem: widget.privacySettingsViewModel.seedPhraseLength, - onItemSelected: (SeedPhraseLength length) { - widget.privacySettingsViewModel.setSeedPhraseLength(length); - }, - ); - }), + Observer(builder: (_) { + return SettingsPickerCell( + title: S.current.seed_phrase_length, + items: SeedPhraseLength.values, + selectedItem: widget.privacySettingsViewModel.seedPhraseLength, + onItemSelected: (SeedPhraseLength length) { + widget.privacySettingsViewModel.setSeedPhraseLength(length); + }, + ); + }), + if (widget.privacySettingsViewModel.hasSeedTypeOption) + Observer(builder: (_) { + return SettingsChoicesCell( + ChoicesListItem( + title: S.current.seedtype, + items: SeedType.all, + selectedItem: widget.privacySettingsViewModel.seedType, + onItemSelected: widget.privacySettingsViewModel.setSeedType, + ), + ); + }), ], ), bottomSectionPadding: EdgeInsets.all(24), diff --git a/lib/src/screens/new_wallet/new_wallet_page.dart b/lib/src/screens/new_wallet/new_wallet_page.dart index 97c23e8d5..03cb92261 100644 --- a/lib/src/screens/new_wallet/new_wallet_page.dart +++ b/lib/src/screens/new_wallet/new_wallet_page.dart @@ -1,5 +1,7 @@ import 'package:cake_wallet/entities/generate_name.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; +import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart'; +import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; import 'package:cake_wallet/main.dart'; import 'package:cake_wallet/routes.dart'; @@ -15,17 +17,20 @@ import 'package:cake_wallet/src/widgets/seed_language_selector.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; -import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart'; +import 'package:cake_wallet/src/widgets/seed_language_picker.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/view_model/wallet_new_vm.dart'; import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart'; import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; +import 'package:cake_wallet/entities/seed_type.dart'; +import 'package:cake_wallet/store/settings_store.dart'; class NewWalletPage extends BasePage { - NewWalletPage(this._walletNewVM); + NewWalletPage(this._walletNewVM, this._settingsStore); final WalletNewVM _walletNewVM; + final SettingsStore _settingsStore; final walletNameImage = Image.asset('assets/images/wallet_name.png'); @@ -36,14 +41,15 @@ class NewWalletPage extends BasePage { @override Widget body(BuildContext context) => WalletNameForm( - _walletNewVM, currentTheme.type == ThemeType.dark ? walletNameImage : walletNameLightImage); + _walletNewVM, currentTheme.type == ThemeType.dark ? walletNameImage : walletNameLightImage, _settingsStore); } class WalletNameForm extends StatefulWidget { - WalletNameForm(this._walletNewVM, this.walletImage); + WalletNameForm(this._walletNewVM, this.walletImage, this._settingsStore); final WalletNewVM _walletNewVM; final Image walletImage; + final SettingsStore _settingsStore; @override _WalletNameFormState createState() => _WalletNameFormState(_walletNewVM); @@ -85,6 +91,8 @@ class _WalletNameFormState extends State { }); } }); + + _setSeedType(SeedType.defaultSeedType); super.initState(); } @@ -180,22 +188,40 @@ class _WalletNameFormState extends State { ), ), ), + if (_walletNewVM.hasLanguageSelector) ...[ - Padding( - padding: EdgeInsets.only(top: 40), - child: Text( - S.of(context).seed_language_choose, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.w500, - color: Theme.of(context).extension()!.titleColor), + if (_walletNewVM.hasSeedType) ...[ + Observer( + builder: (BuildContext build) => Padding( + padding: EdgeInsets.only(top: 24), + child: SelectButton( + text: widget._settingsStore.moneroSeedType.title, + onTap: () async { + await showPopUp( + context: context, + builder: (_) => Picker( + items: SeedType.all, + selectedAtIndex: isPolyseed ? 1 : 0, + onItemSelected: _setSeedType, + isSeparated: false, + ), + ); + }, + ), + ), + ), + ], + Observer( + builder: (BuildContext build) => Padding( + padding: EdgeInsets.only(top: 10), + child: SeedLanguageSelector( + key: _languageSelectorKey, + initialSelected: defaultSeedLanguage, + seedType: _walletNewVM.hasSeedType + ? widget._settingsStore.moneroSeedType + : SeedType.legacy, + ), ), - ), - Padding( - padding: EdgeInsets.only(top: 24), - child: SeedLanguageSelector( - key: _languageSelectorKey, initialSelected: defaultSeedLanguage), ) ], Observer(builder: (context) { @@ -229,7 +255,7 @@ class _WalletNameFormState extends State { Navigator.of(context) .pushNamed(Routes.advancedPrivacySettings, arguments: _walletNewVM.type); }, - child: Text(S.of(context).advanced_privacy_settings), + child: Text(S.of(context).advanced_settings), ), ], )), @@ -253,8 +279,15 @@ class _WalletNameFormState extends State { } else { _walletNewVM.create( options: _walletNewVM.hasLanguageSelector - ? _languageSelectorKey.currentState!.selected + ? [_languageSelectorKey.currentState!.selected, isPolyseed] : null); } } + + bool get isPolyseed => widget._settingsStore.moneroSeedType == SeedType.polyseed; + + void _setSeedType(SeedType item) { + widget._settingsStore.moneroSeedType = item; + _languageSelectorKey.currentState?.selected = defaultSeedLanguage; // Reset Seed language + } } diff --git a/lib/src/screens/restore/wallet_restore_from_keys_form.dart b/lib/src/screens/restore/wallet_restore_from_keys_form.dart index 2c0dfa53f..588fd7187 100644 --- a/lib/src/screens/restore/wallet_restore_from_keys_form.dart +++ b/lib/src/screens/restore/wallet_restore_from_keys_form.dart @@ -133,7 +133,7 @@ class WalletRestoreFromKeysFromState extends State { widget.walletRestoreViewModel.type == WalletType.banano; return AddressTextField( controller: privateKeyController, - placeholder: nanoBased ? S.of(context).seed_key : S.of(context).private_key, + placeholder: nanoBased ? S.of(context).seed_hex_form : S.of(context).private_key, options: [AddressTextFieldOption.paste], buttonColor: Theme.of(context).hintColor, onPushPasteButton: (_) { diff --git a/lib/src/screens/restore/wallet_restore_from_seed_form.dart b/lib/src/screens/restore/wallet_restore_from_seed_form.dart index d520ee4ac..e08c85cb5 100644 --- a/lib/src/screens/restore/wallet_restore_from_seed_form.dart +++ b/lib/src/screens/restore/wallet_restore_from_seed_form.dart @@ -1,16 +1,19 @@ +import 'package:cake_wallet/core/wallet_name_validator.dart'; import 'package:cake_wallet/entities/generate_name.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; -import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart'; import 'package:cake_wallet/src/widgets/seed_widget.dart'; import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart'; import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/core/wallet_name_validator.dart'; import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:cake_wallet/entities/seed_type.dart'; +import 'package:cake_wallet/src/widgets/picker.dart'; +import 'package:cake_wallet/src/widgets/seed_language_picker.dart'; +import 'package:polyseed/polyseed.dart'; class WalletRestoreFromSeedForm extends StatefulWidget { WalletRestoreFromSeedForm({ @@ -47,21 +50,35 @@ class WalletRestoreFromSeedFormState extends State { blockchainHeightKey = GlobalKey(), formKey = GlobalKey(), languageController = TextEditingController(), - nameTextEditingController = TextEditingController(); + nameTextEditingController = TextEditingController(), + seedTypeController = TextEditingController(); final GlobalKey seedWidgetStateKey; final GlobalKey blockchainHeightKey; final TextEditingController languageController; final TextEditingController nameTextEditingController; + final TextEditingController seedTypeController; final GlobalKey formKey; String language; + bool isPolyseed = false; @override void initState() { _setLanguageLabel(language); + _setSeedType(SeedType.defaultSeedType); super.initState(); } + void onSeedChange(String seed) { + if (widget.type == WalletType.monero && Polyseed.isValidSeed(seed)) { + final lang = PolyseedLang.getByPhrase(seed); + + _changeSeedType(SeedType.polyseed); + _changeLanguage(lang.nameEnglish); + } + widget.onSeedChange?.call(seed); + } + @override Widget build(BuildContext context) { return Container( @@ -111,24 +128,58 @@ class WalletRestoreFromSeedFormState extends State { key: seedWidgetStateKey, language: language, type: widget.type, - onSeedChange: widget.onSeedChange), + onSeedChange: onSeedChange), + if (widget.type == WalletType.monero) + GestureDetector( + onTap: () async { + await showPopUp( + context: context, + builder: (_) => Picker( + items: SeedType.all, + selectedAtIndex: isPolyseed ? 1 : 0, + mainAxisAlignment: MainAxisAlignment.start, + onItemSelected: _changeSeedType, + isSeparated: false, + )); + }, + child: Container( + color: Colors.transparent, + padding: EdgeInsets.only(top: 20.0), + child: IgnorePointer( + child: BaseTextFormField( + controller: seedTypeController, + enableInteractiveSelection: false, + readOnly: true, + suffixIcon: expandIcon, + ), + ), + ), + ), if (widget.displayLanguageSelector) GestureDetector( - onTap: () async { - await showPopUp( - context: context, - builder: (_) => - SeedLanguagePicker(selected: language, onItemSelected: _changeLanguage)); - }, - child: Container( - color: Colors.transparent, - padding: EdgeInsets.only(top: 20.0), - child: IgnorePointer( - child: BaseTextFormField( - controller: languageController, - enableInteractiveSelection: false, - readOnly: true)))), - if (widget.displayBlockHeightSelector) + onTap: () async { + await showPopUp( + context: context, + builder: (_) => SeedLanguagePicker( + selected: language, + onItemSelected: _changeLanguage, + seedType: isPolyseed ? SeedType.polyseed : SeedType.legacy, + )); + }, + child: Container( + color: Colors.transparent, + padding: EdgeInsets.only(top: 20.0), + child: IgnorePointer( + child: BaseTextFormField( + controller: languageController, + enableInteractiveSelection: false, + readOnly: true, + suffixIcon: expandIcon, + ), + ), + ), + ), + if (!isPolyseed && widget.displayBlockHeightSelector) BlockchainHeightWidget( focusNode: widget.blockHeightFocusNode, key: blockchainHeightKey, @@ -143,14 +194,37 @@ class WalletRestoreFromSeedFormState extends State { ])); } + Widget get expandIcon => Container( + padding: EdgeInsets.all(18), + width: 24, + height: 24, + child: Image.asset( + 'assets/images/arrow_bottom_purple_icon.png', + height: 8, + color: Theme.of(context).hintColor, + ), + ); + void _changeLanguage(String language) { + final setLang = isPolyseed ? "POLYSEED_$language" : language; setState(() { - this.language = language; - seedWidgetStateKey.currentState!.changeSeedLanguage(language); - _setLanguageLabel(language); - widget.onLanguageChange?.call(language); + this.language = setLang; + seedWidgetStateKey.currentState!.changeSeedLanguage(setLang); + _setLanguageLabel(setLang); + widget.onLanguageChange?.call(setLang); }); } - void _setLanguageLabel(String language) => languageController.text = '$language (Seed language)'; + void _setLanguageLabel(String language) => + languageController.text = '${language.replaceAll("POLYSEED_", "")} (Seed language)'; + + void _changeSeedType(SeedType item) { + _setSeedType(item); + _changeLanguage('English'); + } + + void _setSeedType(SeedType item) { + setState(() => isPolyseed = item == SeedType.polyseed); + seedTypeController.text = item.toString(); + } } diff --git a/lib/src/screens/restore/wallet_restore_page.dart b/lib/src/screens/restore/wallet_restore_page.dart index 691cde8c6..5b5348715 100644 --- a/lib/src/screens/restore/wallet_restore_page.dart +++ b/lib/src/screens/restore/wallet_restore_page.dart @@ -1,29 +1,30 @@ +import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/screens/restore/wallet_restore_from_keys_form.dart'; +import 'package:cake_wallet/src/screens/restore/wallet_restore_from_seed_form.dart'; +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; +import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/themes/extensions/keyboard_theme.dart'; -import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; +import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cake_wallet/view_model/restore/restore_mode.dart'; +import 'package:cake_wallet/view_model/wallet_restore_view_model.dart'; import 'package:cw_core/nano_account_info_response.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:keyboard_actions/keyboard_actions.dart'; import 'package:mobx/mobx.dart'; -import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:polyseed/polyseed.dart'; import 'package:smooth_page_indicator/smooth_page_indicator.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/core/execution_state.dart'; -import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; -import 'package:cake_wallet/view_model/wallet_restore_view_model.dart'; -import 'package:cake_wallet/src/screens/restore/wallet_restore_from_keys_form.dart'; -import 'package:cake_wallet/src/screens/restore/wallet_restore_from_seed_form.dart'; -import 'package:cake_wallet/src/widgets/primary_button.dart'; -import 'package:cake_wallet/utils/show_pop_up.dart'; -import 'package:cake_wallet/view_model/restore/restore_mode.dart'; -import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart'; class WalletRestorePage extends BasePage { WalletRestorePage(this.walletRestoreViewModel) @@ -50,27 +51,13 @@ class WalletRestorePage extends BasePage { } }, onSeedChange: (String seed) { - if (walletRestoreViewModel.hasBlockchainHeightLanguageSelector) { - final hasHeight = walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey - .currentState!.restoreHeightController.text.isNotEmpty; - if (hasHeight) { - walletRestoreViewModel.isButtonEnabled = _isValidSeed(); - } - } else { - walletRestoreViewModel.isButtonEnabled = _isValidSeed(); - } + final isPolyseed = + walletRestoreViewModel.type == WalletType.monero && Polyseed.isValidSeed(seed); + _validateOnChange(isPolyseed: isPolyseed); }, - onLanguageChange: (_) { - if (walletRestoreViewModel.hasBlockchainHeightLanguageSelector) { - final hasHeight = walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey - .currentState!.restoreHeightController.text.isNotEmpty; - - if (hasHeight) { - walletRestoreViewModel.isButtonEnabled = _isValidSeed(); - } - } else { - walletRestoreViewModel.isButtonEnabled = _isValidSeed(); - } + onLanguageChange: (String language) { + final isPolyseed = language.startsWith("POLYSEED_"); + _validateOnChange(isPolyseed: isPolyseed); })); break; case WalletRestoreMode.keys: @@ -225,7 +212,7 @@ class WalletRestorePage extends BasePage { Navigator.of(context).pushNamed(Routes.advancedPrivacySettings, arguments: walletRestoreViewModel.type); }, - child: Text(S.of(context).advanced_privacy_settings), + child: Text(S.of(context).advanced_settings), ), ], ), @@ -238,9 +225,26 @@ class WalletRestorePage extends BasePage { ); } + void _validateOnChange({bool isPolyseed = false}) { + if (!isPolyseed && walletRestoreViewModel.hasBlockchainHeightLanguageSelector) { + final hasHeight = walletRestoreFromSeedFormKey + .currentState?.blockchainHeightKey.currentState?.restoreHeightController.text.isNotEmpty; + + if (hasHeight == true) { + walletRestoreViewModel.isButtonEnabled = _isValidSeed(); + } + } else { + walletRestoreViewModel.isButtonEnabled = _isValidSeed(); + } + } + bool _isValidSeed() { - final seedWords = - walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.text.split(' '); + final seedPhrase = + walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.text; + if (walletRestoreViewModel.type == WalletType.monero && Polyseed.isValidSeed(seedPhrase)) + return true; + + final seedWords = seedPhrase.split(' '); if ((walletRestoreViewModel.type == WalletType.monero || walletRestoreViewModel.type == WalletType.haven) && @@ -285,7 +289,8 @@ class WalletRestorePage extends BasePage { if (walletRestoreViewModel.hasBlockchainHeightLanguageSelector) { credentials['height'] = - walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey.currentState!.height; + walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey.currentState?.height ?? + -1; } credentials['name'] = diff --git a/lib/src/screens/seed_language/seed_language_page.dart b/lib/src/screens/seed_language/seed_language_page.dart deleted file mode 100644 index b15da0375..000000000 --- a/lib/src/screens/seed_language/seed_language_page.dart +++ /dev/null @@ -1,95 +0,0 @@ -import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; -import 'package:cake_wallet/themes/theme_base.dart'; -import 'package:cake_wallet/src/widgets/seed_language_selector.dart'; -import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/src/widgets/primary_button.dart'; -import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; -import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart'; - -class SeedLanguage extends BasePage { - SeedLanguage({required this.onConfirm}); - - final Function(BuildContext, String) onConfirm; - - final walletNameImage = Image.asset('assets/images/wallet_name.png'); - final walletNameLightImage = - Image.asset('assets/images/wallet_name_light.png'); - - @override - String get title => S.current.wallet_list_restore_wallet; - - @override - Widget body(BuildContext context) => - SeedLanguageForm( - onConfirm: onConfirm, - walletImage: currentTheme.type == ThemeType.dark - ? walletNameImage : walletNameLightImage); -} - -class SeedLanguageForm extends StatefulWidget { - SeedLanguageForm({required this.onConfirm, required this.walletImage}); - - final Function(BuildContext, String) onConfirm; - final Image walletImage; - - @override - SeedLanguageFormState createState() => SeedLanguageFormState(); -} - -class SeedLanguageFormState extends State { - static const aspectRatioImage = 1.22; - final _languageSelectorKey = GlobalKey(); - - @override - Widget build(BuildContext context) { - return Container( - padding: EdgeInsets.only(top: 24), - child: ScrollableWithBottomSection( - contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), - content: - Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ - Padding( - padding: EdgeInsets.only(left: 12, right: 12), - child: AspectRatio( - aspectRatio: aspectRatioImage, - child: FittedBox(child: widget.walletImage, - fit: BoxFit.fill)), - ), - Padding( - padding: EdgeInsets.only(top: 40), - child: Text( - S.of(context).seed_language_choose, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.w500, - color: - Theme.of(context).extension()!.titleColor), - ), - ), - Padding( - padding: EdgeInsets.only(top: 24), - child: SeedLanguageSelector( - key: _languageSelectorKey, - initialSelected: defaultSeedLanguage), - ) - ]), - bottomSectionPadding: - EdgeInsets.only(left: 24, right: 24, bottom: 24), - bottomSection: Observer( - builder: (context) { - return PrimaryButton( - onPressed: () => widget.onConfirm( - context, _languageSelectorKey.currentState!.selected), - text: S.of(context).seed_language_next, - color: Colors.green, - textColor: Colors.white); - }, - )), - ); - } -} diff --git a/lib/src/screens/seed_language/widgets/seed_language_picker.dart b/lib/src/screens/seed_language/widgets/seed_language_picker.dart deleted file mode 100644 index 742a31e61..000000000 --- a/lib/src/screens/seed_language/widgets/seed_language_picker.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:cake_wallet/src/widgets/picker.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:cake_wallet/generated/i18n.dart'; - -List flagImages = [ - Image.asset('assets/images/flags/usa.png'), - Image.asset('assets/images/flags/chn.png'), - Image.asset('assets/images/flags/nld.png'), - Image.asset('assets/images/flags/deu.png'), - Image.asset('assets/images/flags/jpn.png'), - Image.asset('assets/images/flags/prt.png'), - Image.asset('assets/images/flags/rus.png'), - Image.asset('assets/images/flags/esp.png'), - Image.asset('assets/images/flags/fra.png'), - Image.asset('assets/images/flags/ita.png'), -]; - -const List languageCodes = [ - 'Eng', - 'Chi', - 'Ned', - 'Ger', - 'Jap', - 'Por', - 'Rus', - 'Esp', - 'Fre', - 'Ita', -]; - -const defaultSeedLanguage = 'English'; - -const List seedLanguages = [ - defaultSeedLanguage, - 'Chinese (simplified)', - 'Dutch', - 'German', - 'Japanese', - 'Portuguese', - 'Russian', - 'Spanish', - 'French', - 'Italian', -]; - -enum Places { topLeft, topRight, bottomLeft, bottomRight, inside } - -class SeedLanguagePicker extends StatefulWidget { - SeedLanguagePicker({Key? key, this.selected = defaultSeedLanguage, required this.onItemSelected}) - : super(key: key); - - final String selected; - final Function(String) onItemSelected; - - @override - SeedLanguagePickerState createState() => - SeedLanguagePickerState(selected: selected, onItemSelected: onItemSelected); -} - -class SeedLanguagePickerState extends State { - SeedLanguagePickerState({required this.selected, required this.onItemSelected}); - - final String selected; - final Function(String) onItemSelected; - - @override - Widget build(BuildContext context) { - return Picker( - selectedAtIndex: seedLanguages.indexOf(selected), - items: seedLanguages, - images: flagImages, - isGridView: true, - title: S.of(context).seed_choose, - hintText: S.of(context).seed_choose, - matchingCriteria: (String language, String searchText) { - return language.toLowerCase().contains(searchText); - }, - onItemSelected: onItemSelected, - ); - } -} diff --git a/lib/src/screens/settings/connection_sync_page.dart b/lib/src/screens/settings/connection_sync_page.dart index e4763653b..50881dd57 100644 --- a/lib/src/screens/settings/connection_sync_page.dart +++ b/lib/src/screens/settings/connection_sync_page.dart @@ -3,8 +3,8 @@ import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arro import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/wallet_connect_button.dart'; -import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart'; import 'package:cake_wallet/utils/device_info.dart'; +import 'package:cake_wallet/utils/feature_flag.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/settings/sync_mode.dart'; @@ -90,7 +90,12 @@ class ConnectionSyncPage extends BasePage { onTap: () => Navigator.of(context).pushNamed(Routes.walletConnectConnectionsListing), ), const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), - ] + ], + if (FeatureFlag.isInAppTorEnabled) + SettingsCellWithArrow( + title: S.current.tor_connection, + handler: (context) => Navigator.of(context).pushNamed(Routes.torPage), + ), ], ), ); diff --git a/lib/src/screens/settings/privacy_page.dart b/lib/src/screens/settings/privacy_page.dart index 7a8671224..b1927648a 100644 --- a/lib/src/screens/settings/privacy_page.dart +++ b/lib/src/screens/settings/privacy_page.dart @@ -22,81 +22,83 @@ class PrivacyPage extends BasePage { @override Widget body(BuildContext context) { - return Container( - padding: EdgeInsets.only(top: 10), - child: Observer(builder: (_) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - SettingsChoicesCell( - ChoicesListItem( - title: S.current.fiat_api, - items: FiatApiMode.all, - selectedItem: _privacySettingsViewModel.fiatApiMode, - onItemSelected: (FiatApiMode fiatApiMode) => - _privacySettingsViewModel.setFiatMode(fiatApiMode), + return SingleChildScrollView( + child: Container( + padding: EdgeInsets.only(top: 10), + child: Observer(builder: (_) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SettingsChoicesCell( + ChoicesListItem( + title: S.current.fiat_api, + items: FiatApiMode.all, + selectedItem: _privacySettingsViewModel.fiatApiMode, + onItemSelected: (FiatApiMode fiatApiMode) => + _privacySettingsViewModel.setFiatMode(fiatApiMode), + ), ), - ), - SettingsChoicesCell( - ChoicesListItem( - title: S.current.exchange, - items: ExchangeApiMode.all, - selectedItem: _privacySettingsViewModel.exchangeStatus, - onItemSelected: (ExchangeApiMode mode) => - _privacySettingsViewModel.setExchangeApiMode(mode), + SettingsChoicesCell( + ChoicesListItem( + title: S.current.exchange, + items: ExchangeApiMode.all, + selectedItem: _privacySettingsViewModel.exchangeStatus, + onItemSelected: (ExchangeApiMode mode) => + _privacySettingsViewModel.setExchangeApiMode(mode), + ), ), - ), - SettingsSwitcherCell( - title: S.current.settings_save_recipient_address, - value: _privacySettingsViewModel.shouldSaveRecipientAddress, - onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setShouldSaveRecipientAddress(value); - }), - if (_privacySettingsViewModel.isAutoGenerateSubaddressesVisible) SettingsSwitcherCell( - title: S.current.auto_generate_subaddresses, - value: _privacySettingsViewModel.isAutoGenerateSubaddressesEnabled, - onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setAutoGenerateSubaddresses(value); - }, - ), - if (DeviceInfo.instance.isMobile) - SettingsSwitcherCell( - title: S.current.prevent_screenshots, - value: _privacySettingsViewModel.isAppSecure, + title: S.current.settings_save_recipient_address, + value: _privacySettingsViewModel.shouldSaveRecipientAddress, onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setIsAppSecure(value); + _privacySettingsViewModel.setShouldSaveRecipientAddress(value); }), - SettingsSwitcherCell( - title: S.current.disable_buy, - value: _privacySettingsViewModel.disableBuy, - onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setDisableBuy(value); - }), - SettingsSwitcherCell( - title: S.current.disable_sell, - value: _privacySettingsViewModel.disableSell, - onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setDisableSell(value); - }), - if (_privacySettingsViewModel.canUseEtherscan) - SettingsSwitcherCell( - title: S.current.etherscan_history, - value: _privacySettingsViewModel.useEtherscan, + if (_privacySettingsViewModel.isAutoGenerateSubaddressesVisible) + SettingsSwitcherCell( + title: S.current.auto_generate_subaddresses, + value: _privacySettingsViewModel.isAutoGenerateSubaddressesEnabled, onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setUseEtherscan(value); + _privacySettingsViewModel.setAutoGenerateSubaddresses(value); + }, + ), + if (DeviceInfo.instance.isMobile) + SettingsSwitcherCell( + title: S.current.prevent_screenshots, + value: _privacySettingsViewModel.isAppSecure, + onValueChange: (BuildContext _, bool value) { + _privacySettingsViewModel.setIsAppSecure(value); + }), + SettingsSwitcherCell( + title: S.current.disable_buy, + value: _privacySettingsViewModel.disableBuy, + onValueChange: (BuildContext _, bool value) { + _privacySettingsViewModel.setDisableBuy(value); }), - SettingsCellWithArrow( - title: S.current.domain_looks_up, - handler: (context) => Navigator.of(context).pushNamed(Routes.domainLookupsPage), - ), - SettingsCellWithArrow( - title: 'Trocador providers', - handler: (context) => Navigator.of(context).pushNamed(Routes.trocadorProvidersPage), - ), - ], - ); - }), + SettingsSwitcherCell( + title: S.current.disable_sell, + value: _privacySettingsViewModel.disableSell, + onValueChange: (BuildContext _, bool value) { + _privacySettingsViewModel.setDisableSell(value); + }), + if (_privacySettingsViewModel.canUseEtherscan) + SettingsSwitcherCell( + title: S.current.etherscan_history, + value: _privacySettingsViewModel.useEtherscan, + onValueChange: (BuildContext _, bool value) { + _privacySettingsViewModel.setUseEtherscan(value); + }), + SettingsCellWithArrow( + title: S.current.domain_looks_up, + handler: (context) => Navigator.of(context).pushNamed(Routes.domainLookupsPage), + ), + SettingsCellWithArrow( + title: 'Trocador providers', + handler: (context) => Navigator.of(context).pushNamed(Routes.trocadorProvidersPage), + ), + ], + ); + }), + ), ); } } diff --git a/lib/src/screens/settings/tor_page.dart b/lib/src/screens/settings/tor_page.dart new file mode 100644 index 000000000..d1d5c7e83 --- /dev/null +++ b/lib/src/screens/settings/tor_page.dart @@ -0,0 +1,267 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/store/app_store.dart'; +import 'package:flutter/material.dart'; +import 'package:tor/tor.dart'; + +class TorPage extends BasePage { + final AppStore appStore; + + TorPage(this.appStore); + + @override + Widget body(BuildContext context) { + return TorPageBody(appStore); + } +} + +class TorPageBody extends StatefulWidget { + final AppStore appStore; + + const TorPageBody(this.appStore, {Key? key}) : super(key: key); + + @override + State createState() => _TorPageBodyState(); +} + +class _TorPageBodyState extends State { + bool torEnabled = false; + bool connecting = false; + + // Set the default text for the host input field. + final hostController = TextEditingController(text: 'https://icanhazip.com/'); + + // https://check.torproject.org is another good option. + + Future startTor() async { + setState(() { + connecting = true; // Update flag + }); + + await Tor.init(); + + // Start the proxy + await Tor.instance.start(); + + // Toggle started flag. + setState(() { + torEnabled = Tor.instance.enabled; // Update flag + connecting = false; + }); + + final node = widget.appStore.settingsStore.getCurrentNode(widget.appStore.wallet!.type); + if (node.socksProxyAddress?.isEmpty ?? true) { + node.socksProxyAddress = "${InternetAddress.loopbackIPv4.address}:${Tor.instance.port}"; + } + widget.appStore.wallet!.connectToNode(node: node); + + print('Done awaiting; tor should be running'); + } + + Future endTor() async { + // Start the proxy + Tor.instance.disable(); + + // Toggle started flag. + setState(() { + torEnabled = Tor.instance.enabled; // Update flag + }); + + print('Done awaiting; tor should be stopped'); + } + + @override + void initState() { + super.initState(); + + torEnabled = Tor.instance.enabled; + } + + @override + void dispose() { + // Clean up the controller when the widget is disposed. + hostController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Container( + padding: const EdgeInsets.all(10), + child: connecting + ? ConnectingScreen() + : torEnabled + ? DisconnectScreen(disconnect: endTor) + : ConnectScreen(connect: startTor), + ), + ); + } +} + +class ConnectScreen extends StatelessWidget { + final Function() connect; + + const ConnectScreen({super.key, required this.connect}); + + @override + Widget build(BuildContext context) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 200, + height: 200, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.blue, + ), + child: Icon( + Icons.lock, + color: Colors.white, + size: 100, + ), + ), + SizedBox(height: 20), + Text( + 'Connect to Tor', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 10), + Text( + 'Your connection to the Tor network ensures privacy and security.', + style: TextStyle( + fontSize: 16, + color: Colors.grey, + ), + textAlign: TextAlign.center, + ), + SizedBox(height: 30), + ElevatedButton( + onPressed: connect, + style: ElevatedButton.styleFrom( + primary: Colors.blue, + padding: EdgeInsets.symmetric(horizontal: 40, vertical: 15), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30), + ), + ), + child: Text( + 'Connect', + style: TextStyle( + fontSize: 18, + color: Colors.white, + ), + ), + ), + ], + ), + ); + } +} + +class DisconnectScreen extends StatelessWidget { + final Function() disconnect; + + const DisconnectScreen({super.key, required this.disconnect}); + + @override + Widget build(BuildContext context) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 200, + height: 200, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.green, + ), + child: Icon( + Icons.check, + color: Colors.white, + size: 100, + ), + ), + SizedBox(height: 20), + Text( + 'Connected to Tor', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 10), + Text( + 'You are currently connected to the Tor network.', + style: TextStyle( + fontSize: 16, + color: Colors.grey, + ), + textAlign: TextAlign.center, + ), + SizedBox(height: 30), + ElevatedButton( + onPressed: disconnect, + style: ElevatedButton.styleFrom( + primary: Colors.red, + padding: EdgeInsets.symmetric(horizontal: 40, vertical: 15), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30), + ), + ), + child: Text( + 'Disconnect', + style: TextStyle( + fontSize: 18, + color: Colors.white, + ), + ), + ), + ], + ), + ); + } +} + +class ConnectingScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 200, + height: 200, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.yellow, + ), + child: Icon( + Icons.hourglass_bottom, + color: Colors.white, + size: 100, + ), + ), + SizedBox(height: 20), + Text( + 'Connecting...', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 10), + ], + ), + ); + } +} diff --git a/lib/src/screens/support_chat/widgets/chatwoot_widget.dart b/lib/src/screens/support_chat/widgets/chatwoot_widget.dart index d9a5f9bb8..73403d667 100644 --- a/lib/src/screens/support_chat/widgets/chatwoot_widget.dart +++ b/lib/src/screens/support_chat/widgets/chatwoot_widget.dart @@ -57,6 +57,8 @@ class ChatwootWidgetState extends State { return true; } - Future storeCookie(String value) async => - await widget.secureStorage.write(key: COOKIE_KEY, value: value); + Future storeCookie(String value) async { + await widget.secureStorage.delete(key: COOKIE_KEY); + await widget.secureStorage.write(key: COOKIE_KEY, value: value); + } } diff --git a/lib/src/screens/wallet_connect/wc_connections_listing_view.dart b/lib/src/screens/wallet_connect/wc_connections_listing_view.dart index 359d96b26..eda2a748f 100644 --- a/lib/src/screens/wallet_connect/wc_connections_listing_view.dart +++ b/lib/src/screens/wallet_connect/wc_connections_listing_view.dart @@ -31,6 +31,8 @@ class WalletConnectConnectionsView extends StatelessWidget { final actualLinkList = launchUri.query.split("uri="); + if (actualLinkList.length <= 1) return; + final query = actualLinkList[1]; final uri = Uri.decodeComponent(query); diff --git a/lib/src/widgets/address_text_field.dart b/lib/src/widgets/address_text_field.dart index d18032f6a..f98e0439b 100644 --- a/lib/src/widgets/address_text_field.dart +++ b/lib/src/widgets/address_text_field.dart @@ -68,13 +68,16 @@ class AddressTextField extends StatelessWidget { enabled: isActive, controller: controller, focusNode: focusNode, + style: textStyle ?? TextStyle( fontSize: 16, color: Theme.of(context).extension()!.titleColor), decoration: InputDecoration( + suffixIcon: SizedBox( width: prefixIconWidth * options.length + (spaceBetweenPrefixIcons * options.length), ), + hintStyle: hintStyle ?? TextStyle(fontSize: 16, color: Theme.of(context).hintColor), hintText: placeholder ?? S.current.widgets_address, focusedBorder: isBorderExist diff --git a/lib/src/widgets/seed_language_picker.dart b/lib/src/widgets/seed_language_picker.dart new file mode 100644 index 000000000..3bb0376b1 --- /dev/null +++ b/lib/src/widgets/seed_language_picker.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:cake_wallet/src/widgets/picker.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:cake_wallet/generated/i18n.dart'; + +import 'package:cake_wallet/entities/seed_type.dart'; + +class SeedLanguagePickerOption { + SeedLanguagePickerOption(this.name, this.nameLocalized, this.image, this.supportedSeedTypes); + + final String name; + final String nameLocalized; + final Image image; + final List supportedSeedTypes; +} + +final List seedLanguages = [ + SeedLanguagePickerOption('English', S.current.seed_language_english, + Image.asset('assets/images/flags/usa.png'), [SeedType.legacy, SeedType.polyseed]), + SeedLanguagePickerOption('Chinese (simplified)', S.current.seed_language_chinese, + Image.asset('assets/images/flags/chn.png'), [SeedType.legacy, SeedType.polyseed]), + SeedLanguagePickerOption('Chinese (Traditional)', S.current.seed_language_chinese_traditional, + Image.asset('assets/images/flags/chn.png'), [SeedType.polyseed]), + SeedLanguagePickerOption('Dutch', S.current.seed_language_dutch, + Image.asset('assets/images/flags/nld.png'), [SeedType.legacy]), + SeedLanguagePickerOption('German', S.current.seed_language_german, + Image.asset('assets/images/flags/deu.png'), [SeedType.legacy]), + SeedLanguagePickerOption('Japanese', S.current.seed_language_japanese, + Image.asset('assets/images/flags/jpn.png'), [SeedType.legacy, SeedType.polyseed]), + SeedLanguagePickerOption('Korean', S.current.seed_language_korean, + Image.asset('assets/images/flags/kor.png'), [SeedType.polyseed]), + SeedLanguagePickerOption('Portuguese', S.current.seed_language_portuguese, + Image.asset('assets/images/flags/prt.png'), [SeedType.legacy, SeedType.polyseed]), + SeedLanguagePickerOption('Russian', S.current.seed_language_russian, + Image.asset('assets/images/flags/rus.png'), [SeedType.legacy]), + SeedLanguagePickerOption('Czech', S.current.seed_language_czech, + Image.asset('assets/images/flags/czk.png'), [SeedType.polyseed]), + SeedLanguagePickerOption('Spanish', S.current.seed_language_spanish, + Image.asset('assets/images/flags/esp.png'), [SeedType.legacy, SeedType.polyseed]), + SeedLanguagePickerOption('French', S.current.seed_language_french, + Image.asset('assets/images/flags/fra.png'), [SeedType.legacy, SeedType.polyseed]), + SeedLanguagePickerOption('Italian', S.current.seed_language_italian, + Image.asset('assets/images/flags/ita.png'), [SeedType.legacy, SeedType.polyseed]), +]; + +const defaultSeedLanguage = 'English'; + +enum Places { topLeft, topRight, bottomLeft, bottomRight, inside } + +class SeedLanguagePicker extends StatefulWidget { + SeedLanguagePicker( + {Key? key, + this.selected = defaultSeedLanguage, + this.seedType = SeedType.defaultSeedType, + required this.onItemSelected}) + : super(key: key); + + final SeedType seedType; + final String selected; + final Function(String) onItemSelected; + + @override + SeedLanguagePickerState createState() => SeedLanguagePickerState( + selected: selected, onItemSelected: onItemSelected, seedType: seedType); +} + +class SeedLanguagePickerState extends State { + SeedLanguagePickerState( + {required this.selected, required this.onItemSelected, required this.seedType}); + + final SeedType seedType; + final String selected; + final Function(String) onItemSelected; + + @override + Widget build(BuildContext context) { + final availableSeedLanguages = seedLanguages + .where((SeedLanguagePickerOption e) => e.supportedSeedTypes.contains(seedType)); + + return Picker( + selectedAtIndex: availableSeedLanguages.map((e) => e.name).toList().indexOf(selected), + items: availableSeedLanguages.map((e) => e.name).toList(), + images: availableSeedLanguages.map((e) => e.image).toList(), + isGridView: true, + title: S.of(context).seed_choose, + hintText: S.of(context).seed_choose, + matchingCriteria: (String language, String searchText) { + return language.toLowerCase().contains(searchText); + }, + onItemSelected: onItemSelected, + ); + } +} diff --git a/lib/src/widgets/seed_language_selector.dart b/lib/src/widgets/seed_language_selector.dart index 4db3684a8..dde78c58c 100644 --- a/lib/src/widgets/seed_language_selector.dart +++ b/lib/src/widgets/seed_language_selector.dart @@ -1,49 +1,42 @@ -import 'package:cake_wallet/utils/show_pop_up.dart'; -import 'package:flutter/material.dart'; +import 'package:cake_wallet/entities/seed_type.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart'; -import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart'; +import 'package:cake_wallet/src/widgets/seed_language_picker.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:flutter/material.dart'; class SeedLanguageSelector extends StatefulWidget { - SeedLanguageSelector({Key? key, required this.initialSelected}) + SeedLanguageSelector( + {Key? key, required this.initialSelected, this.seedType = SeedType.defaultSeedType}) : super(key: key); final String initialSelected; + final SeedType seedType; @override - SeedLanguageSelectorState createState() => - SeedLanguageSelectorState(selected: initialSelected); + SeedLanguageSelectorState createState() => SeedLanguageSelectorState(selected: initialSelected); } class SeedLanguageSelectorState extends State { SeedLanguageSelectorState({required this.selected}); - final seedLocales = [ - S.current.seed_language_english, - S.current.seed_language_chinese, - S.current.seed_language_dutch, - S.current.seed_language_german, - S.current.seed_language_japanese, - S.current.seed_language_portuguese, - S.current.seed_language_russian, - S.current.seed_language_spanish, - S.current.seed_language_french, - S.current.seed_language_italian, - ]; String selected; @override Widget build(BuildContext context) { return SelectButton( image: null, - text: seedLocales[seedLanguages.indexOf(selected)], + text: + "${seedLanguages.firstWhere((e) => e.name == selected).nameLocalized} (${S.of(context).seed_language})", onTap: () async { await showPopUp( - context: context, - builder: (_) => SeedLanguagePicker( - selected: this.selected, - onItemSelected: (String selected) => - setState(() => this.selected = selected))); + context: context, + builder: (_) => SeedLanguagePicker( + selected: this.selected, + seedType: widget.seedType, + onItemSelected: (String selected) => setState(() => this.selected = selected), + ), + ); }, ); } diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 1522ea354..4aaa44e13 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -10,6 +10,7 @@ import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/pin_code_required_duration.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/seed_phrase_length.dart'; +import 'package:cake_wallet/entities/seed_type.dart'; import 'package:cake_wallet/entities/sort_balance_types.dart'; import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart'; import 'package:cake_wallet/view_model/settings/sync_mode.dart'; @@ -48,6 +49,7 @@ abstract class SettingsStoreBase with Store { required BalanceDisplayMode initialBalanceDisplayMode, required bool initialSaveRecipientAddress, required AutoGenerateSubaddressStatus initialAutoGenerateSubaddressStatus, + required SeedType initialMoneroSeedType, required bool initialAppSecure, required bool initialDisableBuy, required bool initialDisableSell, @@ -108,6 +110,7 @@ abstract class SettingsStoreBase with Store { balanceDisplayMode = initialBalanceDisplayMode, shouldSaveRecipientAddress = initialSaveRecipientAddress, autoGenerateSubaddressStatus = initialAutoGenerateSubaddressStatus, + moneroSeedType = initialMoneroSeedType, fiatApiMode = initialFiatMode, allowBiometricalAuthentication = initialAllowBiometricalAuthentication, selectedCake2FAPreset = initialCake2FAPresetOptions, @@ -240,6 +243,11 @@ abstract class SettingsStoreBase with Store { (AutoGenerateSubaddressStatus autoGenerateSubaddressStatus) => sharedPreferences.setInt( PreferencesKey.autoGenerateSubaddressStatusKey, autoGenerateSubaddressStatus.value)); + reaction( + (_) => moneroSeedType, + (SeedType moneroSeedType) => sharedPreferences.setInt( + PreferencesKey.moneroSeedType, moneroSeedType.raw)); + reaction( (_) => fiatApiMode, (FiatApiMode mode) => @@ -435,6 +443,7 @@ abstract class SettingsStoreBase with Store { static const defaultPinCodeTimeOutDuration = PinCodeRequiredDuration.tenminutes; static const defaultAutoGenerateSubaddressStatus = AutoGenerateSubaddressStatus.initialized; static const defaultSeedPhraseLength = SeedPhraseLength.twelveWords; + static const defaultMoneroSeedType = SeedType.defaultSeedType; @observable FiatCurrency fiatCurrency; @@ -460,6 +469,9 @@ abstract class SettingsStoreBase with Store { @observable AutoGenerateSubaddressStatus autoGenerateSubaddressStatus; + @observable + SeedType moneroSeedType; + @observable bool isAppSecure; @@ -775,12 +787,20 @@ abstract class SettingsStoreBase with Store { final packageInfo = await PackageInfo.fromPlatform(); final deviceName = await _getDeviceName() ?? ''; final shouldShowYatPopup = sharedPreferences.getBool(PreferencesKey.shouldShowYatPopup) ?? true; + final generateSubaddresses = sharedPreferences.getInt(PreferencesKey.autoGenerateSubaddressStatusKey); final autoGenerateSubaddressStatus = generateSubaddresses != null ? AutoGenerateSubaddressStatus.deserialize(raw: generateSubaddresses) : defaultAutoGenerateSubaddressStatus; + + final _moneroSeedType = sharedPreferences.getInt(PreferencesKey.moneroSeedType); + + final moneroSeedType = _moneroSeedType != null + ? SeedType.deserialize(raw: _moneroSeedType) + : defaultMoneroSeedType; + final nodes = {}; final powNodes = {}; @@ -833,27 +853,27 @@ abstract class SettingsStoreBase with Store { initialBalanceDisplayMode: currentBalanceDisplayMode, initialSaveRecipientAddress: shouldSaveRecipientAddress, initialAutoGenerateSubaddressStatus: autoGenerateSubaddressStatus, - initialAppSecure: isAppSecure, - initialDisableBuy: disableBuy, - initialDisableSell: disableSell, - initialDefaultBuyProvider: defaultBuyProvider, - initialFiatMode: currentFiatApiMode, - initialAllowBiometricalAuthentication: allowBiometricalAuthentication, - initialCake2FAPresetOptions: selectedCake2FAPreset, - initialUseTOTP2FA: useTOTP2FA, - initialTotpSecretKey: totpSecretKey, - initialFailedTokenTrial: tokenTrialNumber, - initialExchangeStatus: exchangeStatus, - initialTheme: savedTheme, - actionlistDisplayMode: actionListDisplayMode, - initialPinLength: pinLength, - pinTimeOutDuration: pinCodeTimeOutDuration, - seedPhraseLength: seedPhraseWordCount, - initialLanguageCode: savedLanguageCode, - sortBalanceBy: sortBalanceBy, - pinNativeTokenAtTop: pinNativeTokenAtTop, - useEtherscan: useEtherscan, - defaultNanoRep: defaultNanoRep, + initialMoneroSeedType: moneroSeedType, + initialAppSecure: isAppSecure, + initialDisableBuy: disableBuy, + initialDisableSell: disableSell, + initialDefaultBuyProvider: defaultBuyProvider, + initialFiatMode: currentFiatApiMode, + initialAllowBiometricalAuthentication: allowBiometricalAuthentication, + initialCake2FAPresetOptions: selectedCake2FAPreset, + initialUseTOTP2FA: useTOTP2FA, + initialTotpSecretKey: totpSecretKey, + initialFailedTokenTrial: tokenTrialNumber, + initialExchangeStatus: exchangeStatus, + initialTheme: savedTheme, + actionlistDisplayMode: actionListDisplayMode, + initialPinLength: pinLength, + pinTimeOutDuration: pinCodeTimeOutDuration, + seedPhraseLength: seedPhraseWordCount,initialLanguageCode: savedLanguageCode, + sortBalanceBy: sortBalanceBy, + pinNativeTokenAtTop: pinNativeTokenAtTop, + useEtherscan: useEtherscan, + defaultNanoRep: defaultNanoRep, defaultBananoRep: defaultBananoRep, lookupsTwitter: lookupsTwitter, lookupsMastodon: lookupsMastodon, @@ -862,10 +882,10 @@ abstract class SettingsStoreBase with Store { lookupsOpenAlias: lookupsOpenAlias, lookupsENS: lookupsENS, initialMoneroTransactionPriority: moneroTransactionPriority, - initialBitcoinTransactionPriority: bitcoinTransactionPriority, - initialHavenTransactionPriority: havenTransactionPriority, - initialLitecoinTransactionPriority: litecoinTransactionPriority, - initialBitcoinCashTransactionPriority: bitcoinCashTransactionPriority, + initialBitcoinTransactionPriority: bitcoinTransactionPriority, + initialHavenTransactionPriority: havenTransactionPriority, + initialLitecoinTransactionPriority: litecoinTransactionPriority, + initialBitcoinCashTransactionPriority: bitcoinCashTransactionPriority, initialShouldRequireTOTP2FAForAccessingWallet: shouldRequireTOTP2FAForAccessingWallet, initialShouldRequireTOTP2FAForSendsToContact: shouldRequireTOTP2FAForSendsToContact, initialShouldRequireTOTP2FAForSendsToNonContact: shouldRequireTOTP2FAForSendsToNonContact, @@ -927,6 +947,12 @@ abstract class SettingsStoreBase with Store { ? AutoGenerateSubaddressStatus.deserialize(raw: generateSubaddresses) : defaultAutoGenerateSubaddressStatus; + final _moneroSeedType = sharedPreferences.getInt(PreferencesKey.moneroSeedType); + + moneroSeedType = _moneroSeedType != null + ? SeedType.deserialize(raw: _moneroSeedType) + : defaultMoneroSeedType; + balanceDisplayMode = BalanceDisplayMode.deserialize( raw: sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey)!); shouldSaveRecipientAddress = diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index bea43a6c6..36eac4941 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -162,6 +162,7 @@ class ExceptionHandler { "Error while launching http", "OS Error: Network is unreachable", "ClientException: Write failed, uri=http", + "Connection terminated during handshake", ]; static Future _addDeviceInfo(File file) async { diff --git a/lib/utils/feature_flag.dart b/lib/utils/feature_flag.dart index 628023f85..91a4c67ca 100644 --- a/lib/utils/feature_flag.dart +++ b/lib/utils/feature_flag.dart @@ -1,4 +1,5 @@ class FeatureFlag { static const bool isCakePayEnabled = false; static const bool isExolixEnabled = false; + static const bool isInAppTorEnabled = false; } \ No newline at end of file diff --git a/lib/view_model/advanced_privacy_settings_view_model.dart b/lib/view_model/advanced_privacy_settings_view_model.dart index b67a4378a..f62815346 100644 --- a/lib/view_model/advanced_privacy_settings_view_model.dart +++ b/lib/view_model/advanced_privacy_settings_view_model.dart @@ -1,6 +1,7 @@ import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/seed_phrase_length.dart'; +import 'package:cake_wallet/entities/seed_type.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:mobx/mobx.dart'; @@ -19,6 +20,9 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { @computed FiatApiMode get fiatApiMode => _settingsStore.fiatApiMode; + @computed + SeedType get seedType => _settingsStore.moneroSeedType; + @observable bool _addCustomNode = false; @@ -29,6 +33,8 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { bool get hasSeedPhraseLengthOption => type == WalletType.bitcoinCash || type == WalletType.ethereum; + bool get hasSeedTypeOption => type == WalletType.monero; + @computed bool get addCustomNode => _addCustomNode; @@ -38,6 +44,9 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { @action void setFiatApiMode(FiatApiMode fiatApiMode) => _settingsStore.fiatApiMode = fiatApiMode; + @action + void setSeedType(SeedType seedType) => _settingsStore.moneroSeedType = seedType; + @action void setExchangeApiMode(ExchangeApiMode value) => _settingsStore.exchangeStatus = value; diff --git a/lib/view_model/dashboard/nft_view_model.dart b/lib/view_model/dashboard/nft_view_model.dart new file mode 100644 index 000000000..c5acf5523 --- /dev/null +++ b/lib/view_model/dashboard/nft_view_model.dart @@ -0,0 +1,142 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; +import 'dart:developer'; + +import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart'; +import 'package:cake_wallet/src/screens/wallet_connect/widgets/message_display_widget.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'package:http/http.dart' as http; +import 'package:mobx/mobx.dart'; +import 'package:cake_wallet/.secrets.g.dart' as secrets; + +import 'package:cake_wallet/entities/wallet_nft_response.dart'; +import 'package:cake_wallet/store/app_store.dart'; + +part 'nft_view_model.g.dart'; + +class NFTViewModel = NFTViewModelBase with _$NFTViewModel; + +abstract class NFTViewModelBase with Store { + NFTViewModelBase(this.appStore, this.bottomSheetService) + : isLoading = false, + isImportNFTLoading = false, + nftAssetByWalletModels = ObservableList() { + getNFTAssetByWallet(); + + reaction((_) => appStore.wallet, (_) => getNFTAssetByWallet()); + } + + final AppStore appStore; + final BottomSheetService bottomSheetService; + + @observable + bool isLoading; + + @observable + bool isImportNFTLoading; + + ObservableList nftAssetByWalletModels; + + @action + Future getNFTAssetByWallet() async { + if (appStore.wallet!.type != WalletType.ethereum) return; + + final walletAddress = appStore.wallet!.walletInfo.address; + log('Fetching wallet NFTs for $walletAddress'); + + // the [chain] refers to the chain network that the nft is on + // the [format] refers to the number format type of the responses + // the [normalizedMetadata] field is a boolean that determines if + // the response would include a json string of the NFT Metadata that can be decoded + // and used within the wallet + final uri = Uri.https( + 'deep-index.moralis.io', + '/api/v2.2/$walletAddress/nft', + { + "chain": "eth", + "format": "decimal", + "media_items": "false", + "normalizeMetadata": "true", + }, + ); + + try { + isLoading = true; + + final response = await http.get( + uri, + headers: { + "Accept": "application/json", + "X-API-Key": secrets.moralisApiKey, + }, + ); + + final decodedResponse = jsonDecode(response.body) as Map; + + final result = WalletNFTsResponseModel.fromJson(decodedResponse).result ?? []; + + nftAssetByWalletModels.clear(); + + nftAssetByWalletModels.addAll(result); + + isLoading = false; + } catch (e) { + isLoading = false; + log(e.toString()); + bottomSheetService.queueBottomSheet( + isModalDismissible: true, + widget: BottomSheetMessageDisplayWidget( + message: e.toString(), + ), + ); + } + } + + @action + Future importNFT(String tokenAddress, String tokenId) async { + + // the [chain] refers to the chain network that the nft is on + // the [format] refers to the number format type of the responses + // the [normalizedMetadata] field is a boolean that determines if + // the response would include a json string of the NFT Metadata that can be decoded + // and used within the wallet + final uri = Uri.https( + 'deep-index.moralis.io', + '/api/v2.2/nft/$tokenAddress/$tokenId', + { + "chain": "eth", + "format": "decimal", + "media_items": "false", + "normalizeMetadata": "true", + }, + ); + + try { + isImportNFTLoading = true; + + final response = await http.get( + uri, + headers: { + "Accept": "application/json", + "X-API-Key": secrets.moralisApiKey, + }, + ); + + final decodedResponse = jsonDecode(response.body) as Map; + + final nftAsset = NFTAssetModel.fromJson(decodedResponse); + + nftAssetByWalletModels.add(nftAsset); + + isImportNFTLoading = false; + } catch (e) { + isImportNFTLoading = false; + bottomSheetService.queueBottomSheet( + isModalDismissible: true, + widget: BottomSheetMessageDisplayWidget( + message: e.toString(), + ), + ); + } + } +} diff --git a/lib/view_model/edit_backup_password_view_model.dart b/lib/view_model/edit_backup_password_view_model.dart index e7b8ee017..aca76502a 100644 --- a/lib/view_model/edit_backup_password_view_model.dart +++ b/lib/view_model/edit_backup_password_view_model.dart @@ -37,6 +37,7 @@ abstract class EditBackupPasswordViewModelBase with Store { @action Future save() async { final key = generateStoreKeyFor(key: SecretStoreKey.backupPassword); + await secureStorage.delete(key: key); await secureStorage.write(key: key, value: backupPassword); secretStore.write(key: key, value: backupPassword); } diff --git a/lib/view_model/restore/restore_wallet.dart b/lib/view_model/restore/restore_wallet.dart index 264b3d421..d46c48092 100644 --- a/lib/view_model/restore/restore_wallet.dart +++ b/lib/view_model/restore/restore_wallet.dart @@ -45,7 +45,7 @@ class RestoredWallet { factory RestoredWallet.fromSeed(Map json) { final height = json['height'] as String?; final mnemonic_seed = json['mnemonic_seed'] as String?; - final seed = json['seed'] as String?; + final seed = json['seed'] as String? ?? json['hexSeed'] as String?; return RestoredWallet( restoreMode: json['mode'] as WalletRestoreMode, type: json['type'] as WalletType, diff --git a/lib/view_model/restore/wallet_restore_from_qr_code.dart b/lib/view_model/restore/wallet_restore_from_qr_code.dart index 077675d1f..6085f4354 100644 --- a/lib/view_model/restore/wallet_restore_from_qr_code.dart +++ b/lib/view_model/restore/wallet_restore_from_qr_code.dart @@ -10,6 +10,7 @@ import 'package:cw_core/wallet_type.dart'; import 'package:flutter/cupertino.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:collection/collection.dart'; +import 'package:polyseed/polyseed.dart'; class WalletRestoreFromQRCode { WalletRestoreFromQRCode(); @@ -51,7 +52,7 @@ class WalletRestoreFromQRCode { RegExp _getPattern(int wordCount) => RegExp(r'(?<=\W|^)((?:\w+\s+){' + (wordCount - 1).toString() + r'}\w+)(?=\W|$)'); - List patternCounts = walletType == WalletType.monero ? [25, 14, 13] : [24, 18, 12]; + List patternCounts = walletType == WalletType.monero ? [25, 16, 14, 13] : [24, 18, 12]; for (final count in patternCounts) { final pattern = _getPattern(count); @@ -123,12 +124,17 @@ class WalletRestoreFromQRCode { } if (credentials['seed'] != null) { - final seedValue = credentials['seed']; + final seedValue = credentials['seed'] as String; final words = SeedValidator.getWordList(type: type, language: 'english'); + + if (type == WalletType.monero && Polyseed.isValidSeed(seedValue)) { + return WalletRestoreMode.seed; + } + seedValue.split(' ').forEach((element) { if (!words.contains(element)) { throw Exception( - 'Unexpected restore mode: mnemonic_seed is invalid or does\'t match wallet type'); + "Unexpected restore mode: mnemonic_seed is invalid or doesn't match wallet type"); } }); return WalletRestoreMode.seed; @@ -151,6 +157,14 @@ class WalletRestoreFromQRCode { return WalletRestoreMode.keys; } + if ((type == WalletType.nano || type == WalletType.banano) && credentials.containsKey('hexSeed')) { + final hexSeed = credentials['hexSeed'] as String; + if (hexSeed.isEmpty) { + throw Exception('Unexpected restore mode: hexSeed'); + } + return WalletRestoreMode.seed; + } + throw Exception('Unexpected restore mode: restore params are invalid'); } } diff --git a/lib/view_model/wallet_keys_view_model.dart b/lib/view_model/wallet_keys_view_model.dart index 257bae1fd..d9de9473d 100644 --- a/lib/view_model/wallet_keys_view_model.dart +++ b/lib/view_model/wallet_keys_view_model.dart @@ -107,19 +107,18 @@ abstract class WalletKeysViewModelBase with Store { ]); } - if (_appStore.wallet!.type == WalletType.nano || _appStore.wallet!.type == WalletType.banano) { + bool nanoBased = + _appStore.wallet!.type == WalletType.nano || _appStore.wallet!.type == WalletType.banano; - // we don't necessarily have the seed phrase for nano / banano: - if (_appStore.wallet!.seed != null) { - items.addAll([ - StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!), - ]); - } - - // we always have the hex version of the seed: + if (nanoBased) { + // we always have the hex version of the seed and private key: items.addAll([ + if (_appStore.wallet!.seed != null) + StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!), + if (_appStore.wallet!.hexSeed != null) + StandartListItem(title: S.current.seed_hex_form, value: _appStore.wallet!.hexSeed!), if (_appStore.wallet!.privateKey != null) - StandartListItem(title: S.current.spend_key_private, value: _appStore.wallet!.privateKey!), + StandartListItem(title: S.current.private_key, value: _appStore.wallet!.privateKey!), ]); } } @@ -172,7 +171,10 @@ abstract class WalletKeysViewModelBase with Store { final restoreHeightResult = await restoreHeight; return { if (_appStore.wallet!.seed != null) 'seed': _appStore.wallet!.seed!, - if (_appStore.wallet!.privateKey != null) 'private_key': _appStore.wallet!.privateKey!, + if (_appStore.wallet!.seed == null && _appStore.wallet!.hexSeed != null) + 'hexSeed': _appStore.wallet!.hexSeed!, + if (_appStore.wallet!.seed == null && _appStore.wallet!.privateKey != null) + 'private_key': _appStore.wallet!.privateKey!, if (restoreHeightResult != null) ...{'height': restoreHeightResult} }; } diff --git a/lib/view_model/wallet_new_vm.dart b/lib/view_model/wallet_new_vm.dart index c2f557a67..7e163c0b7 100644 --- a/lib/view_model/wallet_new_vm.dart +++ b/lib/view_model/wallet_new_vm.dart @@ -1,6 +1,4 @@ -import 'package:cake_wallet/view_model/restore/restore_wallet.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; -import 'package:flutter/foundation.dart'; import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; @@ -32,17 +30,22 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store { bool get hasLanguageSelector => type == WalletType.monero || type == WalletType.haven; + bool get hasSeedType => type == WalletType.monero; + @override - WalletCredentials getCredentials(dynamic options) { + WalletCredentials getCredentials(dynamic _options) { + final options = _options as List?; switch (type) { case WalletType.monero: - return monero!.createMoneroNewWalletCredentials(name: name, language: options as String); + return monero!.createMoneroNewWalletCredentials( + name: name, language: options!.first as String, isPolyseed: options.last as bool); case WalletType.bitcoin: return bitcoin!.createBitcoinNewWalletCredentials(name: name); case WalletType.litecoin: return bitcoin!.createBitcoinNewWalletCredentials(name: name); case WalletType.haven: - return haven!.createHavenNewWalletCredentials(name: name, language: options as String); + return haven! + .createHavenNewWalletCredentials(name: name, language: options!.first as String); case WalletType.ethereum: return ethereum!.createEthereumNewWalletCredentials(name: name); case WalletType.bitcoinCash: diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 664a5231b..329732075 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -30,13 +30,11 @@ PODS: - FlutterMacOS - package_info (0.0.1): - FlutterMacOS + - package_info_plus (0.0.1): + - FlutterMacOS - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - - platform_device_id (0.0.1): - - FlutterMacOS - - platform_device_id_macos (0.0.1): - - FlutterMacOS - ReachabilitySwift (5.0.0) - share_plus_macos (0.0.1): - FlutterMacOS @@ -45,7 +43,7 @@ PODS: - FlutterMacOS - url_launcher_macos (0.0.1): - FlutterMacOS - - wakelock_macos (0.0.1): + - wakelock_plus (0.0.1): - FlutterMacOS DEPENDENCIES: @@ -57,13 +55,12 @@ DEPENDENCIES: - FlutterMacOS (from `Flutter/ephemeral`) - in_app_review (from `Flutter/ephemeral/.symlinks/plugins/in_app_review/macos`) - package_info (from `Flutter/ephemeral/.symlinks/plugins/package_info/macos`) + - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - - platform_device_id (from `Flutter/ephemeral/.symlinks/plugins/platform_device_id/macos`) - - platform_device_id_macos (from `Flutter/ephemeral/.symlinks/plugins/platform_device_id_macos/macos`) - share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - - wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`) + - wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`) SPEC REPOS: trunk: @@ -86,20 +83,18 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/in_app_review/macos package_info: :path: Flutter/ephemeral/.symlinks/plugins/package_info/macos + package_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos path_provider_foundation: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin - platform_device_id: - :path: Flutter/ephemeral/.symlinks/plugins/platform_device_id/macos - platform_device_id_macos: - :path: Flutter/ephemeral/.symlinks/plugins/platform_device_id_macos/macos share_plus_macos: :path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos shared_preferences_foundation: :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin url_launcher_macos: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos - wakelock_macos: - :path: Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos + wakelock_plus: + :path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos SPEC CHECKSUMS: connectivity_plus_macos: f6e86fd000e971d361e54b5afcadc8c8fa773308 @@ -110,14 +105,13 @@ SPEC CHECKSUMS: FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 in_app_review: a850789fad746e89bce03d4aeee8078b45a53fd0 package_info: 6eba2fd8d3371dda2d85c8db6fe97488f24b74b2 + package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 - platform_device_id: 3e414428f45df149bbbfb623e2c0ca27c545b763 - platform_device_id_macos: f763bb55f088be804d61b96eb4710b8ab6598e94 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4 shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95 - wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9 + wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269 PODFILE CHECKSUM: 5107934592df7813b33d744aebc8ddc6b5a5445f diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 648cee7db..b2bc3c904 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -83,7 +83,7 @@ dependencies: ref: main version: 1.0.0 flutter_plugin_android_lifecycle: 2.0.9 - path_provider_android: 2.0.24 + path_provider_android: ^2.2.1 shared_preferences_android: 2.0.17 url_launcher_android: 6.0.24 sensitive_clipboard: ^1.0.0 @@ -98,6 +98,15 @@ dependencies: url: https://github.com/cake-tech/bitcoin_flutter.git ref: silent-payments fluttertoast: 8.1.4 + tor: + git: + url: https://github.com/cake-tech/tor.git + ref: main + socks5_proxy: ^1.0.4 + flutter_svg: ^2.0.9 + polyseed: + git: + url: https://github.com/cake-tech/polyseed_dart.git dev_dependencies: flutter_test: diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 01e7d2c5b..21e572234 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -160,7 +160,7 @@ "seed_title": "سييد", "seed_share": "شارك السييد", "copy": "نسخ", - "seed_language_choose": "الرجاء اختيار لغة السييد:", + "seed_language": "لغة البذور", "seed_choose": "اختر لغة السييد", "seed_language_next": "التالي", "seed_language_english": "إنجليزي", @@ -383,7 +383,7 @@ "change_backup_password_alert": "لن تكون ملفات النسخ الاحتياطي السابقة متاحة للاستيراد بكلمة مرور نسخ احتياطي جديدة. سيتم استخدام كلمة مرور النسخ الاحتياطي الجديدة لملفات النسخ الاحتياطي الجديدة فقط. هل أنت متأكد أنك تريد تغيير كلمة المرور الاحتياطية؟", "enter_backup_password": "أدخل كلمة المرور الاحتياطية هنا", "select_backup_file": "حدد ملف النسخ الاحتياطي", - "import": "اختيار ملف", + "import": "ﺩﺭﻮﺘﺴﻳ", "please_select_backup_file": "الرجاء تحديد ملف النسخ الاحتياطي وإدخال كلمة مرور النسخ الاحتياطي.", "fixed_rate": "السعر الثابت", "fixed_rate_alert": "ستتمكن من إدخال مبلغ الاستلام عند تشغيل وضع السعر الثابت. هل تريد التبديل إلى وضع السعر الثابت؟", @@ -569,7 +569,7 @@ "always": "دائماً", "minutes_to_pin_code": "${minutes} دقيقة", "disable_exchange": "تعطيل التبادل", - "advanced_privacy_settings": "إعدادات الخصوصية المتقدمة", + "advanced_settings": "إعدادات متقدمة", "settings_can_be_changed_later": "يمكن تغيير هذه الإعدادات لاحقًا في إعدادات التطبيق", "add_custom_node": "إضافة عقدة مخصصة جديدة", "disable_fiat": "تعطيل fiat", @@ -727,11 +727,26 @@ "require_for_exchanges_to_external_wallets": "ﺔﻴﺟﺭﺎﺧ ﻆﻓﺎﺤﻣ ﻰﻟﺇ ﺕﻻﺩﺎﺒﺘﻟﺍ ﺐﻠﻄﺘﺗ", "camera_permission_is_required": ".ﺍﺮﻴﻣﺎﻜﻟﺍ ﻥﺫﺇ ﺏﻮﻠﻄﻣ", "switchToETHWallet": "ﻯﺮﺧﺃ ﺓﺮﻣ ﺔﻟﻭﺎﺤﻤﻟﺍﻭ Ethereum ﺔﻈﻔﺤﻣ ﻰﻟﺇ ﻞﻳﺪﺒﺘﻟﺍ ﻰﺟﺮﻳ", - "seed_phrase_length": " ﺭﻭﺬﺒﻟﺍ ﺓﺭﺎﺒﻌﻟﺍ ﻝﻮﻃ", - "unavailable_balance": " ﺮﻓﻮﺘﻣ ﺮﻴﻏ ﺪﻴﺻﺭ", + "importNFTs": "NFTs ﺩﺍﺮﻴﺘﺳﺍ", + "noNFTYet": "ﻥﻵﺍ ﻰﺘﺣ NFTs ﺪﺟﻮﻳ ﻻ", + "address": " ﻥﺍﻮﻨﻋ", + "enterTokenID": "ﺰﻴﻤﻤﻟﺍ ﺰﻣﺮﻟﺍ ﻑﺮﻌﻣ ﻞﺧﺩﺃ", + "tokenID": "ﻒﻳﺮﻌﺗ ﺔﻗﺎﻄﺑ", + "name": "ﻢﺳﺍ", + "symbol": "ﺰﻣﺭ", + "seed_phrase_length": "ﺭﻭﺬﺒﻟﺍ ﺓﺭﺎﺒﻌﻟﺍ ﻝﻮﻃ", + "unavailable_balance": "ﺮﻓﻮﺘﻣ ﺮﻴﻏ ﺪﻴﺻﺭ", "unavailable_balance_description": ".ﺎﻫﺪﻴﻤﺠﺗ ءﺎﻐﻟﺇ ﺭﺮﻘﺗ ﻰﺘﺣ ﺕﻼﻣﺎﻌﻤﻠﻟ ﻝﻮﺻﻮﻠﻟ ﺔﻠﺑﺎﻗ ﺮﻴﻏ ﺓﺪﻤﺠﻤﻟﺍ ﺓﺪﺻﺭﻷﺍ ﻞﻈﺗ ﺎﻤﻨﻴﺑ ،ﺎﻬﺑ ﺔﺻﺎﺨﻟﺍ ﺕﻼﻣﺎﻌﻤﻟﺍ ﻝﺎﻤﺘﻛﺍ ﺩﺮﺠﻤﺑ ﺔﺣﺎﺘﻣ ﺔﻠﻔﻘﻤﻟﺍ ﺓﺪﺻﺭﻷﺍ ﺢﺒﺼﺘﺳ .ﻚﺑ ﺔﺻﺎﺨﻟﺍ ﺕﻼﻤﻌﻟﺍ ﻲﻓ ﻢﻜﺤﺘﻟﺍ ﺕﺍﺩﺍﺪﻋﺇ ﻲﻓ ﻂﺸﻧ ﻞﻜﺸﺑ ﺎﻫﺪﻴﻤﺠﺘﺑ ﺖﻤﻗ", "unspent_change": "يتغير", "Block_remaining": "${status} كتلة متبقية", "labeled_silent_addresses": "العناوين الصامتة المسمى", - "use_testnet": "استخدم testnet" -} \ No newline at end of file + "use_testnet": "استخدم testnet", + "seed_hex_form": "بذور المحفظة (شكل عرافة)", + "tor_connection": "ﺭﻮﺗ ﻝﺎﺼﺗﺍ", + "seedtype": "البذور", + "seedtype_legacy": "إرث (25 كلمة)", + "seedtype_polyseed": "بوليسيد (16 كلمة)", + "seed_language_czech": "التشيكية", + "seed_language_korean": "الكورية", + "seed_language_chinese_traditional": "تقاليد صينية)" +} diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 26a70bbbe..6dde8d9b0 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -160,7 +160,7 @@ "seed_title": "Seed", "seed_share": "Споделяне на seed", "copy": "Копиране", - "seed_language_choose": "Моля, изберете език на seed-а:", + "seed_language": "Език на семената", "seed_choose": "Изберете език на seed-а", "seed_language_next": "Следващ", "seed_language_english": "Английски", @@ -569,7 +569,7 @@ "always": "Винаги", "minutes_to_pin_code": "${minute} минути", "disable_exchange": "Деактивиране на борса", - "advanced_privacy_settings": "Допълнителни настройки за поверителност", + "advanced_settings": "Разширени настройки", "settings_can_be_changed_later": "Тези настройки могат да бъдат променени по-късно от приложението", "add_custom_node": "Добавяне на нов персонализиран Node", "disable_fiat": "Деактивиране на fiat", @@ -723,11 +723,26 @@ "require_for_exchanges_to_external_wallets": "Изискване за обмен към външни портфейли", "camera_permission_is_required": "Изисква се разрешение за камерата.\nМоля, активирайте го от настройките на приложението.", "switchToETHWallet": "Моля, преминете към портфейл Ethereum и опитайте отново", + "importNFTs": "Импортирайте NFT", + "noNFTYet": "Все още няма NFT", + "address": "Адрес", + "enterTokenID": "Въведете идентификатора на токена", + "tokenID": "документ за самоличност", + "name": "Име", + "symbol": "Символ", "seed_phrase_length": "Дължина на началната фраза", "unavailable_balance": "Неналично салдо", "unavailable_balance_description": "Неналично салдо: Тази обща сума включва средства, които са заключени в чакащи транзакции и тези, които сте замразили активно в настройките за контрол на монетите. Заключените баланси ще станат достъпни, след като съответните им транзакции бъдат завършени, докато замразените баланси остават недостъпни за транзакции, докато не решите да ги размразите.", "unspent_change": "Промяна", "Block_remaining": "${status} останал блок", "labeled_silent_addresses": "Етикетирани безшумни адреси", - "use_testnet": "Използвайте TestNet" -} \ No newline at end of file + "use_testnet": "Използвайте TestNet", + "tor_connection": "Tor връзка", + "seed_hex_form": "Семена от портфейл (шестнадесетична форма)", + "seedtype": "Семенна тип", + "seedtype_legacy": "Наследство (25 думи)", + "seedtype_polyseed": "Поли семе (16 думи)", + "seed_language_czech": "Чех", + "seed_language_korean": "Корейски", + "seed_language_chinese_traditional": "Традиционен китайски)" +} diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 5a907bccd..a7e70c699 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -160,7 +160,7 @@ "seed_title": "Seed", "seed_share": "Sdílet seed", "copy": "Kopírovat", - "seed_language_choose": "Prosím zvolte si jazyk seedu:", + "seed_language": "Jazyk semen", "seed_choose": "Zvolte si jazyk seedu", "seed_language_next": "Další", "seed_language_english": "Angličtina", @@ -383,7 +383,7 @@ "change_backup_password_alert": "Vaše předchozí soubory se zálohami nebude možné naimportovat s novým heslem. Nové heslo bude použito pouze pro nové zálohy. Opravdu chcete změnit heslo pro zálohy?", "enter_backup_password": "Zde zadejte své heslo pro zálohy", "select_backup_file": "Vybrat soubor se zálohou", - "import": "Importovat", + "import": "Import", "please_select_backup_file": "Prosím vyberte soubor se zálohou a zadejte heslo pro zálohy.", "fixed_rate": "Pevný kurz", "fixed_rate_alert": "Když je zvolený pevný kurz, můžete zadat konkrétní částku, kterou chcete dostat. Chcete se přepnout do režimu s pevným kurzem?", @@ -569,7 +569,7 @@ "always": "Vždy", "minutes_to_pin_code": "${minute} minutách", "disable_exchange": "Zakázat směnárny", - "advanced_privacy_settings": "Pokročilá nastavení soukromí", + "advanced_settings": "Pokročilé nastavení", "settings_can_be_changed_later": "Tato nastavení mohou být změněna později v nastavení v této aplikaci", "add_custom_node": "Přidat vlastní uzel", "disable_fiat": "Zakázat fiat", @@ -723,11 +723,26 @@ "require_for_exchanges_to_external_wallets": "Vyžadovat pro výměny do externích peněženek", "camera_permission_is_required": "Vyžaduje se povolení fotoaparátu.\nPovolte jej v nastavení aplikace.", "switchToETHWallet": "Přejděte na peněženku Ethereum a zkuste to znovu", + "importNFTs": "Importujte NFT", + "noNFTYet": "Zatím žádné NFT", + "address": "Adresa", + "enterTokenID": "Zadejte ID tokenu", + "tokenID": "ID", + "name": "název", + "symbol": "Symbol", "seed_phrase_length": "Délka fráze semene", "unavailable_balance": "Nedostupný zůstatek", "unavailable_balance_description": "Nedostupný zůstatek: Tento součet zahrnuje prostředky, které jsou uzamčeny v nevyřízených transakcích a ty, které jste aktivně zmrazili v nastavení kontroly mincí. Uzamčené zůstatky budou k dispozici po dokončení příslušných transakcí, zatímco zmrazené zůstatky zůstanou pro transakce nepřístupné, dokud se nerozhodnete je uvolnit.", "unspent_change": "Změna", "Block_remaining": "${status} Blok zbývající", "labeled_silent_addresses": "Označené tiché adresy", - "use_testnet": "Použijte testNet" -} \ No newline at end of file + "use_testnet": "Použijte testNet", + "tor_connection": "Připojení Tor", + "seed_hex_form": "Semeno peněženky (hex formulář)", + "seedtype": "SeedType", + "seedtype_legacy": "Legacy (25 slov)", + "seedtype_polyseed": "Polyseed (16 slov)", + "seed_language_czech": "čeština", + "seed_language_korean": "korejština", + "seed_language_chinese_traditional": "Číňan (tradiční)" +} diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 4c9e2a960..466c1bb19 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -160,7 +160,7 @@ "seed_title": "Seed", "seed_share": "Seed teilen", "copy": "Kopieren", - "seed_language_choose": "Bitte wählen Sie die Sprache des Seeds:", + "seed_language": "Seed-Sprache", "seed_choose": "Seed-Sprache auswählen", "seed_language_next": "Weiter", "seed_language_english": "Englisch", @@ -571,7 +571,7 @@ "always": "immer", "minutes_to_pin_code": "${minute} Minuten", "disable_exchange": "Exchange deaktivieren", - "advanced_privacy_settings": "Erweiterte Datenschutzeinstellungen", + "advanced_settings": "Erweiterte Einstellungen", "settings_can_be_changed_later": "Diese Einstellungen können später in den App-Einstellungen geändert werden", "add_custom_node": "Neuen benutzerdefinierten Knoten hinzufügen", "disable_fiat": "Fiat deaktivieren", @@ -722,20 +722,35 @@ "awaitDAppProcessing": "Bitte warten Sie, bis die dApp die Verarbeitung abgeschlossen hat.", "copyWalletConnectLink": "Kopieren Sie den WalletConnect-Link von dApp und fügen Sie ihn hier ein", "enterWalletConnectURI": "Geben Sie den WalletConnect-URI ein", - "seed_key": "Samenschlüssel", - "enter_seed_phrase": "Geben Sie Ihre Samenphrase ein", - "change_rep_successful": "Erfolgreich veränderte Vertreter", + "seed_key": "Seed-Schlüssel", + "enter_seed_phrase": "Geben Sie Ihre Seed-Phrase ein", + "change_rep_successful": "Vertreter erfolgreich gerändert", "add_contact": "Kontakt hinzufügen", "exchange_provider_unsupported": "${providerName} wird nicht mehr unterstützt!", "domain_looks_up": "Domain-Suchen", "require_for_exchanges_to_external_wallets": "Erforderlich für den Umtausch in externe Wallets", "camera_permission_is_required": "Eine Kameraerlaubnis ist erforderlich.\nBitte aktivieren Sie es in den App-Einstellungen.", "switchToETHWallet": "Bitte wechseln Sie zu einem Ethereum-Wallet und versuchen Sie es erneut", + "importNFTs": "NFTs importieren", + "noNFTYet": "Noch keine NFTs", + "address": "Adresse", + "enterTokenID": "Geben Sie die Token-ID ein", + "tokenID": "AUSWEIS", + "name": "Name", + "symbol": "Symbol", "seed_phrase_length": "Länge der Seed-Phrase", "unavailable_balance": "Nicht verfügbares Guthaben", "unavailable_balance_description": "Nicht verfügbares Guthaben: Diese Summe umfasst Gelder, die in ausstehenden Transaktionen gesperrt sind, und solche, die Sie in Ihren Münzkontrolleinstellungen aktiv eingefroren haben. Gesperrte Guthaben werden verfügbar, sobald die entsprechenden Transaktionen abgeschlossen sind, während eingefrorene Guthaben für Transaktionen nicht zugänglich bleiben, bis Sie sich dazu entschließen, sie wieder freizugeben.", "unspent_change": "Wechselgeld", "Block_remaining": "${status} Block verbleibend", "labeled_silent_addresses": "Bezeichnete stille Adressen", - "use_testnet": "TESTNET verwenden" -} \ No newline at end of file + "use_testnet": "TESTNET verwenden", + "tor_connection": "Tor-Verbindung", + "seed_hex_form": "Brieftaschensamen (Sechskantform)", + "seedtype": "Seedtyp", + "seedtype_legacy": "Veraltet (25 Wörter)", + "seedtype_polyseed": "Polyseed (16 Wörter)", + "seed_language_czech": "Tschechisch", + "seed_language_korean": "Koreanisch", + "seed_language_chinese_traditional": "Chinesisch (Traditionell)" +} diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 68a5f219f..5c74fc5a3 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -160,7 +160,7 @@ "seed_title": "Seed", "seed_share": "Share seed", "copy": "Copy", - "seed_language_choose": "Please choose seed language:", + "seed_language": "Seed language", "seed_choose": "Choose seed language", "seed_language_next": "Next", "seed_language_english": "English", @@ -572,7 +572,7 @@ "always": "Always", "minutes_to_pin_code": "${minute} minutes", "disable_exchange": "Disable exchange", - "advanced_privacy_settings": "Advanced Privacy Settings", + "advanced_settings": "Advanced Settings", "settings_can_be_changed_later": "These settings can be changed later in the app settings", "add_custom_node": "Add New Custom Node", "disable_fiat": "Disable fiat", @@ -732,11 +732,26 @@ "require_for_exchanges_to_external_wallets": "Require for exchanges to external wallets", "camera_permission_is_required": "Camera permission is required. \nPlease enable it from app settings.", "switchToETHWallet": "Please switch to an Ethereum wallet and try again", + "importNFTs": "Import NFTs", + "noNFTYet": "No NFTs yet", + "address": "Address", + "enterTokenID": "Enter the token ID", + "tokenID": "ID", + "name": "Name", + "symbol": "Symbol", "seed_phrase_length": "Seed phrase length", "unavailable_balance": "Unavailable balance", "unavailable_balance_description": "Unavailable Balance: This total includes funds that are locked in pending transactions and those you have actively frozen in your coin control settings. Locked balances will become available once their respective transactions are completed, while frozen balances remain inaccessible for transactions until you decide to unfreeze them.", "unspent_change": "Change", "Block_remaining": "${status} Block Remaining", "labeled_silent_addresses": "Labeled Silent Addresses", - "use_testnet": "Use testnet" -} \ No newline at end of file + "use_testnet": "Use testnet", + "tor_connection": "Tor connection", + "seed_hex_form": "Wallet seed (hex form)", + "seedtype": "Seedtype", + "seedtype_legacy": "Legacy (25 words)", + "seedtype_polyseed": "Polyseed (16 words)", + "seed_language_czech": "Czech", + "seed_language_korean": "Korean", + "seed_language_chinese_traditional": "Chinese (Traditional)" +} diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 94dbf06b4..ad438eac4 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -160,7 +160,7 @@ "seed_title": "Semilla", "seed_share": "Compartir semillas", "copy": "Dupdo", - "seed_language_choose": "Por favor elija el idioma semilla:", + "seed_language": "Lenguaje de semillas", "seed_choose": "Elige el idioma semilla", "seed_language_next": "Próximo", "seed_language_english": "Inglés", @@ -571,7 +571,7 @@ "always": "siempre", "minutes_to_pin_code": "${minute} minutos", "disable_exchange": "Deshabilitar intercambio", - "advanced_privacy_settings": "Configuración avanzada de privacidad", + "advanced_settings": "Ajustes avanzados", "settings_can_be_changed_later": "Estas configuraciones se pueden cambiar más tarde en la configuración de la aplicación", "add_custom_node": "Agregar nuevo nodo personalizado", "disable_fiat": "Deshabilitar fiat", @@ -731,11 +731,26 @@ "require_for_exchanges_to_external_wallets": "Requerido para intercambios a billeteras externas", "camera_permission_is_required": "Se requiere permiso de la cámara.\nHabilítelo desde la configuración de la aplicación.", "switchToETHWallet": "Cambie a una billetera Ethereum e inténtelo nuevamente.", + "importNFTs": "Importar NFT", + "noNFTYet": "Aún no hay NFT", + "address": "DIRECCIÓN", + "enterTokenID": "Ingrese el ID del token", + "tokenID": "IDENTIFICACIÓN", + "name": "Nombre", + "symbol": "Símbolo", "seed_phrase_length": "Longitud de la frase inicial", "unavailable_balance": "Saldo no disponible", "unavailable_balance_description": "Saldo no disponible: este total incluye fondos que están bloqueados en transacciones pendientes y aquellos que usted ha congelado activamente en su configuración de control de monedas. Los saldos bloqueados estarán disponibles una vez que se completen sus respectivas transacciones, mientras que los saldos congelados permanecerán inaccesibles para las transacciones hasta que usted decida descongelarlos.", "unspent_change": "Cambiar", "Block_remaining": "${status} bloque restante", "labeled_silent_addresses": "Direcciones silenciosas etiquetadas", - "use_testnet": "Use TestNet" -} \ No newline at end of file + "use_testnet": "Use TestNet", + "tor_connection": "conexión tor", + "seed_hex_form": "Semilla de billetera (forma hexadecimal)", + "seedtype": "Type de semillas", + "seedtype_legacy": "Legado (25 palabras)", + "seedtype_polyseed": "Polieta (16 palabras)", + "seed_language_czech": "checo", + "seed_language_korean": "coreano", + "seed_language_chinese_traditional": "Chino (tradicional)" +} diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index b318de3f0..b394e55fe 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -160,7 +160,7 @@ "seed_title": "Phrase secrète (seed)", "seed_share": "Partager la phrase secrète (seed)", "copy": "Copier", - "seed_language_choose": "Merci de choisir la langue de la phrase secrète (seed) :", + "seed_language": "Langage de la phrase secrète", "seed_choose": "Choisissez la langue de la phrase secrète (seed)", "seed_language_next": "Suivant", "seed_language_english": "Anglais", @@ -571,7 +571,7 @@ "always": "toujours", "minutes_to_pin_code": "${minute} minutes", "disable_exchange": "Désactiver l'échange", - "advanced_privacy_settings": "Paramètres de confidentialité avancés", + "advanced_settings": "Réglages avancés", "settings_can_be_changed_later": "Ces paramètres peuvent être modifiés ultérieurement dans les paramètres de l'application", "add_custom_node": "Ajouter un nouveau nœud personnalisé", "disable_fiat": "Désactiver les montants en fiat", @@ -729,6 +729,15 @@ "exchange_provider_unsupported": "${providerName} n'est plus pris en charge !", "domain_looks_up": "Résolution de nom", "require_for_exchanges_to_external_wallets": "Exiger pour les échanges vers des portefeuilles externes", + "camera_permission_is_required": "L'autorisation de la caméra est requise.\nVeuillez l'activer à partir des paramètres de l'application.", + "switchToETHWallet": "Veuillez passer à un portefeuille (wallet) Ethereum et réessayer", + "importNFTs": "Importer des NFT", + "noNFTYet": "Pas encore de NFT", + "address": "Adresse", + "enterTokenID": "Entrez l'ID du jeton", + "tokenID": "IDENTIFIANT", + "name": "Nom", + "symbol": "Symbole", "seed_phrase_length": "Longueur de la phrase de départ", "unavailable_balance": "Solde indisponible", "unavailable_balance_description": "Solde indisponible : ce total comprend les fonds bloqués dans les transactions en attente et ceux que vous avez activement gelés dans vos paramètres de contrôle des pièces. Les soldes bloqués deviendront disponibles une fois leurs transactions respectives terminées, tandis que les soldes gelés resteront inaccessibles aux transactions jusqu'à ce que vous décidiez de les débloquer.", @@ -737,5 +746,14 @@ "unspent_change": "Changement", "Block_remaining": "${status} bloc restant", "labeled_silent_addresses": "Adresses silencieuses étiquetées", - "use_testnet": "Utiliser TestNet" -} \ No newline at end of file + "use_testnet": "Utiliser TestNet", + "unspent_change": "Changement", + "tor_connection": "Connexion Tor", + "seed_hex_form": "Graine du portefeuille (forme hexagonale)", + "seedtype": "Type de type graine", + "seedtype_legacy": "Héritage (25 mots)", + "seedtype_polyseed": "Polyseed (16 mots)", + "seed_language_czech": "tchèque", + "seed_language_korean": "coréen", + "seed_language_chinese_traditional": "Chinois (Traditionnel)" +} diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 794a2812c..adc593888 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -160,7 +160,7 @@ "seed_title": "iri", "seed_share": "Raba iri", "copy": "Kwafi", - "seed_language_choose": "Don Allah zaɓi harshen seed:", + "seed_language": "Harshen Magani", "seed_choose": "Zaɓi harshen seed", "seed_language_next": "Na gaba", "seed_language_english": "Ingilishi", @@ -570,7 +570,7 @@ "always": "Koyaushe", "minutes_to_pin_code": "${minute} minti", "disable_exchange": "Kashe musanya", - "advanced_privacy_settings": "Babban Saitunan Sirri", + "advanced_settings": "Saitunan ci gaba", "settings_can_be_changed_later": "Ana iya canza waɗannan saitunan daga baya a cikin saitunan app", "add_custom_node": "Ƙara Sabon Kulli na Custom", "disable_fiat": "Dakatar da fiat", @@ -709,11 +709,26 @@ "require_for_exchanges_to_external_wallets": "Bukatar musanya zuwa wallet na waje", "camera_permission_is_required": "Ana buƙatar izinin kyamara.\nDa fatan za a kunna shi daga saitunan app.", "switchToETHWallet": "Da fatan za a canza zuwa walat ɗin Ethereum kuma a sake gwadawa", + "importNFTs": "Shigo da NFTs", + "noNFTYet": "Babu NFTs tukuna", + "address": "Adireshi", + "enterTokenID": "Shigar da alamar alama", + "tokenID": "ID", + "name": "Suna", + "symbol": "Alama", "seed_phrase_length": "Tsawon jimlar iri", "unavailable_balance": "Ma'aunin da ba ya samuwa", "unavailable_balance_description": "Ma'auni Babu: Wannan jimlar ya haɗa da kuɗi waɗanda ke kulle a cikin ma'amaloli da ke jiran aiki da waɗanda kuka daskare sosai a cikin saitunan sarrafa kuɗin ku. Ma'auni da aka kulle za su kasance da zarar an kammala ma'amalolinsu, yayin da daskararrun ma'auni ba za su iya samun damar yin ciniki ba har sai kun yanke shawarar cire su.", "unspent_change": "Canza", "Block_remaining": "${status} toshe ragowar", "labeled_silent_addresses": "Mai labarar adireshin shiru", - "use_testnet": "Amfani da gwaji" -} \ No newline at end of file + "use_testnet": "Amfani da gwaji", + "tor_connection": "Tor haɗin gwiwa", + "seed_hex_form": "Gany Sero (form form)", + "seedtype": "Seedtype", + "seedtype_legacy": "Legacy (25 kalmomi)", + "seedtype_polyseed": "Polyseed (16 kalmomi)", + "seed_language_czech": "Czech", + "seed_language_korean": "Yaren Koriya", + "seed_language_chinese_traditional": "Sinanci (na gargajiya)" +} diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 48af5dc1d..e975676a7 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -160,7 +160,7 @@ "seed_title": "बीज", "seed_share": "बीज साझा करें", "copy": "प्रतिलिपि", - "seed_language_choose": "कृपया बीज भाषा चुनें:", + "seed_language": "बीज", "seed_choose": "बीज भाषा चुनें", "seed_language_next": "आगामी", "seed_language_english": "अंग्रेज़ी", @@ -571,7 +571,7 @@ "always": "हमेशा", "minutes_to_pin_code": "${minute} मिनट", "disable_exchange": "एक्सचेंज अक्षम करें", - "advanced_privacy_settings": "उन्नत गोपनीयता सेटिंग्स", + "advanced_settings": "एडवांस सेटिंग", "settings_can_be_changed_later": "इन सेटिंग्स को बाद में ऐप सेटिंग में बदला जा सकता है", "add_custom_node": "नया कस्टम नोड जोड़ें", "disable_fiat": "िएट को अक्षम करें", @@ -731,11 +731,26 @@ "require_for_exchanges_to_external_wallets": "बाहरी वॉलेट में एक्सचेंज की आवश्यकता है", "camera_permission_is_required": "कैमरे की अनुमति आवश्यक है.\nकृपया इसे ऐप सेटिंग से सक्षम करें।", "switchToETHWallet": "कृपया एथेरियम वॉलेट पर स्विच करें और पुनः प्रयास करें", + "importNFTs": "एनएफटी आयात करें", + "noNFTYet": "अभी तक कोई एनएफटी नहीं", + "address": "पता", + "enterTokenID": "टोकन आईडी दर्ज करें", + "tokenID": "पहचान", + "name": "नाम", + "symbol": "प्रतीक", "seed_phrase_length": "बीज वाक्यांश की लंबाई", "unavailable_balance": "अनुपलब्ध शेष", "unavailable_balance_description": "अनुपलब्ध शेष राशि: इस कुल में वे धनराशि शामिल हैं जो लंबित लेनदेन में बंद हैं और जिन्हें आपने अपनी सिक्का नियंत्रण सेटिंग्स में सक्रिय रूप से जमा कर रखा है। लॉक किए गए शेष उनके संबंधित लेन-देन पूरे होने के बाद उपलब्ध हो जाएंगे, जबकि जमे हुए शेष लेन-देन के लिए अप्राप्य रहेंगे जब तक कि आप उन्हें अनफ्रीज करने का निर्णय नहीं लेते।", "unspent_change": "परिवर्तन", "Block_remaining": "${status} शेष ब्लॉक", "labeled_silent_addresses": "मूक पते लेबल", - "use_testnet": "टेस्टनेट का उपयोग करें" -} \ No newline at end of file + "use_testnet": "टेस्टनेट का उपयोग करें", + "tor_connection": "टोर कनेक्शन", + "seed_hex_form": "वॉलेट सीड (हेक्स फॉर्म)", + "seedtype": "बीज", + "seedtype_legacy": "विरासत (25 शब्द)", + "seedtype_polyseed": "पॉलीसीड (16 शब्द)", + "seed_language_czech": "चेक", + "seed_language_korean": "कोरियाई", + "seed_language_chinese_traditional": "चीनी पारंपरिक)" +} diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 9ec57df71..3313dfb91 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -160,7 +160,7 @@ "seed_title": "Prisupni izraz", "seed_share": "Podijeli pristupni izraz", "copy": "Kopiraj", - "seed_language_choose": "Molimo odaberite jezik pristupnog izraza:", + "seed_language": "Sjemeni jezik", "seed_choose": "Odaberi jezik pristupnog izraza", "seed_language_next": "Dalje", "seed_language_english": "Engleski", @@ -383,7 +383,7 @@ "change_backup_password_alert": "Nećemo moći uvesti Vaše prethodne datoteke sigurnosne kopije s novom lozinkom za sigurnosnu kopiju. Novu lozinku za sigurnosnu kopiju moći ćete koristiti samo za nove datoteke sigurnosne kopije. Jeste li sigurni da želite promijeniti lozinku za sigurnosnu kopiju?", "enter_backup_password": "Unesite svoju lozinku za sigurnosnu kopiju ovdje", "select_backup_file": "Odaberite datoteku sigurnosne kopije", - "import": "Uvezi", + "import": "Uvoz", "please_select_backup_file": "Molimo odaberite datoteku sigurnosne kopije i unesite lozinku za sigurnosnu kopiju.", "fixed_rate": "Fiksna stopa", "fixed_rate_alert": "Moći ćete unijeti iznos koji želite primiti nakon što označite način rada fiksne stope. Želite li se prebaciti na način rada fiksne stope?", @@ -571,7 +571,7 @@ "always": "Uvijek", "minutes_to_pin_code": "${minute} minuta", "disable_exchange": "Onemogući exchange", - "advanced_privacy_settings": "Napredne postavke privatnosti", + "advanced_settings": "Napredne postavke", "settings_can_be_changed_later": "Te se postavke mogu promijeniti kasnije u postavkama aplikacije", "add_custom_node": "Dodaj novi prilagođeni čvor", "disable_fiat": "Isključi, fiat", @@ -729,11 +729,26 @@ "require_for_exchanges_to_external_wallets": "Zahtijeva razmjene na vanjske novčanike", "camera_permission_is_required": "Potrebno je dopuštenje kamere.\nOmogućite ga u postavkama aplikacije.", "switchToETHWallet": "Prijeđite na Ethereum novčanik i pokušajte ponovno", + "importNFTs": "Uvoz NFT-ova", + "noNFTYet": "Još nema NFT-ova", + "address": "Adresa", + "enterTokenID": "Unesite ID tokena", + "tokenID": "iskaznica", + "name": "Ime", + "symbol": "Simbol", "seed_phrase_length": "Duljina početne fraze", "unavailable_balance": "Nedostupno stanje", "unavailable_balance_description": "Nedostupno stanje: Ovaj ukupni iznos uključuje sredstva koja su zaključana u transakcijama na čekanju i ona koja ste aktivno zamrznuli u postavkama kontrole novčića. Zaključani saldi postat će dostupni kada se dovrše njihove transakcije, dok zamrznuti saldi ostaju nedostupni za transakcije sve dok ih ne odlučite odmrznuti.", "unspent_change": "Promijeniti", "Block_remaining": "${status} ostao blok", "labeled_silent_addresses": "Označene tihe adrese", - "use_testnet": "Koristite TestNet" -} \ No newline at end of file + "use_testnet": "Koristite TestNet", + "tor_connection": "Tor veza", + "seed_hex_form": "Sjeme novčanika (šesterokutni oblik)", + "seedtype": "Sjemenska vrsta", + "seedtype_legacy": "Nasljeđe (25 riječi)", + "seedtype_polyseed": "Poliseed (16 riječi)", + "seed_language_czech": "češki", + "seed_language_korean": "korejski", + "seed_language_chinese_traditional": "Kinesko (tradicionalno)" +} diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index ded93e095..493b3ce19 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -160,7 +160,7 @@ "seed_title": "Bibit", "seed_share": "Bagikan bibit", "copy": "Salin", - "seed_language_choose": "Silakan pilih bahasa bibit:", + "seed_language": "Bahasa benih", "seed_choose": "Pilih bahasa bibit", "seed_language_next": "Selanjutnya", "seed_language_english": "Inggris", @@ -570,7 +570,7 @@ "always": "Selalu", "minutes_to_pin_code": "${minute} menit", "disable_exchange": "Nonaktifkan pertukaran", - "advanced_privacy_settings": "Pengaturan Privasi Lanjutan", + "advanced_settings": "Pengaturan lanjutan", "settings_can_be_changed_later": "Pengaturan ini dapat diubah nanti di pengaturan aplikasi", "add_custom_node": "Tambahkan Node Kustom Baru", "disable_fiat": "Nonaktifkan fiat", @@ -719,11 +719,26 @@ "require_for_exchanges_to_external_wallets": "Memerlukan pertukaran ke dompet eksternal", "camera_permission_is_required": "Izin kamera diperlukan.\nSilakan aktifkan dari pengaturan aplikasi.", "switchToETHWallet": "Silakan beralih ke dompet Ethereum dan coba lagi", + "importNFTs": "Impor NFT", + "noNFTYet": "Belum ada NFT", + "address": "Alamat", + "enterTokenID": "Masukkan ID tokennya", + "tokenID": "PENGENAL", + "name": "Nama", + "symbol": "Simbol", "seed_phrase_length": "Panjang frase benih", "unavailable_balance": "Saldo tidak tersedia", "unavailable_balance_description": "Saldo Tidak Tersedia: Total ini termasuk dana yang terkunci dalam transaksi yang tertunda dan dana yang telah Anda bekukan secara aktif di pengaturan kontrol koin Anda. Saldo yang terkunci akan tersedia setelah transaksi masing-masing selesai, sedangkan saldo yang dibekukan tetap tidak dapat diakses untuk transaksi sampai Anda memutuskan untuk mencairkannya.", "unspent_change": "Mengubah", "Block_remaining": "${status} blok tersisa", "labeled_silent_addresses": "Label alamat diam", - "use_testnet": "Gunakan TestNet" -} \ No newline at end of file + "use_testnet": "Gunakan TestNet", + "tor_connection": "koneksi Tor", + "seed_hex_form": "Biji dompet (bentuk hex)", + "seedtype": "Seedtype", + "seedtype_legacy": "Legacy (25 kata)", + "seedtype_polyseed": "Polyseed (16 kata)", + "seed_language_czech": "Ceko", + "seed_language_korean": "Korea", + "seed_language_chinese_traditional": "Cina (tradisional)" +} diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 461809a9e..73000621f 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -160,7 +160,7 @@ "seed_title": "Seme", "seed_share": "Condividi seme", "copy": "Copia", - "seed_language_choose": "Gentilmente scegli la lingua del seme:", + "seed_language": "Linguaggio di semi", "seed_choose": "Scegli la lingua del seme", "seed_language_next": "Prossimo", "seed_language_english": "Inglese", @@ -383,7 +383,7 @@ "change_backup_password_alert": "I precedenti file di backup non potranno essere importati con la nuova password di backup. La nuova password di backup verrà usata soltanto per i nuovi file di backup. Sei sicuro di voler cambiare la tua password di backup?", "enter_backup_password": "Inserisci la password di backup qui", "select_backup_file": "Seleziona file di backup", - "import": "Importa", + "import": "Importare", "please_select_backup_file": "Gentilmente seleziona il file di backup e inserisci la password di backup.", "fixed_rate": "Tasso fisso", "fixed_rate_alert": "Potrai inserire l'ammontare da ricevere quando il tasso è fisso. Vuoi cambiare alla modalità tasso fisso?", @@ -571,7 +571,7 @@ "always": "sempre", "minutes_to_pin_code": "${minute} minuti", "disable_exchange": "Disabilita scambio", - "advanced_privacy_settings": "Impostazioni avanzate sulla privacy", + "advanced_settings": "Impostazioni avanzate", "settings_can_be_changed_later": "Queste impostazioni possono essere modificate in seguito nelle impostazioni dell'app", "add_custom_node": "Aggiungi nuovo nodo personalizzato", "disable_fiat": "Disabilita fiat", @@ -731,11 +731,26 @@ "require_for_exchanges_to_external_wallets": "Richiede scambi con portafogli esterni", "camera_permission_is_required": "È richiesta l'autorizzazione della fotocamera.\nAbilitalo dalle impostazioni dell'app.", "switchToETHWallet": "Passa a un portafoglio Ethereum e riprova", + "importNFTs": "Importa NFT", + "noNFTYet": "Nessun NFT ancora", + "address": "Indirizzo", + "enterTokenID": "Inserisci l'ID del token", + "tokenID": "ID", + "name": "Nome", + "symbol": "Simbolo", "seed_phrase_length": "Lunghezza della frase seed", "unavailable_balance": "Saldo non disponibile", "unavailable_balance_description": "Saldo non disponibile: questo totale include i fondi bloccati nelle transazioni in sospeso e quelli che hai congelato attivamente nelle impostazioni di controllo delle monete. I saldi bloccati diventeranno disponibili una volta completate le rispettive transazioni, mentre i saldi congelati rimarranno inaccessibili per le transazioni finché non deciderai di sbloccarli.", "unspent_change": "Modifica", "Block_remaining": "${status} blocco rimanente", "labeled_silent_addresses": "Indirizzi silenziosi etichettati", - "use_testnet": "Usa TestNet" -} \ No newline at end of file + "use_testnet": "Usa TestNet", + "tor_connection": "Connessione Tor", + "seed_hex_form": "Seme di portafoglio (forma esadecimale)", + "seedtype": "Seedtype", + "seedtype_legacy": "Legacy (25 parole)", + "seedtype_polyseed": "Polyseed (16 parole)", + "seed_language_czech": "ceco", + "seed_language_korean": "coreano", + "seed_language_chinese_traditional": "Cinese tradizionale)" +} diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 93a827f0d..3beb94da3 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -160,7 +160,7 @@ "seed_title": "シード", "seed_share": "シードを共有する", "copy": "コピー", - "seed_language_choose": "シード言語を選択してください:", + "seed_language": "シード言語", "seed_choose": "シード言語を選択してください", "seed_language_next": "次", "seed_language_english": "英語", @@ -383,7 +383,7 @@ "change_backup_password_alert": "以前のバックアップファイルは、新しいバックアップパスワードでインポートできなくなります。 新しいバックアップパスワードは、新しいバックアップファイルにのみ使用されます。 バックアップパスワードを変更してもよろしいですか?", "enter_backup_password": "ここにバックアップパスワードを入力してください", "select_backup_file": "バックアップファイルを選択", - "import": "インポート", + "import": "輸入", "please_select_backup_file": "バックアップファイルを選択し、バックアップパスワードを入力してください。", "fixed_rate": "固定金利", "fixed_rate_alert": "固定金利モードにチェックを入れると、受取額を入力できるようになります。 固定金利モードに切り替えますか?", @@ -571,7 +571,7 @@ "always": "いつも", "minutes_to_pin_code": "${minute} 分", "disable_exchange": "交換を無効にする", - "advanced_privacy_settings": "高度なプライバシー設定", + "advanced_settings": "高度な設定", "settings_can_be_changed_later": "これらの設定は、後でアプリの設定で変更できます", "add_custom_node": "新しいカスタム ノードを追加", "disable_fiat": "フィアットを無効にする", @@ -731,11 +731,26 @@ "require_for_exchanges_to_external_wallets": "外部ウォレットへの交換に必要", "camera_permission_is_required": "カメラの許可が必要です。\nアプリの設定から有効にしてください。", "switchToETHWallet": "イーサリアムウォレットに切り替えてもう一度お試しください", + "importNFTs": "NFTのインポート", + "noNFTYet": "NFTはまだありません", + "address": "住所", + "enterTokenID": "トークンIDを入力してください", + "tokenID": "ID", + "name": "名前", + "symbol": "シンボル", "seed_phrase_length": "シードフレーズの長さ", "unavailable_balance": "利用できない残高", "unavailable_balance_description": "利用不可能な残高: この合計には、保留中のトランザクションにロックされている資金と、コイン管理設定でアクティブに凍結した資金が含まれます。ロックされた残高は、それぞれの取引が完了すると利用可能になりますが、凍結された残高は、凍結を解除するまで取引にアクセスできません。", "unspent_change": "変化", "Block_remaining": "${status}ブロックの残り", "labeled_silent_addresses": "サイレントアドレスとラベル付けされています", - "use_testnet": "TestNetを使用します" -} \ No newline at end of file + "use_testnet": "TestNetを使用します", + "tor_connection": "Tor接続", + "seed_hex_form": "ウォレットシード(ヘックスフォーム)", + "seedtype": "SeedType", + "seedtype_legacy": "レガシー(25語)", + "seedtype_polyseed": "ポリシード(16語)", + "seed_language_czech": "チェコ", + "seed_language_korean": "韓国語", + "seed_language_chinese_traditional": "中国の伝統的な)" +} diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 6e0fb6bc8..758c89ad0 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -160,7 +160,7 @@ "seed_title": "씨", "seed_share": "시드 공유", "copy": "부", - "seed_language_choose": "종자 언어를 선택하십시오:", + "seed_language": "종자 언어", "seed_choose": "시드 언어를 선택하십시오", "seed_language_next": "다음 것", "seed_language_english": "영어", @@ -571,7 +571,7 @@ "always": "언제나", "minutes_to_pin_code": "${minute}분", "disable_exchange": "교환 비활성화", - "advanced_privacy_settings": "고급 개인 정보 설정", + "advanced_settings": "고급 설정", "settings_can_be_changed_later": "이 설정은 나중에 앱 설정에서 변경할 수 있습니다.", "add_custom_node": "새 사용자 정의 노드 추가", "disable_fiat": "법정화폐 비활성화", @@ -729,11 +729,26 @@ "require_for_exchanges_to_external_wallets": "외부 지갑으로의 교환을 위해 필요", "camera_permission_is_required": "카메라 권한이 필요합니다.\n앱 설정에서 활성화해 주세요.", "switchToETHWallet": "이더리움 지갑으로 전환한 후 다시 시도해 주세요.", + "importNFTs": "NFT 가져오기", + "noNFTYet": "아직 NFT가 없습니다", + "address": "주소", + "enterTokenID": "토큰 ID를 입력하세요", + "tokenID": "ID", + "name": "이름", + "symbol": "상징", "seed_phrase_length": "시드 문구 길이", "unavailable_balance": "사용할 수 없는 잔액", "unavailable_balance_description": "사용할 수 없는 잔액: 이 총계에는 보류 중인 거래에 잠겨 있는 자금과 코인 관리 설정에서 적극적으로 동결된 자금이 포함됩니다. 잠긴 잔액은 해당 거래가 완료되면 사용할 수 있게 되며, 동결된 잔액은 동결을 해제하기 전까지 거래에 액세스할 수 없습니다.", "unspent_change": "변화", "Block_remaining": "${status} 나머지 블록", "labeled_silent_addresses": "라벨링 된 무음 주소", - "use_testnet": "TestNet을 사용하십시오" -} \ No newline at end of file + "use_testnet": "TestNet을 사용하십시오", + "tor_connection": "토르 연결", + "seed_hex_form": "지갑 씨앗 (16 진 양식)", + "seedtype": "시드 타입", + "seedtype_legacy": "레거시 (25 단어)", + "seedtype_polyseed": "다문 (16 단어)", + "seed_language_czech": "체코 사람", + "seed_language_korean": "한국인", + "seed_language_chinese_traditional": "중국 전통)" +} diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 9e1ac4d7e..48434424f 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -160,7 +160,7 @@ "seed_title": "မျိုးစေ့", "seed_share": "မျိုးစေ့မျှဝေပါ။", "copy": "ကော်ပီ", - "seed_language_choose": "ကျေးဇူးပြု၍ မျိုးစေ့ဘာသာစကားကို ရွေးပါ-", + "seed_language": "မျိုးစေ့ဘာသာ", "seed_choose": "မျိုးစေ့ဘာသာစကားကို ရွေးချယ်ပါ။", "seed_language_next": "နောက်တစ်ခု", "seed_language_english": "အင်္ဂလိပ်စာ", @@ -569,7 +569,7 @@ "always": "အမြဲတမ်း", "minutes_to_pin_code": "${minute} မိနစ်", "disable_exchange": "လဲလှယ်မှုကို ပိတ်ပါ။", - "advanced_privacy_settings": "အဆင့်မြင့် ကိုယ်ရေးကိုယ်တာ ဆက်တင်များ", + "advanced_settings": "အဆင့်မြင့်ချိန်ညှိချက်များ", "settings_can_be_changed_later": "အက်ပ်ဆက်တင်များတွင် ဤဆက်တင်များကို နောက်ပိုင်းတွင် ပြောင်းလဲနိုင်သည်။", "add_custom_node": "စိတ်ကြိုက် Node အသစ်ကို ထည့်ပါ။", "disable_fiat": "Fiat ကိုပိတ်ပါ။", @@ -729,11 +729,26 @@ "require_for_exchanges_to_external_wallets": "ပြင်ပပိုက်ဆံအိတ်များသို့ လဲလှယ်ရန် လိုအပ်သည်။", "camera_permission_is_required": "ကင်မရာခွင့်ပြုချက် လိုအပ်ပါသည်။\nအက်ပ်ဆက်တင်များမှ ၎င်းကိုဖွင့်ပါ။", "switchToETHWallet": "ကျေးဇူးပြု၍ Ethereum ပိုက်ဆံအိတ်သို့ ပြောင်းပြီး ထပ်စမ်းကြည့်ပါ။", + "importNFTs": "NFTs များကို တင်သွင်းပါ။", + "noNFTYet": "NFTs မရှိသေးပါ။", + "address": "လိပ်စာ", + "enterTokenID": "တိုကင် ID ကိုထည့်ပါ။", + "tokenID": "အမှတ်သညာ", + "name": "နာမည်", + "symbol": "သင်္ကေတ", "seed_phrase_length": "မျိုးစေ့စာပိုဒ်တိုအရှည်", "unavailable_balance": "လက်ကျန်ငွေ မရရှိနိုင်ပါ။", "unavailable_balance_description": "မရရှိနိုင်သော လက်ကျန်ငွေ- ဤစုစုပေါင်းတွင် ဆိုင်းငံ့ထားသော ငွေပေးငွေယူများတွင် သော့ခတ်ထားသော ငွေကြေးများနှင့် သင်၏ coin ထိန်းချုပ်မှုဆက်တင်များတွင် သင် တက်ကြွစွာ အေးခဲထားသော ငွေများ ပါဝင်သည်။ သော့ခတ်ထားသော လက်ကျန်ငွေများကို ၎င်းတို့၏ သက်ဆိုင်ရာ ငွေပေးငွေယူများ ပြီးမြောက်သည်နှင့် တပြိုင်နက် ရရှိနိုင်မည်ဖြစ်ပြီး၊ အေးခဲထားသော လက်ကျန်များကို ၎င်းတို့အား ပြန်ဖြုတ်ရန် သင်ဆုံးဖြတ်သည်အထိ ငွေပေးငွေယူများအတွက် ဆက်လက်၍မရနိုင်ပါ။", "unspent_change": "ပေြာင်းလဲခြင်း", "Block_remaining": "ကျန်ရှိသော ${status}", "labeled_silent_addresses": "အသံတိတ်အသံတိတ်လိပ်စာများတံဆိပ်ကပ်", - "use_testnet": "testnet ကိုသုံးပါ" -} \ No newline at end of file + "use_testnet": "testnet ကိုသုံးပါ", + "tor_connection": "Tor ချိတ်ဆက်မှု", + "seed_hex_form": "ပိုက်ဆံအိတ်မျိုးစေ့ (Hex Form)", + "seedtype": "မျိုးပွားခြင်း", + "seedtype_legacy": "အမွေအနှစ် (စကားလုံး 25 လုံး)", + "seedtype_polyseed": "polyseed (စကားလုံး 16 လုံး)", + "seed_language_czech": "ချက်", + "seed_language_korean": "ကိုးရီးယား", + "seed_language_chinese_traditional": "တရုတ်ရိုးရာ)" +} diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 4f5481bd1..3d7e46a83 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -160,7 +160,7 @@ "seed_title": "Zaad", "seed_share": "Deel zaad", "copy": "Kopiëren", - "seed_language_choose": "Kies een starttaal:", + "seed_language": "Zaadtaal", "seed_choose": "Kies een starttaal", "seed_language_next": "Volgende", "seed_language_english": "Engels", @@ -571,7 +571,7 @@ "always": "altijd", "minutes_to_pin_code": "${minute} minuten", "disable_exchange": "Uitwisseling uitschakelen", - "advanced_privacy_settings": "Geavanceerde privacy-instellingen", + "advanced_settings": "Geavanceerde instellingen", "settings_can_be_changed_later": "Deze instellingen kunnen later worden gewijzigd in de app-instellingen", "add_custom_node": "Voeg een nieuw aangepast knooppunt toe", "disable_fiat": "Schakel Fiat uit", @@ -731,11 +731,26 @@ "require_for_exchanges_to_external_wallets": "Vereist voor uitwisselingen naar externe portemonnees", "camera_permission_is_required": "Cameratoestemming is vereist.\nSchakel dit in via de app-instellingen.", "switchToETHWallet": "Schakel over naar een Ethereum-portemonnee en probeer het opnieuw", + "importNFTs": "NFT's importeren", + "noNFTYet": "Nog geen NFT's", + "address": "Adres", + "enterTokenID": "Voer de token-ID in", + "tokenID": "ID kaart", + "name": "Naam", + "symbol": "Symbool", "seed_phrase_length": "Lengte van de zaadzin", "unavailable_balance": "Onbeschikbaar saldo", "unavailable_balance_description": "Niet-beschikbaar saldo: Dit totaal omvat het geld dat is vergrendeld in lopende transacties en het geld dat u actief hebt bevroren in uw muntcontrole-instellingen. Vergrendelde saldi komen beschikbaar zodra de betreffende transacties zijn voltooid, terwijl bevroren saldi ontoegankelijk blijven voor transacties totdat u besluit ze weer vrij te geven.", "unspent_change": "Wijziging", "Block_remaining": "${status} blok resterend", "labeled_silent_addresses": "Gelabelde stille adressen", - "use_testnet": "Gebruik testnet" -} \ No newline at end of file + "use_testnet": "Gebruik testnet", + "tor_connection": "Tor-verbinding", + "seed_hex_form": "Portemonnee zaad (hexvorm)", + "seedtype": "Zaadtype", + "seedtype_legacy": "Legacy (25 woorden)", + "seedtype_polyseed": "Polyseed (16 woorden)", + "seed_language_czech": "Tsjechisch", + "seed_language_korean": "Koreaans", + "seed_language_chinese_traditional": "Chinese (traditionele)" +} diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 39f53e40e..6b88357f0 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -160,7 +160,7 @@ "seed_title": "Seed", "seed_share": "Udostępnij seed", "copy": "Kopiuj", - "seed_language_choose": "Proszę wybrać język słów we frazie seed:", + "seed_language": "Język nasion", "seed_choose": "Wybierz język", "seed_language_next": "Następny", "seed_language_english": "Angielski", @@ -383,7 +383,7 @@ "change_backup_password_alert": "Twoje poprzednie pliki kopii zapasowej nie będą dostępne do zaimportowania z nowym hasłem kopii zapasowej. Nowe hasło kopii zapasowej będzie używane tylko dla nowych plików kopii zapasowych. Czy na pewno chcesz zmienić hasło zapasowe?", "enter_backup_password": "Wprowadź tutaj hasło kopii zapasowej", "select_backup_file": "Wybierz plik kopii zapasowej", - "import": "Zaimportuj", + "import": "Import", "please_select_backup_file": "Wybierz plik kopii zapasowej i wprowadź hasło.", "fixed_rate": "Stała stawka", "fixed_rate_alert": "Będziesz mógł wprowadzić kwotę do otrzymania, gdy wybrany bedzie tryb stałego przeliczenia. Czy chcesz przejść do trybu stałej stawki?", @@ -571,7 +571,7 @@ "always": "zawsze", "minutes_to_pin_code": "${minute} minut", "disable_exchange": "Wyłącz wymianę", - "advanced_privacy_settings": "Zaawansowane ustawienia prywatności", + "advanced_settings": "Zaawansowane ustawienia", "settings_can_be_changed_later": "Te ustawienia można później zmienić w ustawieniach aplikacji", "add_custom_node": "Dodaj nowy węzeł niestandardowy", "disable_fiat": "Wyłącz waluty FIAT", @@ -731,11 +731,26 @@ "require_for_exchanges_to_external_wallets": "Wymagaj wymiany na portfele zewnętrzne", "camera_permission_is_required": "Wymagane jest pozwolenie na korzystanie z aparatu.\nWłącz tę funkcję w ustawieniach aplikacji.", "switchToETHWallet": "Przejdź na portfel Ethereum i spróbuj ponownie", + "importNFTs": "Importuj NFT", + "noNFTYet": "Nie ma jeszcze NFT", + "address": "Adres", + "enterTokenID": "Wprowadź identyfikator tokena", + "tokenID": "ID", + "name": "Nazwa", + "symbol": "Symbol", "seed_phrase_length": "Długość frazy początkowej", "unavailable_balance": "Niedostępne saldo", "unavailable_balance_description": "Niedostępne saldo: Suma ta obejmuje środki zablokowane w transakcjach oczekujących oraz te, które aktywnie zamroziłeś w ustawieniach kontroli monet. Zablokowane salda staną się dostępne po zakończeniu odpowiednich transakcji, natomiast zamrożone salda pozostaną niedostępne dla transakcji, dopóki nie zdecydujesz się ich odblokować.", "unspent_change": "Zmiana", "Block_remaining": "${status} Block pozostały", "labeled_silent_addresses": "Oznaczone ciche adresy", - "use_testnet": "Użyj testne" -} \ No newline at end of file + "use_testnet": "Użyj testne", + "tor_connection": "Połączenie Torem", + "seed_hex_form": "Nasiona portfela (forma sześciokątna)", + "seedtype": "Sedtype", + "seedtype_legacy": "Dziedzictwo (25 słów)", + "seedtype_polyseed": "Poliqueed (16 słów)", + "seed_language_czech": "Czech", + "seed_language_korean": "koreański", + "seed_language_chinese_traditional": "Chiński tradycyjny)" +} diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index d875ba063..fa78964fd 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -160,7 +160,7 @@ "seed_title": "Semente", "seed_share": "Compartilhar semente", "copy": "Copiar", - "seed_language_choose": "Por favor, escolha o idioma da semente:", + "seed_language": "Linguagem de semente", "seed_choose": "Escolha o idioma da semente", "seed_language_next": "Próximo", "seed_language_english": "Inglesa", @@ -570,7 +570,7 @@ "always": "sempre", "minutes_to_pin_code": "${minute} minutos", "disable_exchange": "Desativar troca", - "advanced_privacy_settings": "Configurações de privacidade avançadas", + "advanced_settings": "Configurações avançadas", "settings_can_be_changed_later": "Essas configurações podem ser alteradas posteriormente nas configurações do aplicativo", "add_custom_node": "Adicionar novo nó personalizado", "disable_fiat": "Desativar fiat", @@ -730,11 +730,26 @@ "require_for_exchanges_to_external_wallets": "Exigir trocas para carteiras externas", "camera_permission_is_required": "É necessária permissão da câmera.\nAtive-o nas configurações do aplicativo.", "switchToETHWallet": "Mude para uma carteira Ethereum e tente novamente", + "importNFTs": "Importar NFTs", + "noNFTYet": "Ainda não há NFT", + "address": "Endereço", + "enterTokenID": "Insira o ID do token", + "tokenID": "EU IA", + "name": "Nome", + "symbol": "Símbolo", "seed_phrase_length": "Comprimento da frase-semente", "unavailable_balance": "Saldo indisponível", "unavailable_balance_description": "Saldo Indisponível: Este total inclui fundos bloqueados em transações pendentes e aqueles que você congelou ativamente nas configurações de controle de moedas. Os saldos bloqueados ficarão disponíveis assim que suas respectivas transações forem concluídas, enquanto os saldos congelados permanecerão inacessíveis para transações até que você decida descongelá-los.", "unspent_change": "Mudar", "Block_remaining": "${status} bloco restante", "labeled_silent_addresses": "Endereços silenciosos rotulados", - "use_testnet": "Use testNet" -} \ No newline at end of file + "use_testnet": "Use testNet", + "tor_connection": "Conexão Tor", + "seed_hex_form": "Semente de carteira (forma hexadecimal)", + "seedtype": "SeedType", + "seedtype_legacy": "Legado (25 palavras)", + "seedtype_polyseed": "Polyseed (16 palavras)", + "seed_language_czech": "Tcheco", + "seed_language_korean": "coreano", + "seed_language_chinese_traditional": "Chinês tradicional)" +} diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index ffe22037b..7546fe748 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -160,7 +160,7 @@ "seed_title": "Мнемоническая фраза", "seed_share": "Поделиться мнемонической фразой", "copy": "Скопировать", - "seed_language_choose": "Пожалуйста, выберите язык мнемонической фразы:", + "seed_language": "Язык семян", "seed_choose": "Выберите язык мнемонической фразы", "seed_language_next": "Продолжить", "seed_language_english": "Английский", @@ -571,7 +571,7 @@ "always": "всегда", "minutes_to_pin_code": "${minute} минут", "disable_exchange": "Отключить обмен", - "advanced_privacy_settings": "Расширенные настройки конфиденциальности", + "advanced_settings": "Расширенные настройки", "settings_can_be_changed_later": "Эти настройки можно изменить позже в настройках приложения.", "add_custom_node": "Добавить новый пользовательский узел", "disable_fiat": "Отключить фиат", @@ -731,11 +731,26 @@ "require_for_exchanges_to_external_wallets": "Требовать обмена на внешние кошельки", "camera_permission_is_required": "Требуется разрешение камеры.\nПожалуйста, включите его в настройках приложения.", "switchToETHWallet": "Пожалуйста, переключитесь на кошелек Ethereum и повторите попытку.", + "importNFTs": "Импортировать NFT", + "noNFTYet": "NFT пока нет", + "address": "Адрес", + "enterTokenID": "Введите идентификатор токена", + "tokenID": "ИДЕНТИФИКАТОР", + "name": "Имя", + "symbol": "Символ", "seed_phrase_length": "Длина исходной фразы", "unavailable_balance": "Недоступный баланс", "unavailable_balance_description": "Недоступный баланс: в эту сумму входят средства, заблокированные в ожидающих транзакциях, и средства, которые вы активно заморозили в настройках управления монетами. Заблокированные балансы станут доступны после завершения соответствующих транзакций, а замороженные балансы останутся недоступными для транзакций, пока вы не решите их разморозить.", "unspent_change": "Изменять", "Block_remaining": "${status} оставшееся блок", "labeled_silent_addresses": "Помеченные тихий адреса", - "use_testnet": "Используйте Testnet" -} \ No newline at end of file + "use_testnet": "Используйте Testnet", + "tor_connection": "Тор соединение", + "seed_hex_form": "Семя кошелька (шестнадцатеричная форма)", + "seedtype": "SEEDTYPE", + "seedtype_legacy": "Наследие (25 слов)", + "seedtype_polyseed": "Полиса (16 слов)", + "seed_language_czech": "Чешский", + "seed_language_korean": "Корейский", + "seed_language_chinese_traditional": "Китайский традиционный)" +} diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 03daf1a93..d26a896e8 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -160,7 +160,7 @@ "seed_title": "Seed", "seed_share": "แบ่งปัน seed", "copy": "คัดลอก", - "seed_language_choose": "โปรดเลือกภาษาของ seed:", + "seed_language": "ภาษาเมล็ด", "seed_choose": "เลือกภาษาของ seed", "seed_language_next": "ถัดไป", "seed_language_english": "อังกฤษ", @@ -569,7 +569,7 @@ "always": "เสมอ", "minutes_to_pin_code": "${minute} นาที", "disable_exchange": "ปิดใช้งานการแลกเปลี่ยน", - "advanced_privacy_settings": "การตั้งค่าความเป็นส่วนตัวขั้นสูง", + "advanced_settings": "ตั้งค่าขั้นสูง", "settings_can_be_changed_later": "การตั้งค่านี้สามารถเปลี่ยนแปลงได้ภายหลังในการตั้งค่าแอพฯ", "add_custom_node": "เพิ่มจุดโหนดแบบกำหนดเอง", "disable_fiat": "ปิดใช้งานสกุลเงินตรา", @@ -729,11 +729,26 @@ "require_for_exchanges_to_external_wallets": "จำเป็นต้องแลกเปลี่ยนกับกระเป๋าเงินภายนอก", "camera_permission_is_required": "ต้องได้รับอนุญาตจากกล้อง\nโปรดเปิดใช้งานจากการตั้งค่าแอป", "switchToETHWallet": "โปรดเปลี่ยนไปใช้กระเป๋าเงิน Ethereum แล้วลองอีกครั้ง", + "importNFTs": "นำเข้า NFT", + "noNFTYet": "ยังไม่มี NFT", + "address": "ที่อยู่", + "enterTokenID": "ป้อนรหัสโทเค็น", + "tokenID": "บัตรประจำตัวประชาชน", + "name": "ชื่อ", + "symbol": "เครื่องหมาย", "seed_phrase_length": "ความยาววลีของเมล็ด", "unavailable_balance": "ยอดคงเหลือไม่พร้อมใช้งาน", "unavailable_balance_description": "ยอดคงเหลือที่ไม่พร้อมใช้งาน: ยอดรวมนี้รวมถึงเงินทุนที่ถูกล็อคในการทำธุรกรรมที่รอดำเนินการและที่คุณได้แช่แข็งไว้ในการตั้งค่าการควบคุมเหรียญของคุณ ยอดคงเหลือที่ถูกล็อคจะพร้อมใช้งานเมื่อธุรกรรมที่เกี่ยวข้องเสร็จสมบูรณ์ ในขณะที่ยอดคงเหลือที่แช่แข็งจะไม่สามารถเข้าถึงได้สำหรับธุรกรรมจนกว่าคุณจะตัดสินใจยกเลิกการแช่แข็ง", "unspent_change": "เปลี่ยน", "Block_remaining": "${status} เหลือบล็อกที่เหลืออยู่", "labeled_silent_addresses": "ที่อยู่เงียบที่มีป้ายกำกับ", - "use_testnet": "ใช้ testnet" -} \ No newline at end of file + "use_testnet": "ใช้ testnet", + "tor_connection": "การเชื่อมต่อทอร์", + "seed_hex_form": "เมล็ดกระเป๋าเงิน (รูปแบบฐานสิบหก)", + "seedtype": "เมล็ดพันธุ์", + "seedtype_legacy": "มรดก (25 คำ)", + "seedtype_polyseed": "โพลีส (16 คำ)", + "seed_language_czech": "ภาษาเช็ก", + "seed_language_korean": "เกาหลี", + "seed_language_chinese_traditional": "จีน (ดั้งเดิม)" +} diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 5734b3133..48c6d2164 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -160,7 +160,7 @@ "seed_title": "Binhi", "seed_share": "Magbahagi ng binhi", "copy": "Kopya", - "seed_language_choose": "Mangyaring pumili ng wika ng binhi:", + "seed_language": "Wika ng binhi", "seed_choose": "Pumili ng wika ng binhi", "seed_language_next": "Susunod", "seed_language_english": "Ingles", @@ -572,7 +572,7 @@ "always": "Palagi", "minutes_to_pin_code": "${minute} minuto", "disable_exchange": "Huwag paganahin ang palitan", - "advanced_privacy_settings": "Mga setting ng advanced na privacy", + "advanced_settings": "Mga Advanced na Setting", "settings_can_be_changed_later": "Ang mga setting na ito ay maaaring mabago mamaya sa mga setting ng app", "add_custom_node": "Magdagdag ng bagong pasadyang node", "disable_fiat": "Huwag paganahin ang Fiat", @@ -726,11 +726,25 @@ "require_for_exchanges_to_external_wallets": "Kinakailangan para sa mga palitan sa mga panlabas na wallet", "camera_permission_is_required": "Kinakailangan ang pahintulot sa camera.\nMangyaring paganahin ito mula sa mga setting ng app.", "switchToETHWallet": "Mangyaring lumipat sa isang Ethereum wallet at subukang muli", + "importNFTs": "Mag-import ng mga NFT", + "noNFTYet": "Wala pang NFT", + "address": "Address", + "enterTokenID": "Ilagay ang token ID", + "tokenID": "ID", + "name": "Pangalan", + "symbol": "Simbolo", "seed_phrase_length": "Haba ng parirala ng binhi", "unavailable_balance": "Hindi available na balanse", "unavailable_balance_description": "Hindi Available na Balanse: Kasama sa kabuuang ito ang mga pondong naka-lock sa mga nakabinbing transaksyon at ang mga aktibong na-freeze mo sa iyong mga setting ng kontrol ng coin. Magiging available ang mga naka-lock na balanse kapag nakumpleto na ang kani-kanilang mga transaksyon, habang ang mga nakapirming balanse ay nananatiling hindi naa-access para sa mga transaksyon hanggang sa magpasya kang i-unfreeze ang mga ito.", "unspent_change": "Baguhin", "Block_remaining": "${status} I -block ang natitira", "labeled_silent_addresses": "May label na tahimik na mga address", - "use_testnet": "Gumamit ng testnet" -} \ No newline at end of file + "use_testnet": "Gumamit ng testnet", + "tor_connection": "Koneksyon ng Tor", + "seed_hex_form": "Wallet seed (hex form)", + "seedtype_legacy": "Pamana (25 salita)", + "seedtype_polyseed": "Polyseed (16 na salita)", + "seed_language_czech": "Czech", + "seed_language_korean": "Korean", + "seed_language_chinese_traditional": "Intsik (tradisyonal)" +} diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 889f3e5a1..96347d610 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -160,7 +160,7 @@ "seed_title": "Tohum", "seed_share": "Tohumu paylaş", "copy": "Kopyala", - "seed_language_choose": "Lütfen tohum dilini seç:", + "seed_language": "Tohum dili", "seed_choose": "Tohum dilini seçin", "seed_language_next": "İleri", "seed_language_english": "İngilizce", @@ -383,7 +383,7 @@ "change_backup_password_alert": "Önceki yedekleme dosyaların yeni yedek parolası ile içe aktarılamayacaktır. Yeni yedekleme parolası yalnızca yeni yedekleme dosyaları için kullanılabilir olacak. Yedekleme parolasını değiştirmek istediğinden emin misin?", "enter_backup_password": "Yedekleme parolasını buraya gir", "select_backup_file": "Yedek dosyası seç", - "import": "İçe aktar", + "import": "İçe aktarmak", "please_select_backup_file": "Lütfen yedekleme dosyasını seç ve yedekleme parolasını gir.", "fixed_rate": "Sabit oran", "fixed_rate_alert": "Sabit oran modunu işaretlersen alım tutarını girebilirsin. Sabit oran moduna geçmek ister misin?", @@ -569,7 +569,7 @@ "always": "Her Zaman", "minutes_to_pin_code": "${minute} dakika", "disable_exchange": "Borsayı devre dışı bırak", - "advanced_privacy_settings": "Gelişmiş Gizlilik Ayarları", + "advanced_settings": "Gelişmiş Ayarlar", "settings_can_be_changed_later": "Bu ayarlar daha sonra uygulama ayarlarından da değiştirilebilir", "add_custom_node": "Yeni Özel Düğüm Ekleme", "disable_fiat": "İtibari paraları devre dışı bırak", @@ -729,11 +729,26 @@ "require_for_exchanges_to_external_wallets": "Harici cüzdanlara geçiş yapılmasını zorunlu kılın", "camera_permission_is_required": "Kamera izni gereklidir.\nLütfen uygulama ayarlarından etkinleştirin.", "switchToETHWallet": "Lütfen bir Ethereum cüzdanına geçin ve tekrar deneyin", + "importNFTs": "NFT'leri içe aktar", + "noNFTYet": "Henüz NFT yok", + "address": "Adres", + "enterTokenID": "Belirteç kimliğini girin", + "tokenID": "İD", + "name": "İsim", + "symbol": "Sembol", "seed_phrase_length": "Çekirdek cümle uzunluğu", "unavailable_balance": "Kullanılamayan bakiye", "unavailable_balance_description": "Kullanılamayan Bakiye: Bu toplam, bekleyen işlemlerde kilitlenen fonları ve jeton kontrol ayarlarınızda aktif olarak dondurduğunuz fonları içerir. Kilitli bakiyeler, ilgili işlemleri tamamlandıktan sonra kullanılabilir hale gelir; dondurulmuş bakiyeler ise siz onları dondurmaya karar verene kadar işlemler için erişilemez durumda kalır.", "unspent_change": "Değiştirmek", "Block_remaining": "${status} blok kalan blok", "labeled_silent_addresses": "Etiketli sessiz adresler", - "use_testnet": "TestNet kullanın" -} \ No newline at end of file + "use_testnet": "TestNet kullanın", + "tor_connection": "Tor bağlantısı", + "seed_hex_form": "Cüzdan tohumu (onaltılık form)", + "seedtype": "Tohum", + "seedtype_legacy": "Miras (25 kelime)", + "seedtype_polyseed": "Polyseed (16 kelime)", + "seed_language_czech": "Çek", + "seed_language_korean": "Koreli", + "seed_language_chinese_traditional": "Çin geleneği)" +} diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 3728cc692..26aed010f 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -160,7 +160,7 @@ "seed_title": "Мнемонічна фраза", "seed_share": "Поділитися мнемонічною фразою", "copy": "Скопіювати", - "seed_language_choose": "Будь ласка, виберіть мову мнемонічної фрази:", + "seed_language": "Насіннєва мова", "seed_choose": "Виберіть мову мнемонічної фрази", "seed_language_next": "Продовжити", "seed_language_english": "Англійська", @@ -383,7 +383,7 @@ "change_backup_password_alert": "Ваші попередні файли резервних копій будуть недоступні для імпорту з новим паролем резервної копії. Новий пароль резервної копії буде використовуватися тільки для нових файлів резервних копій. Ви впевнені, що хочете змінити пароль резервної копії?", "enter_backup_password": "Введіть пароль резервної копії", "select_backup_file": "Виберіть файл резервної копії", - "import": "Імпортувати", + "import": "Імпорт", "please_select_backup_file": "Виберіть файл резервної копії та введіть пароль резервної копії.", "fixed_rate": "Фіксована ставка", "fixed_rate_alert": "Ви зможете ввести суму отримання тоді, коли буде встановлений режим фіксованої ставки. Ви хочете перейти в режим фіксованої ставки?", @@ -571,7 +571,7 @@ "always": "Завжди", "minutes_to_pin_code": "${minute} хвилин", "disable_exchange": "Вимкнути exchange", - "advanced_privacy_settings": "Розширені налаштування конфіденційності", + "advanced_settings": "Розширені налаштування", "settings_can_be_changed_later": "Ці параметри можна змінити пізніше в налаштуваннях програми", "add_custom_node": "Додати новий спеціальний вузол", "disable_fiat": "Вимкнути фиат", @@ -731,11 +731,26 @@ "require_for_exchanges_to_external_wallets": "Потрібен для обміну на зовнішні гаманці", "camera_permission_is_required": "Потрібен дозвіл камери.\nУвімкніть його в налаштуваннях програми.", "switchToETHWallet": "Перейдіть на гаманець Ethereum і повторіть спробу", + "importNFTs": "Імпорт NFT", + "noNFTYet": "NFT ще немає", + "address": "Адреса", + "enterTokenID": "Введіть ідентифікатор токена", + "tokenID": "ID", + "name": "Ім'я", + "symbol": "символ", "seed_phrase_length": "Довжина початкової фрази", "unavailable_balance": "Недоступний баланс", "unavailable_balance_description": "Недоступний баланс: ця сума включає кошти, заблоковані в незавершених транзакціях, і ті, які ви активно заморозили в налаштуваннях контролю монет. Заблоковані баланси стануть доступними після завершення відповідних транзакцій, тоді як заморожені баланси залишаються недоступними для транзакцій, доки ви не вирішите їх розморозити.", "unspent_change": "Зміна", "Block_remaining": "${status} блок залишився", "labeled_silent_addresses": "Позначені мовчазними адресами", - "use_testnet": "Використовуйте тестову мережу" -} \ No newline at end of file + "use_testnet": "Використовуйте тестову мережу", + "tor_connection": "Підключення Tor", + "seed_hex_form": "Насіння гаманця (шістнадцяткова форма)", + "seedtype": "Насіннєвий тип", + "seedtype_legacy": "Спадщина (25 слів)", + "seedtype_polyseed": "Полісей (16 слів)", + "seed_language_czech": "Чеський", + "seed_language_korean": "Корейський", + "seed_language_chinese_traditional": "Китайський (традиційний)" +} diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 84a2d3413..d43f65fce 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -160,7 +160,7 @@ "seed_title": "بیج", "seed_share": "بیج بانٹیں۔", "copy": "کاپی", - "seed_language_choose": "براہ کرم بیج کی زبان کا انتخاب کریں:", + "seed_language": "بیج کی زبان", "seed_choose": "بیج کی زبان کا انتخاب کریں۔", "seed_language_next": "اگلے", "seed_language_english": "انگریزی", @@ -384,7 +384,7 @@ "change_backup_password_alert": "آپ کی پچھلی بیک اپ فائلیں نئے بیک اپ پاس ورڈ کے ساتھ درآمد کرنے کے لیے دستیاب نہیں ہوں گی۔ نیا بیک اپ پاس ورڈ صرف نئی بیک اپ فائلوں کے لیے استعمال کیا جائے گا۔ کیا آپ واقعی بیک اپ پاس ورڈ تبدیل کرنا چاہتے ہیں؟", "enter_backup_password": "یہاں بیک اپ پاس ورڈ درج کریں۔", "select_backup_file": "بیک اپ فائل کو منتخب کریں۔", - "import": "درآمد کریں۔", + "import": " ۔ﮟﯾﺮﮐ ﺪﻣﺁﺭﺩ", "please_select_backup_file": "براہ کرم بیک اپ فائل منتخب کریں اور بیک اپ پاس ورڈ درج کریں۔", "fixed_rate": "مقررہ شرح", "fixed_rate_alert": "فکسڈ ریٹ موڈ چیک ہونے پر آپ وصولی رقم درج کر سکیں گے۔ کیا آپ فکسڈ ریٹ موڈ پر سوئچ کرنا چاہتے ہیں؟", @@ -570,7 +570,7 @@ "always": "ہمیشہ", "minutes_to_pin_code": "${minute} منٹ", "disable_exchange": "تبادلے کو غیر فعال کریں۔", - "advanced_privacy_settings": "اعلی درجے کی رازداری کی ترتیبات", + "advanced_settings": "اعلی درجے کی ترتیبات", "settings_can_be_changed_later": "ان ترتیبات کو بعد میں ایپ کی ترتیبات میں تبدیل کیا جا سکتا ہے۔", "add_custom_node": "نیا کسٹم نوڈ شامل کریں۔", "disable_fiat": "فیاٹ کو غیر فعال کریں۔", @@ -723,11 +723,26 @@ "require_for_exchanges_to_external_wallets": "۔ﮯﮨ ﺕﺭﻭﺮﺿ ﯽﮐ ﮯﻟﺩﺎﺒﺗ ﮟﯿﻣ ﮮﻮﭩﺑ ﯽﻧﻭﺮﯿﺑ", "camera_permission_is_required": "۔ﮯﮨ ﺭﺎﮐﺭﺩ ﺕﺯﺎﺟﺍ ﯽﮐ ﮮﺮﻤﯿﮐ", "switchToETHWallet": "۔ﮟﯾﺮﮐ ﺶﺷﻮﮐ ﮦﺭﺎﺑﻭﺩ ﺭﻭﺍ ﮟﯾﺮﮐ ﭻﺋﻮﺳ ﺮﭘ ﭧﯿﻟﺍﻭ Ethereum ﻡﺮﮐ ﮦﺍﺮﺑ", - "seed_phrase_length": " ﯽﺋﺎﺒﻤﻟ ﯽﮐ ﮯﻠﻤﺟ ﮯﮐ ﺞﯿﺑ", - "unavailable_balance": " ﺲﻨﻠﯿﺑ ﺏﺎﯿﺘﺳﺩ ﺮﯿﻏ", + "importNFTs": "NFTs ۔ﮟﯾﺮﮐ ﺪﻣﺁﺭﺩ", + "noNFTYet": "۔ﮟﯿﮨ ﮟﯿﮩﻧ NFTs ﯽﺋﻮﮐ ﮏﺗ ﯽﮭﺑﺍ", + "address": "ﮧﺘﭘ", + "enterTokenID": " ۔ﮟﯾﺮﮐ ﺝﺭﺩ ID ﻦﮐﻮﭨ", + "tokenID": "ID", + "name": "ﻡﺎﻧ", + "symbol": "ﺖﻣﻼﻋ", + "seed_phrase_length": "ﯽﺋﺎﺒﻤﻟ ﯽﮐ ﮯﻠﻤﺟ ﮯﮐ ﺞﯿﺑ", + "unavailable_balance": "ﺲﻨﻠﯿﺑ ﺏﺎﯿﺘﺳﺩ ﺮﯿﻏ", "unavailable_balance_description": "۔ﮯﺗﺮﮐ ﮟﯿﮩﻧ ﮧﻠﺼﯿﻓ ﺎﮐ ﮯﻧﺮﮐ ﺪﻤﺠﻨﻣ ﻥﺍ ﮟﯿﮩﻧﺍ ﭖﺁ ﮧﮐ ﮏﺗ ﺐﺟ ﮟﯿﮨ ﮯﺘﮨﺭ ﯽﺋﺎﺳﺭ ﻞﺑﺎﻗﺎﻧ ﮏﺗ ﺖﻗﻭ ﺱﺍ ﮯﯿﻟ ﮯﮐ ﻦﯾﺩ ﻦﯿﻟ ﺲﻨﻠﯿﺑ ﺪﻤﺠﻨﻣ ﮧﮐ ﺐﺟ ،ﮯﮔ ﮟﯿﺋﺎﺟ ﻮﮨ ﺏﺎﯿﺘﺳﺩ ﺲﻨﻠﯿﺑ ﻞﻔﻘﻣ ﺪﻌﺑ ﮯﮐ ﮯﻧﻮﮨ ﻞﻤﮑﻣ ﻦﯾﺩ ﻦﯿﻟ ﮧﻘﻠﻌﺘﻣ ﮯﮐ ﻥﺍ ۔ﮯﮨ ﺎﮭﮐﺭ ﺮ", "unspent_change": "تبدیل کریں", "Block_remaining": "${status} باقی بلاک", "labeled_silent_addresses": "خاموش پتے لیبل لگا", "use_testnet": "ٹیسٹ نیٹ استعمال کریں" + "seed_hex_form": "پرس بیج (ہیکس فارم)", + "tor_connection": "ﻦﺸﮑﻨﮐ ﺭﻮﭨ", + "seedtype": "سیڈ ٹائپ", + "seedtype_legacy": "میراث (25 الفاظ)", + "seedtype_polyseed": "پالیسیڈ (16 الفاظ)", + "seed_language_czech": "چیک", + "seed_language_korean": "کورین", + "seed_language_chinese_traditional": "چینی (روایتی)" } diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 230ea0658..b16626d92 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -160,7 +160,7 @@ "seed_title": "Hóró", "seed_share": "Pín hóró", "copy": "Ṣẹ̀dà", - "seed_language_choose": "Ẹ jọ̀wọ́ yan èdè hóró:", + "seed_language": "Ewu ọmọ", "seed_choose": "Yan èdè hóró", "seed_language_next": "Tẹ̀síwájú", "seed_language_english": "Èdè Gẹ̀ẹ́sì", @@ -381,7 +381,7 @@ "change_backup_password_alert": "Ẹ kò lè fi ọ̀rọ̀ aṣínà títun ti ẹ̀dà nípamọ́ ṣí àwọn àkọsílẹ̀ nípamọ́ tẹ́lẹ̀tẹ́lẹ̀ yín. Ẹ máa fi ọ̀rọ̀ aṣínà ti ẹ̀dà nípamọ́ títun ṣí àwọn àkọsílẹ̀ nípamọ́ títun nìkan. Ṣé ó dá ẹ lójú pé ẹ fẹ́ pààrọ̀ aṣínà ti ẹ̀dà nípamọ́?", "enter_backup_password": "Tẹ̀ ọ̀rọ̀ aṣínà ti ẹ̀dà ḿbí", "select_backup_file": "Select backup file", - "import": "Gba wọlé", + "import": "gbe wọle", "please_select_backup_file": "Ẹ jọ̀wọ́ yan àkọsílẹ̀ nípamọ́ àti tẹ̀ ọ̀rọ̀ aṣínà ti ẹ̀dà.", "fixed_rate": "Iye t'á ṣẹ́ owó sí ò ní pààrọ̀", "fixed_rate_alert": "Ẹ lè tẹ̀ iye owó tó ń bọ̀ tí iye t'a ṣẹ́ owó sí bá is checked. Ṣé ẹ fẹ́ sún ipò ti iye t'á ṣẹ́ owó sí ò ní pààrọ̀ mọ́?", @@ -567,7 +567,7 @@ "always": "Ní gbogbo àwọn ìgbà", "minutes_to_pin_code": "${minute} ìṣẹ́jú", "disable_exchange": "Pa ilé pàṣípààrọ̀", - "advanced_privacy_settings": "Àwọn ààtò àdáni títóbi", + "advanced_settings": "Awọn eto ilọsiwaju", "settings_can_be_changed_later": "Ẹ lè pààrọ̀ àwọn ààtò yìí nínú ààtò áàpù t’ó bá yá", "add_custom_node": "Fikún apẹka títun t'ẹ́ pààrọ̀", "disable_fiat": "Pa owó tí ìjọba pàṣẹ wa lò", @@ -725,11 +725,26 @@ "require_for_exchanges_to_external_wallets": "Beere fun awọn paṣipaarọ si awọn apamọwọ ita", "camera_permission_is_required": "A nilo igbanilaaye kamẹra.\nJọwọ jeki o lati app eto.", "switchToETHWallet": "Jọwọ yipada si apamọwọ Ethereum ki o tun gbiyanju lẹẹkansi", + "importNFTs": "Gbe awọn NFT wọle", + "noNFTYet": "Ko si awọn NFT sibẹsibẹ", + "address": "Adirẹsi", + "enterTokenID": "Tẹ ID ami sii", + "tokenID": "ID", + "name": "Oruko", + "symbol": "Aami", "seed_phrase_length": "Gigun gbolohun irugbin", "unavailable_balance": "Iwontunwonsi ti ko si", "unavailable_balance_description": "Iwontunws.funfun ti ko si: Lapapọ yii pẹlu awọn owo ti o wa ni titiipa ni awọn iṣowo isunmọ ati awọn ti o ti didi ni itara ninu awọn eto iṣakoso owo rẹ. Awọn iwọntunwọnsi titiipa yoo wa ni kete ti awọn iṣowo oniwun wọn ba ti pari, lakoko ti awọn iwọntunwọnsi tio tutunini ko ni iraye si fun awọn iṣowo titi iwọ o fi pinnu lati mu wọn kuro.", "unspent_change": "Yipada", "Block_remaining": "${status} Bdund díẹ", "labeled_silent_addresses": "Awọn adirẹsi ipalọlọ", - "use_testnet": "Lo tele" -} \ No newline at end of file + "use_testnet": "Lo tele", + "tor_connection": "Tor asopọ", + "seed_hex_form": "Irú Opamọwọ apamọwọ (HOX)", + "seedtype": "Irugbin-seetypu", + "seedtype_legacy": "Legacy (awọn ọrọ 25)", + "seedtype_polyseed": "Polyseed (awọn ọrọ 16)", + "seed_language_czech": "Czech", + "seed_language_korean": "Ara ẹni", + "seed_language_chinese_traditional": "Kannada (ibile)" +} diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 3cfa8fd47..aabf0a57b 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -160,7 +160,7 @@ "seed_title": "种子", "seed_share": "分享种子", "copy": "复制", - "seed_language_choose": "请选择种子语言:", + "seed_language": "种子语言", "seed_choose": "选择种子语言", "seed_language_next": "下一个", "seed_language_english": "英文", @@ -382,7 +382,7 @@ "change_backup_password_alert": "您以前的备份文件将无法使用新的备份密码導入。 新的备份密码将仅用于新的备份文件。 您确定要更改备份密码吗?", "enter_backup_password": "在此处输入備用密码", "select_backup_file": "选择备份文件", - "import": "导入", + "import": "进口", "please_select_backup_file": "请选择备份文件,然后输入备份密码。", "fixed_rate": "固定汇率", "fixed_rate_alert": "选中固定汇率模式后,您将可以输入接收金额。 您要切换到固定汇率模式吗?", @@ -570,7 +570,7 @@ "always": "总是", "minutes_to_pin_code": "${minute} 分钟", "disable_exchange": "禁用交换", - "advanced_privacy_settings": "高级隐私设置", + "advanced_settings": "高级设置", "settings_can_be_changed_later": "稍后可以在应用设置中更改这些设置", "add_custom_node": "添加新的自定义节点", "disable_fiat": "禁用法令", @@ -730,11 +730,26 @@ "require_for_exchanges_to_external_wallets": "需要兑换到外部钱包", "camera_permission_is_required": "需要相机许可。\n请从应用程序设置中启用它。", "switchToETHWallet": "请切换到以太坊钱包并重试", + "importNFTs": "导入 NFT", + "noNFTYet": "还没有 NFT", + "address": "地址", + "enterTokenID": "输入令牌 ID", + "tokenID": "ID", + "name": "姓名", + "symbol": "象征", "seed_phrase_length": "种子短语长度", "unavailable_balance": "不可用余额", "unavailable_balance_description": "不可用余额:此总额包括锁定在待处理交易中的资金以及您在硬币控制设置中主动冻结的资金。一旦各自的交易完成,锁定的余额将变得可用,而冻结的余额在您决定解冻之前仍然无法进行交易。", "unspent_change": "改变", "Block_remaining": "${status}块剩余", "labeled_silent_addresses": "标记为无声地址", - "use_testnet": "使用TestNet" -} \ No newline at end of file + "use_testnet": "使用TestNet", + "tor_connection": "Tor连接", + "seed_hex_form": "钱包种子(十六进制形式)", + "seedtype": "籽粒", + "seedtype_legacy": "遗产(25个单词)", + "seedtype_polyseed": "多种物品(16个单词)", + "seed_language_czech": "捷克", + "seed_language_korean": "韩国人", + "seed_language_chinese_traditional": "中国传统的)" +} diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index ba7575d3a..d241c4421 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -15,15 +15,15 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.7.3" -MONERO_COM_BUILD_NUMBER=64 +MONERO_COM_VERSION="1.8.0" +MONERO_COM_BUILD_NUMBER=68 MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_PACKAGE="com.monero.app" MONERO_COM_SCHEME="monero.com" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.10.3" -CAKEWALLET_BUILD_NUMBER=178 +CAKEWALLET_VERSION="4.11.0" +CAKEWALLET_BUILD_NUMBER=182 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" CAKEWALLET_SCHEME="cakewallet" diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index 328b3825b..874501cb5 100644 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_IOS_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.7.4" -MONERO_COM_BUILD_NUMBER=63 +MONERO_COM_VERSION="1.8.0" +MONERO_COM_BUILD_NUMBER=66 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.10.4" -CAKEWALLET_BUILD_NUMBER=196 +CAKEWALLET_VERSION="4.11.0" +CAKEWALLET_BUILD_NUMBER=200 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" HAVEN_NAME="Haven" diff --git a/scripts/macos/app_env.sh b/scripts/macos/app_env.sh index 57434db8e..f69670c9a 100755 --- a/scripts/macos/app_env.sh +++ b/scripts/macos/app_env.sh @@ -15,8 +15,8 @@ if [ -n "$1" ]; then fi CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="1.3.3" -CAKEWALLET_BUILD_NUMBER=40 +CAKEWALLET_VERSION="1.4.0" +CAKEWALLET_BUILD_NUMBER=43 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then diff --git a/tool/configure.dart b/tool/configure.dart index dcc1fecd3..bf9a255c5 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -138,7 +138,6 @@ import 'package:cw_core/unspent_transaction_output.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_monero/monero_unspent.dart'; import 'package:mobx/mobx.dart'; -import 'package:flutter/foundation.dart'; import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/transaction_priority.dart'; @@ -148,7 +147,8 @@ 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:hive/hive.dart'; +import 'package:polyseed/polyseed.dart';"""; const moneroCWHeaders = """ import 'package:cw_core/get_height_by_date.dart'; import 'package:cw_core/monero_amount_format.dart'; @@ -156,7 +156,6 @@ import 'package:cw_core/monero_transaction_priority.dart'; import 'package:cw_monero/monero_wallet_service.dart'; import 'package:cw_monero/monero_wallet.dart'; import 'package:cw_monero/monero_transaction_info.dart'; -import 'package:cw_monero/monero_transaction_history.dart'; import 'package:cw_monero/monero_transaction_creation_credentials.dart'; import 'package:cw_core/account.dart' as monero_account; import 'package:cw_monero/api/wallet.dart' as monero_wallet_api; @@ -259,7 +258,7 @@ abstract class Monero { required String language, required int height}); WalletCredentials createMoneroRestoreWalletFromSeedCredentials({required String name, required String password, required int height, required String mnemonic}); - WalletCredentials createMoneroNewWalletCredentials({required String name, required String language, String password,}); + WalletCredentials createMoneroNewWalletCredentials({required String name, required String language, required bool isPolyseed, String password}); Map getKeys(Object wallet); Object createMoneroTransactionCreationCredentials({required List outputs, required TransactionPriority priority}); Object createMoneroTransactionCreationCredentialsRaw({required List outputs, required TransactionPriority priority}); diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index a8c6a6166..163b80135 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -36,6 +36,7 @@ class SecretKey { SecretKey('robinhoodApplicationId', () => ''), SecretKey('robinhoodCIdApiSecret', () => ''), SecretKey('walletConnectProjectId', () => ''), + SecretKey('moralisApiKey', () => '') ]; static final ethereumSecrets = [