From 01d40c4dc38705ef9df336541b1ef416e79562f5 Mon Sep 17 00:00:00 2001 From: tuxsudo Date: Fri, 19 Jul 2024 02:37:20 +0000 Subject: [PATCH 1/9] Guides update (#1542) * Rename build-guide-win.md to howto-build-windows.md * Update howto-build-windows.md * Update howto-build-windows.md * Update howto-build-windows.md * Update howto-build-windows.md * Update howto-build-android.md * Update howto-build-android.md * Update howto-build-macos.md * Update howto-build-macos.md * Update howto-build-ios.md * Update howto-build-android.md * Update howto-build-windows.md --- build-guide-win.md | 38 ---------------------------- howto-build-android.md | 40 ++++++++++++++--------------- howto-build-ios.md | 28 ++++++++++----------- howto-build-macos.md | 28 ++++++++++----------- howto-build-windows.md | 57 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 105 insertions(+), 86 deletions(-) delete mode 100644 build-guide-win.md create mode 100644 howto-build-windows.md diff --git a/build-guide-win.md b/build-guide-win.md deleted file mode 100644 index 6ace961af..000000000 --- a/build-guide-win.md +++ /dev/null @@ -1,38 +0,0 @@ -# Building CakeWallet for Windows - -## Requirements and Setup - -The following are the system requirements to build CakeWallet for your Windows PC. - -``` -Windows 10 or later (64-bit), x86-64 based -Flutter 3 or above -``` - -## Building CakeWallet on Windows - -These steps will help you configure and execute a build of CakeWallet from its source code. - -### 1. Installing Package Dependencies - -For build CakeWallet windows application from sources you will be needed to have: -> [Install Flutter]Follow installation guide (https://docs.flutter.dev/get-started/install/windows) and install do not miss to dev tools (install https://docs.flutter.dev/get-started/install/windows/desktop#development-tools) which are required for windows desktop development (need to install Git for Windows and Visual Studio 2022). Then install `Desktop development with C++` packages via GUI Visual Studio 2022, or Visual Studio Build Tools 2022 including: `C++ Build Tools core features`, `C++ 2022 Redistributable Update`, `C++ core desktop features`, `MVC v143 - VS 2022 C++ x64/x86 build tools`, `C++ CMake tools for Windwos`, `Testing tools core features - Build Tools`, `C++ AddressSanitizer`. -> [Install WSL] for building monero dependencies need to install Windows WSL (https://learn.microsoft.com/en-us/windows/wsl/install) and required packages for WSL (Ubuntu): -`$ sudo apt update ` -`$ sudo apt build-essential cmake gcc-mingw-w64 g++-mingw-w64 autoconf libtool pkg-config` - -### 2. Pull CakeWallet source code - -You can downlaod CakeWallet source code from our [GitHub repository](github.com/cake-tech/cake_wallet) via git by following next command: -`$ git clone https://github.com/cake-tech/cake_wallet.git --branch MrCyjaneK-cyjan-monerodart` -OR you can download it as [Zip archive](https://github.com/cake-tech/cake_wallet/archive/refs/heads/MrCyjaneK-cyjan-monerodart.zip) - -### 3. Build Monero, Monero_c and their dependencies - -For use monero in the application need to build Monero wrapper - Monero_C which will be used by monero.dart package. For that need to run shell (bash - typically same named utility should be available after WSL is enabled in your system) with previously installed WSL, then change current directory to the application project directory with your used shell and then change current directory to `scripts/windows`: `$ cd scripts/windows`. Run build script: `$ ./build_all.sh`. - -### 4. Configure and build CakeWallet application - -To configure the application open directory where you have downloaded or unarchived CakeWallet sources and run `cakewallet.bat`. -Or if you used WSL and have active shell session you can run `$ ./cakewallet.sh` script in `scripts/windows` which will run `cakewallet.bat` in WSL. -After execution of `cakewallet.bat` you should to get `Cake Wallet.zip` in project root directory which will contains `CakeWallet.exe` file and another needed files for run the application. Now you can extract files from `Cake Wallet.zip` archive and run the application. diff --git a/howto-build-android.md b/howto-build-android.md index 4ad88ea0d..57d29f459 100644 --- a/howto-build-android.md +++ b/howto-build-android.md @@ -1,23 +1,19 @@ -# Building CakeWallet for Android +# Building Cake Wallet for Android ## Requirements and Setup -The following are the system requirements to build CakeWallet for your Android device. +The following are the system requirements to build Cake Wallet for your Android device. ``` Ubuntu >= 20.04 Android SDK 29 or higher (better to have the latest one 33) Android NDK 17c -Flutter 3.19.x or earlier +Flutter 3.19.x ``` -## Building CakeWallet on Android - -These steps will help you configure and execute a build of CakeWallet from its source code. - ### 1. Installing Package Dependencies -CakeWallet cannot be built without the following packages installed on your build system. +CakeWallet cannot be built without the following packages installed on your system. - curl @@ -51,13 +47,17 @@ You may easily install them on your build system with the following command: ### 2. Installing Android Studio and Android toolchain -You may download and install the latest version of Android Studio [here](https://developer.android.com/studio#downloads). After installing, start Android Studio, and go through the "Setup Wizard." This installs the latest Android SDK, Android SDK Command-line Tools, and Android SDK Build-Tools, which are required by CakeWallet. **Be sure you are installing SDK version 28 or later when stepping through the wizard** +You may download and install the latest version of Android Studio [here](https://developer.android.com/studio#downloads). After installing, start Android Studio, and go through the "Setup Wizard." This installs the latest Android SDK, Android SDK Command-line Tools, and Android SDK Build-Tools, which are required by Cake Wallet. **Be sure you are installing SDK version 28 or later when stepping through the wizard** ### 3. Installing Flutter -Need to install flutter with version `3.19.x`. For this please check section [Install Flutter manually](https://docs.flutter.dev/get-started/install/linux#install-flutter-manually). +Install Flutter with version `3.19.x`. For this please check section [Install Flutter manually](https://docs.flutter.dev/get-started/install/linux#install-flutter-manually). -### 4. Verify Installations +### 4. Installing rustup + +Install rustup from the [rustup.rs](https://rustup.rs/) website. + +### 5. Verify Installations Verify that the Android toolchain, Flutter, and Android Studio have been correctly installed on your system with the following command: @@ -71,15 +71,15 @@ Doctor summary (to see all details, run flutter doctor -v): [✓] Android Studio (version 4.0 or higher) ``` -### 5. Generate a secure keystore for Android +### 6. Generate a secure keystore for Android `$ keytool -genkey -v -keystore $HOME/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key` You will be prompted to create two passwords. First you will be prompted for the "store password", followed by a "key password" towards the end of the creation process. **TAKE NOTE OF THESE PASSWORDS!** You will need them in later steps. -### 6. Acquiring the CakeWallet Source Code +### 7. Acquiring the Cake Wallet Source Code -Create the directory that will be use to store the CakeWallet source... +Create the directory that will be use to store the Cake Wallet source... ``` $ sudo mkdir -p /opt/android @@ -95,11 +95,11 @@ Proceed into the source code before proceeding with the next steps: `$ cd cake_wallet/scripts/android/` -### 7. Installing Android NDK +### 8. Installing Android NDK `$ ./install_ndk.sh` -### 8. Execute Build & Setup Commands for CakeWallet +### 9. Execute Build & Setup Commands for Cak eWallet We need to generate project settings like app name, app icon, package name, etc. For this need to setup environment variables and configure project files. @@ -116,7 +116,7 @@ Build the Monero libraries and their dependencies: `$ ./build_all.sh` -It is now time to change back to the base directory of the CakeWallet source code: +It is now time to change back to the base directory of the Cake Wallet source code: `$ cd ../../` @@ -124,7 +124,7 @@ Install Flutter package dependencies with this command: `$ flutter pub get` -Your CakeWallet binary will be built with cryptographic salts, which are used for secure encryption of your data. You may generate these secret salts with the following command: +Your Cake Wallet binary will be built with cryptographic salts, which are used for secure encryption of your data. You may generate these secret salts with the following command: `$ flutter packages pub run tool/generate_new_secrets.dart` @@ -142,8 +142,8 @@ Finally build mobx models for the app: `$ ./model_generator.sh` -### 9. Build! +### 10. Build! `$ flutter build apk --release` -Copyright (c) 2022 Cake Technologies LLC. +Copyright (c) 2024 Cake Labs LLC diff --git a/howto-build-ios.md b/howto-build-ios.md index 3bb345861..418fbc96b 100644 --- a/howto-build-ios.md +++ b/howto-build-ios.md @@ -1,8 +1,8 @@ -# Building CakeWallet for iOS +# Building Cake Wallet for iOS ## Requirements and Setup -The following are the system requirements to build CakeWallet for your iOS device. +The following are the system requirements to build Cake Wallet for your iOS device. ``` macOS >= 14.0 @@ -10,13 +10,9 @@ Xcode 15.3 Flutter 3.19.x ``` -## Building CakeWallet on iOS - -These steps will help you configure and execute a build of CakeWallet from its source code. - ### 1. Installing Package Dependencies -CakeWallet cannot be built without the following packages installed on your build system. +Cake Wallet cannot be built without the following packages installed on your build system. For installing dependency tools you can use brew [Install brew](https://brew.sh). @@ -32,7 +28,11 @@ You may download and install the latest version of [Xcode](https://developer.app Need to install flutter with version `3.19.x`. For this please check section [Install Flutter](https://docs.flutter.dev/get-started/install/macos/mobile-ios?tab=download). -### 4. Verify Installations +### 4. Installing rustup + +Install rustup from the [rustup.rs](https://rustup.rs/) website. + +### 5. Verify Installations Verify that the Flutter and Xcode have been correctly installed on your system with the following command: @@ -45,7 +45,7 @@ Doctor summary (to see all details, run flutter doctor -v): [✓] Xcode - develop for iOS and macOS (Xcode 15.3) ``` -### 5. Acquiring the CakeWallet source code +### 6. Acquiring the CakeWallet source code Download the source code. @@ -55,7 +55,7 @@ Proceed into the source code before proceeding with the next steps: `$ cd cake_wallet/scripts/ios/` -### 6. Execute Build & Setup Commands for CakeWallet +### 7. Execute Build & Setup Commands for Cake Wallet We need to generate project settings like app name, app icon, package name, etc. For this need to setup environment variables and configure project files. @@ -72,7 +72,7 @@ Build the Monero libraries and their dependencies: `$ ./build_monero_all.sh` -It is now time to change back to the base directory of the CakeWallet source code: +It is now time to change back to the base directory of the Cake Wallet source code: `$ cd ../../` @@ -80,7 +80,7 @@ Install Flutter package dependencies with this command: `$ flutter pub get` -Your CakeWallet binary will be built with cryptographic salts, which are used for secure encryption of your data. You may generate these secret salts with the following command: +Your Cake Wallet binary will be built with cryptographic salts, which are used for secure encryption of your data. You may generate these secret salts with the following command: `$ flutter packages pub run tool/generate_new_secrets.dart` @@ -88,7 +88,7 @@ Then we need to generate localization files and mobx models. `$ ./configure_cake_wallet.sh ios` -### 7. Build! +### 8. Build! `$ flutter build ios --release` @@ -98,4 +98,4 @@ Or if you want to run to connected device: `$ flutter run --release` -Copyright (c) 2024 Cake Technologies LLC. +Copyright (c) 2024 Cake Labs LLC diff --git a/howto-build-macos.md b/howto-build-macos.md index 24d3a9d85..2e535e5be 100644 --- a/howto-build-macos.md +++ b/howto-build-macos.md @@ -1,8 +1,8 @@ -# Building CakeWallet for macOS +# Building Cake Wallet for macOS ## Requirements and Setup -The following are the system requirements to build CakeWallet for your macOS device. +The following are the system requirements to build Cake Wallet for your macOS device. ``` macOS >= 14.0 @@ -10,13 +10,9 @@ Xcode 15.3 Flutter 3.19.x ``` -## Building CakeWallet on macOS - -These steps will help you configure and execute a build of CakeWallet from its source code. - ### 1. Installing Package Dependencies -CakeWallet cannot be built without the following packages installed on your build system. +Cake Wallet cannot be built without the following packages installed on your build system. For installing dependency tools you can use brew [Install brew](https://brew.sh). @@ -34,7 +30,11 @@ You may download and install the latest version of [Xcode](https://developer.app Need to install flutter with version `3.19.x`. For this please check section [Install Flutter](https://docs.flutter.dev/get-started/install/macos/desktop?tab=download). -### 4. Verify Installations +### 4. Installing rustup + +Install rustup from the [rustup.rs](https://rustup.rs/) website. + +### 5. Verify Installations Verify that Flutter and Xcode have been correctly installed on your system with the following command: @@ -47,7 +47,7 @@ Doctor summary (to see all details, run flutter doctor -v): [✓] Xcode - develop for iOS and macOS (Xcode 15.3) ``` -### 5. Acquiring the CakeWallet source code +### 6. Acquiring the Cake Wallet source code Download the source code. @@ -57,7 +57,7 @@ Proceed into the source code before proceeding with the next steps: `$ cd cake_wallet/scripts/macos/` -### 6. Execute Build & Setup Commands for CakeWallet +### 7. Execute Build & Setup Commands for Cake Wallet We need to generate project settings like app name, app icon, package name, etc. For this need to setup environment variables and configure project files. @@ -83,7 +83,7 @@ If you be needed to build universal monero lib, then it will require additional If you will be needed to build monero wallet lib only for x86_64 on arm64 mac, then you need use steps above, but run build script with rosetta without arguments: `$ arch -x86_64 ./build_monero_all.sh`. -It is now time to change back to the base directory of the CakeWallet source code: +It is now time to change back to the base directory of the Cake Wallet source code: `$ cd ../../` @@ -91,7 +91,7 @@ Install Flutter package dependencies with this command: `$ flutter pub get` -Your CakeWallet binary will be built with cryptographic salts, which are used for secure encryption of your data. You may generate these secret salts with the following command: +Your Cake Wallet binary will be built with cryptographic salts, which are used for secure encryption of your data. You may generate these secret salts with the following command: `$ flutter packages pub run tool/generate_new_secrets.dart` @@ -99,7 +99,7 @@ Then we need to generate localization files and mobx models. `$ ./configure_cake_wallet.sh macos` -### 7. Build! +### 8. Build! `$ flutter build macos --release` @@ -109,4 +109,4 @@ Or if you want to run to connected device: `$ flutter run --release` -Copyright (c) 2024 Cake Technologies LLC. +Copyright (c) 2024 Cake Labs LLC diff --git a/howto-build-windows.md b/howto-build-windows.md new file mode 100644 index 000000000..796cb3cc8 --- /dev/null +++ b/howto-build-windows.md @@ -0,0 +1,57 @@ +# Building Cake Wallet for Windows + +## Requirements and Setup + +The following are the system requirements to build CakeWallet for your Windows PC. + +``` +Windows 10 or later (64-bit), x86-64 based +Flutter 3.19.x +``` + +### 1. Installing Flutter + +Install Flutter with version `3.19.x`. Follow the Flutter [installation guide](https://docs.flutter.dev/get-started/install/windows). + +### 2. Install Development Tools + +Install Git for Windows and Visual Studio 2022. Follow the [Development Tools](https://docs.flutter.dev/get-started/install/windows/desktop#development-tools) installation instructions. + +Then install `Desktop development with C++` packages via Visual Studio 2022, or Visual Studio Build Tools 2022 including: +- `C++ Build Tools core features` +- `C++ 2022 Redistributable Update` +- `C++ core desktop features` +- `MVC v143 - VS 2022 C++ x64/x86 build tools` +- `C++ CMake tools for Windwos` +- `Testing tools core features - Build Tools` +- `C++ AddressSanitizer`. + +### 3. Installing rustup + +Install rustup from the [rustup.rs](https://rustup.rs/#) website. Download and run the 64-bit rustup-init.exe + +### 4. Installing WSL (Windows Subsystem for Linux) + +For building monero dependencies, it is required to install Windows WSL (https://learn.microsoft.com/en-us/windows/wsl/install) and required packages for WSL (Ubuntu): +`$ sudo apt update ` +`$ sudo apt build-essential cmake gcc-mingw-w64 g++-mingw-w64 autoconf libtool pkg-config` + +### 5. Pull Cake Wallet source code + +You can downlaod CakeWallet source code from our [GitHub repository](github.com/cake-tech/cake_wallet) via git: +`$ git clone https://github.com/cake-tech/cake_wallet.git --branch MrCyjaneK-cyjan-monerodart` +OR you can download it as [Zip archive](https://github.com/cake-tech/cake_wallet/archive/refs/heads/MrCyjaneK-cyjan-monerodart.zip) + +### 6. Build Monero, monero_c and their dependencies + +To use Monero in Cake Wallet, you must build the Monero_C wrapper which will be used by monero.dart package. + +For that you need to run the shell (bash - typically same named utility should be available after WSL is enabled in your system) with the previously installed WSL install, then change current directory to the application project directory with your shell then change current directory to `scripts/windows`: `$ cd scripts/windows`. Run build script: `$ ./build_all.sh`. + +### 7. Configure and build Cake Wallet application + +To configure the application, open the directory where you have downloaded or unarchived Cake Wallet sources and run `cakewallet.bat`. +Or if you used WSL and have active shell session you can run `$ ./cakewallet.sh` script in `scripts/windows` which will run `cakewallet.bat` in WSL. +After execution of `cakewallet.bat` you should to get `Cake Wallet.zip` in project root directory which will contains `CakeWallet.exe` file and another needed files for run the application. Now you can extract files from `Cake Wallet.zip` archive and run the application. + +Copyright (c) 2024 Cake Labs LLC. From d6c5b84188ddc7d3961e882452213de668f04a30 Mon Sep 17 00:00:00 2001 From: Konstantin Ullrich Date: Fri, 19 Jul 2024 19:31:11 +0200 Subject: [PATCH 2/9] Fix Bitcoin not sending on Ledger (#1539) --- cw_bitcoin/lib/electrum_wallet.dart | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 6cc82780f..39cf95009 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -519,17 +519,20 @@ abstract class ElectrumWalletBase ); spendsSilentPayment = true; isSilentPayment = true; - } else { + } else if (!isHardwareWallet) { privkey = generateECPrivate(hd: hd, index: utx.bitcoinAddressRecord.index, network: network); } vinOutpoints.add(Outpoint(txid: utx.hash, index: utx.vout)); - inputPrivKeyInfos.add(ECPrivateInfo( - privkey, - address.type == SegwitAddresType.p2tr, - tweak: !isSilentPayment, - )); + + if (privkey != null) { + inputPrivKeyInfos.add(ECPrivateInfo( + privkey, + address.type == SegwitAddresType.p2tr, + tweak: !isSilentPayment, + )); + } utxos.add( UtxoWithAddress( @@ -541,7 +544,7 @@ abstract class ElectrumWalletBase isSilentPayment: isSilentPayment, ), ownerDetails: UtxoAddressDetails( - publicKey: privkey.getPublic().toHex(), + publicKey: pubKeyHex, address: address, ), ), From c0cd68a823d4ccd8d416529e6f146854a446ebf6 Mon Sep 17 00:00:00 2001 From: cyan Date: Fri, 19 Jul 2024 21:26:15 +0200 Subject: [PATCH 3/9] update monero_c to fix unreachable wownero git hosting (#1534) * update monero_c commit * fix: no element in getAllUnusedSubAddresses * fix: Wallet created with empty seed and 0 as private key The error that was there is caused when wallet is being created, but it errors out, so better handling of errors should be all that's needed, as it is not an error on it's own, but rather lack of handling. * fix: create transaction multi dest function is missing * update monero_c hash * fix: receiving on 2 different addresses shows as 1 --- cw_core/lib/transaction_info.dart | 1 + cw_monero/lib/api/wallet_manager.dart | 38 +++++++++---------- cw_monero/lib/monero_subaddress_list.dart | 2 +- cw_monero/lib/monero_transaction_info.dart | 14 +++++-- cw_wownero/lib/api/wallet_manager.dart | 38 +++++++++---------- cw_wownero/lib/wownero_transaction_info.dart | 12 ++++-- .../transaction_details_view_model.dart | 4 +- scripts/prepare_moneroc.sh | 2 +- 8 files changed, 57 insertions(+), 54 deletions(-) diff --git a/cw_core/lib/transaction_info.dart b/cw_core/lib/transaction_info.dart index 7b02bf1ff..a270c0479 100644 --- a/cw_core/lib/transaction_info.dart +++ b/cw_core/lib/transaction_info.dart @@ -3,6 +3,7 @@ import 'package:cw_core/keyable.dart'; abstract class TransactionInfo extends Object with Keyable { late String id; + late String txhash = id; late int amount; int? fee; late TransactionDirection direction; diff --git a/cw_monero/lib/api/wallet_manager.dart b/cw_monero/lib/api/wallet_manager.dart index 8a1eee3f7..f77ef05e2 100644 --- a/cw_monero/lib/api/wallet_manager.dart +++ b/cw_monero/lib/api/wallet_manager.dart @@ -194,28 +194,24 @@ void loadWallet( wptr = openedWalletsByPath[path]!; return; } - try { - if (wptr == null || path != _lastOpenedWallet) { - if (wptr != null) { - final addr = wptr!.address; - Isolate.run(() { - monero.Wallet_store(Pointer.fromAddress(addr)); - }); - } - txhistory = null; - wptr = monero.WalletManager_openWallet(wmPtr, - path: path, password: password); - openedWalletsByPath[path] = wptr!; - _lastOpenedWallet = path; + if (wptr == null || path != _lastOpenedWallet) { + if (wptr != null) { + final addr = wptr!.address; + Isolate.run(() { + monero.Wallet_store(Pointer.fromAddress(addr)); + }); } - } catch (e) { - print(e); - } - final status = monero.Wallet_status(wptr!); - if (status != 0) { - final err = monero.Wallet_errorString(wptr!); - print(err); - throw WalletOpeningException(message: err); + txhistory = null; + wptr = monero.WalletManager_openWallet(wmPtr, + path: path, password: password); + _lastOpenedWallet = path; + final status = monero.Wallet_status(wptr!); + if (status != 0) { + final err = monero.Wallet_errorString(wptr!); + print(err); + throw WalletOpeningException(message: err); + } + openedWalletsByPath[path] = wptr!; } } diff --git a/cw_monero/lib/monero_subaddress_list.dart b/cw_monero/lib/monero_subaddress_list.dart index 676a9536c..623d744fd 100644 --- a/cw_monero/lib/monero_subaddress_list.dart +++ b/cw_monero/lib/monero_subaddress_list.dart @@ -124,7 +124,7 @@ abstract class MoneroSubaddressListBase with Store { Future> _getAllUnusedAddresses( {required int accountIndex, required String label}) async { final allAddresses = subaddress_list.getAllSubaddresses(); - final lastAddress = allAddresses.last.address; + final lastAddress = allAddresses.length == 0 ? allAddresses.last.address : Subaddress(id: -1, address: "", label: ""); if (allAddresses.isEmpty || _usedAddresses.contains(lastAddress)) { final isAddressUnused = await _newSubaddress(accountIndex: accountIndex, label: label); if (!isAddressUnused) { diff --git a/cw_monero/lib/monero_transaction_info.dart b/cw_monero/lib/monero_transaction_info.dart index 748b65329..b1a4e4229 100644 --- a/cw_monero/lib/monero_transaction_info.dart +++ b/cw_monero/lib/monero_transaction_info.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/monero_amount_format.dart'; import 'package:cw_monero/api/structs/transaction_info_row.dart'; @@ -7,12 +9,14 @@ import 'package:cw_core/format_amount.dart'; import 'package:cw_monero/api/transaction_history.dart'; class MoneroTransactionInfo extends TransactionInfo { - MoneroTransactionInfo(this.id, this.height, this.direction, this.date, + MoneroTransactionInfo(this.txhash, this.height, this.direction, this.date, this.isPending, this.amount, this.accountIndex, this.addressIndex, this.fee, - this.confirmations); + this.confirmations) : + id = "${txhash}_${amount}_${accountIndex}_${addressIndex}"; MoneroTransactionInfo.fromMap(Map map) - : id = (map['hash'] ?? '') as String, + : id = "${map['hash']}_${map['amount']}_${map['accountIndex']}_${map['addressIndex']}", + txhash = map['hash'] as String, height = (map['height'] ?? 0) as int, direction = map['direction'] != null ? parseTransactionDirectionFromNumber(map['direction'] as String) @@ -34,7 +38,8 @@ class MoneroTransactionInfo extends TransactionInfo { } MoneroTransactionInfo.fromRow(TransactionInfoRow row) - : id = row.getHash(), + : id = "${row.getHash()}_${row.getAmount()}_${row.subaddrAccount}_${row.subaddrIndex}", + txhash = row.getHash(), height = row.blockHeight, direction = parseTransactionDirectionFromInt(row.direction), date = DateTime.fromMillisecondsSinceEpoch(row.getDatetime() * 1000), @@ -53,6 +58,7 @@ class MoneroTransactionInfo extends TransactionInfo { } final String id; + final String txhash; final int height; final TransactionDirection direction; final DateTime date; diff --git a/cw_wownero/lib/api/wallet_manager.dart b/cw_wownero/lib/api/wallet_manager.dart index 2f92bb080..e90843538 100644 --- a/cw_wownero/lib/api/wallet_manager.dart +++ b/cw_wownero/lib/api/wallet_manager.dart @@ -208,28 +208,24 @@ void loadWallet( wptr = openedWalletsByPath[path]!; return; } - try { - if (wptr == null || path != _lastOpenedWallet) { - if (wptr != null) { - final addr = wptr!.address; - Isolate.run(() { - wownero.Wallet_store(Pointer.fromAddress(addr)); - }); - } - txhistory = null; - wptr = wownero.WalletManager_openWallet(wmPtr, - path: path, password: password); - openedWalletsByPath[path] = wptr!; - _lastOpenedWallet = path; + if (wptr == null || path != _lastOpenedWallet) { + if (wptr != null) { + final addr = wptr!.address; + Isolate.run(() { + wownero.Wallet_store(Pointer.fromAddress(addr)); + }); } - } catch (e) { - print(e); - } - final status = wownero.Wallet_status(wptr!); - if (status != 0) { - final err = wownero.Wallet_errorString(wptr!); - print(err); - throw WalletOpeningException(message: err); + txhistory = null; + wptr = wownero.WalletManager_openWallet(wmPtr, + path: path, password: password); + _lastOpenedWallet = path; + final status = wownero.Wallet_status(wptr!); + if (status != 0) { + final err = wownero.Wallet_errorString(wptr!); + print(err); + throw WalletOpeningException(message: err); + } + openedWalletsByPath[path] = wptr!; } } diff --git a/cw_wownero/lib/wownero_transaction_info.dart b/cw_wownero/lib/wownero_transaction_info.dart index 2060f1f95..6016e2e59 100644 --- a/cw_wownero/lib/wownero_transaction_info.dart +++ b/cw_wownero/lib/wownero_transaction_info.dart @@ -7,12 +7,14 @@ import 'package:cw_core/format_amount.dart'; import 'package:cw_wownero/api/transaction_history.dart'; class WowneroTransactionInfo extends TransactionInfo { - WowneroTransactionInfo(this.id, this.height, this.direction, this.date, + WowneroTransactionInfo(this.txhash, this.height, this.direction, this.date, this.isPending, this.amount, this.accountIndex, this.addressIndex, this.fee, - this.confirmations); + this.confirmations) : + id = "${txhash}_${amount}_${accountIndex}_${addressIndex}"; WowneroTransactionInfo.fromMap(Map map) - : id = (map['hash'] ?? '') as String, + : id = "${map['hash']}_${map['amount']}_${map['accountIndex']}_${map['addressIndex']}", + txhash = map['hash'] as String, height = (map['height'] ?? 0) as int, direction = map['direction'] != null ? parseTransactionDirectionFromNumber(map['direction'] as String) @@ -34,7 +36,8 @@ class WowneroTransactionInfo extends TransactionInfo { } WowneroTransactionInfo.fromRow(TransactionInfoRow row) - : id = row.getHash(), + : id = "${row.getHash()}_${row.getAmount()}_${row.subaddrAccount}_${row.subaddrIndex}", + txhash = row.getHash(), height = row.blockHeight, direction = parseTransactionDirectionFromInt(row.direction), date = DateTime.fromMillisecondsSinceEpoch(row.getDatetime() * 1000), @@ -53,6 +56,7 @@ class WowneroTransactionInfo extends TransactionInfo { } final String id; + final String txhash; final int height; final TransactionDirection direction; final DateTime date; diff --git a/lib/view_model/transaction_details_view_model.dart b/lib/view_model/transaction_details_view_model.dart index acf334382..025ccab9b 100644 --- a/lib/view_model/transaction_details_view_model.dart +++ b/lib/view_model/transaction_details_view_model.dart @@ -214,7 +214,7 @@ abstract class TransactionDetailsViewModelBase with Store { final addressIndex = tx.additionalInfo['addressIndex'] as int; final feeFormatted = tx.feeFormatted(); final _items = [ - StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id), + StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txhash), StandartListItem( title: S.current.transaction_details_date, value: dateFormat.format(tx.date)), StandartListItem(title: S.current.transaction_details_height, value: '${tx.height}'), @@ -455,7 +455,7 @@ abstract class TransactionDetailsViewModelBase with Store { final addressIndex = tx.additionalInfo['addressIndex'] as int; final feeFormatted = tx.feeFormatted(); final _items = [ - StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id), + StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txhash), StandartListItem( title: S.current.transaction_details_date, value: dateFormat.format(tx.date)), StandartListItem(title: S.current.transaction_details_height, value: '${tx.height}'), diff --git a/scripts/prepare_moneroc.sh b/scripts/prepare_moneroc.sh index 27d839ef4..e91b3d4a4 100755 --- a/scripts/prepare_moneroc.sh +++ b/scripts/prepare_moneroc.sh @@ -8,7 +8,7 @@ if [[ ! -d "monero_c" ]]; then git clone https://github.com/mrcyjanek/monero_c --branch rewrite-wip cd monero_c - git checkout eaa7bdb8be3479418445ddb18bf33d453f64afcf + git checkout d1e246aaf4c53b60ff9e4ab4a4ac3ae4a1f94a33 git reset --hard git submodule update --init --force --recursive ./apply_patches.sh monero From 44101016721abe3f2404a5a8c90ad434ae54e24d Mon Sep 17 00:00:00 2001 From: Serhii Date: Fri, 19 Jul 2024 23:55:01 +0300 Subject: [PATCH 4/9] add popup for cake pay transaction sent alert (#1533) * add popup for cake pay transaction sent alert * Revert "add popup for cake pay transaction sent alert" This reverts commit 1df1bf0f00a20765fc5c5e0eb3eaa893b53c6286. * fix localisation --- .../cake_pay_confirm_purchase_card_page.dart | 29 +++++++++++++++---- res/values/strings_ar.arb | 1 + res/values/strings_bg.arb | 1 + res/values/strings_cs.arb | 1 + res/values/strings_de.arb | 3 +- res/values/strings_en.arb | 1 + res/values/strings_es.arb | 1 + res/values/strings_fr.arb | 3 +- res/values/strings_ha.arb | 1 + res/values/strings_hi.arb | 1 + res/values/strings_hr.arb | 1 + res/values/strings_id.arb | 1 + res/values/strings_it.arb | 1 + res/values/strings_ja.arb | 1 + res/values/strings_ko.arb | 1 + res/values/strings_my.arb | 1 + res/values/strings_nl.arb | 1 + res/values/strings_pl.arb | 1 + res/values/strings_pt.arb | 1 + res/values/strings_ru.arb | 1 + res/values/strings_th.arb | 1 + res/values/strings_tl.arb | 1 + res/values/strings_tr.arb | 1 + res/values/strings_uk.arb | 1 + res/values/strings_ur.arb | 1 + res/values/strings_yo.arb | 1 + res/values/strings_zh.arb | 1 + 27 files changed, 52 insertions(+), 7 deletions(-) diff --git a/lib/src/screens/cake_pay/cards/cake_pay_confirm_purchase_card_page.dart b/lib/src/screens/cake_pay/cards/cake_pay_confirm_purchase_card_page.dart index 15aa576c8..fd8dce103 100644 --- a/lib/src/screens/cake_pay/cards/cake_pay_confirm_purchase_card_page.dart +++ b/lib/src/screens/cake_pay/cards/cake_pay_confirm_purchase_card_page.dart @@ -9,18 +9,18 @@ import 'package:cake_wallet/src/screens/cake_pay/widgets/link_extractor.dart'; import 'package:cake_wallet/src/screens/cake_pay/widgets/text_icon_button.dart'; import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/src/widgets/alert_with_two_actions.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/themes/extensions/cake_text_theme.dart'; -import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart'; import 'package:cake_wallet/themes/extensions/picker_theme.dart'; import 'package:cake_wallet/themes/extensions/receive_page_theme.dart'; -import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; import 'package:cake_wallet/typography.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/cake_pay/cake_pay_purchase_view_model.dart'; import 'package:cake_wallet/view_model/send/send_view_model_state.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:mobx/mobx.dart'; @@ -372,9 +372,7 @@ class CakePayBuyCardDetailPage extends BasePage { if (state is TransactionCommitted) { WidgetsBinding.instance.addPostFrameCallback((_) { cakePayPurchaseViewModel.sendViewModel.clearOutputs(); - if (context.mounted) { - showStateAlert(context, S.of(context).sending, S.of(context).transaction_sent); - } + if (context.mounted) showSentAlert(context); }); } }); @@ -394,6 +392,27 @@ class CakePayBuyCardDetailPage extends BasePage { }); } + Future showSentAlert(BuildContext context) async { + final order = cakePayPurchaseViewModel.order!.orderId; + final isCopy = await showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithTwoActions( + alertTitle: S.of(context).transaction_sent, + alertContent: + S.of(context).cake_pay_save_order + '\n${order}', + leftButtonText: S.of(context).ignor, + rightButtonText: S.of(context).copy, + actionLeftButton: () => Navigator.of(context).pop(false), + actionRightButton: () => Navigator.of(context).pop(true)); + }) ?? + false; + + if (isCopy) { + await Clipboard.setData(ClipboardData(text: order)); + } + } + void _handleDispose(ReactionDisposer? disposer) { cakePayPurchaseViewModel.dispose(); if (disposer != null) { diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index db6a4cae2..59fe26ef8 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "موضوع الكعكة الظلام", "cake_pay_account_note": "قم بالتسجيل باستخدام عنوان بريد إلكتروني فقط لمشاهدة البطاقات وشرائها. حتى أن بعضها متوفر بسعر مخفض!", "cake_pay_learn_more": "شراء واسترداد بطاقات الهدايا على الفور في التطبيق!\nاسحب من اليسار إلى اليمين لمعرفة المزيد.", + "cake_pay_save_order": "يجب إرسال البطاقة إلى بريدك الإلكتروني خلال يوم عمل واحد \n حفظ معرف الطلب الخاص بك:", "cake_pay_subtitle": "شراء بطاقات مسبقة الدفع وبطاقات الهدايا في جميع أنحاء العالم", "cake_pay_web_cards_subtitle": "اشتري بطاقات مدفوعة مسبقا وبطاقات هدايا في جميع أنحاء العالم", "cake_pay_web_cards_title": "بطاقات Cake Pay Web", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 06132c244..689fc06cf 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "Торта тъмна тема", "cake_pay_account_note": "Регистрайте се само с един имейл, за да виждате и купувате карти. За някои има дори и отстъпка!", "cake_pay_learn_more": "Купете и използвайте гифткарти директно в приложението!\nПлъзнете отляво надясно, за да научите още.", + "cake_pay_save_order": "Картата трябва да бъде изпратена до вашия имейл в рамките на 1 работен ден \n Запазете вашия идентификационен номер на поръчката:", "cake_pay_subtitle": "Купете предплатени карти и карти за подаръци в световен мащаб", "cake_pay_web_cards_subtitle": "Купете световно признати предплатени и гифт карти", "cake_pay_web_cards_title": "Cake Pay Онлайн Карти", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 589f89fd7..433351a5b 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "Dort tmavé téma", "cake_pay_account_note": "Přihlaste se svou e-mailovou adresou pro zobrazení a nákup karet. Některé jsou dostupné ve slevě!", "cake_pay_learn_more": "Okamžitý nákup a uplatnění dárkových karet v aplikaci!\nPřejeďte prstem zleva doprava pro další informace.", + "cake_pay_save_order": "Karta by měla být odeslána do vašeho e-mailu do 1 pracovního dne \n Uložit ID objednávky:", "cake_pay_subtitle": "Kupte si celosvětové předplacené karty a dárkové karty", "cake_pay_web_cards_subtitle": "Kupte si celosvětové předplacené a dárkové karty", "cake_pay_web_cards_title": "Cake Pay webové karty", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index a4b816f5b..624147cc2 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "Cake Dark Thema", "cake_pay_account_note": "Melden Sie sich nur mit einer E-Mail-Adresse an, um Karten anzuzeigen und zu kaufen. Einige sind sogar mit Rabatt erhältlich!", "cake_pay_learn_more": "Kaufen und lösen Sie Geschenkkarten sofort in der App ein!\nWischen Sie von links nach rechts, um mehr zu erfahren.", + "cake_pay_save_order": "Die Karte sollte innerhalb von 1 Werktag an Ihre E-Mail gesendet werden, \n Ihre Bestell-ID zu speichern:", "cake_pay_subtitle": "Kaufen Sie weltweite Prepaid -Karten und Geschenkkarten", "cake_pay_web_cards_subtitle": "Kaufen Sie weltweit Prepaid-Karten und Geschenkkarten", "cake_pay_web_cards_title": "Cake Pay-Webkarten", @@ -876,4 +877,4 @@ "you_will_get": "Konvertieren zu", "you_will_send": "Konvertieren von", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index d39b175dd..4c7dcbebe 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "Cake Dark Theme", "cake_pay_account_note": "Sign up with just an email address to see and purchase cards. Some are even available at a discount!", "cake_pay_learn_more": "Instantly purchase and redeem gift cards in the app!\nSwipe left to right to learn more.", + "cake_pay_save_order": "The card should be sent to your e-mail within 1 business day \n Save your Order ID:", "cake_pay_subtitle": "Buy worldwide prepaid cards and gift cards", "cake_pay_web_cards_subtitle": "Buy worldwide prepaid cards and gift cards", "cake_pay_web_cards_title": "Cake Pay Web Cards", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 5fba46830..5cf99d3c3 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "Tema oscuro del pastel", "cake_pay_account_note": "Regístrese con solo una dirección de correo electrónico para ver y comprar tarjetas. ¡Algunas incluso están disponibles con descuento!", "cake_pay_learn_more": "¡Compre y canjee tarjetas de regalo al instante en la aplicación!\nDeslice el dedo de izquierda a derecha para obtener más información.", + "cake_pay_save_order": "La tarjeta debe enviarse a su correo electrónico dentro de 1 día hábil \n Guardar su ID de pedido:", "cake_pay_subtitle": "Compre tarjetas prepagas y tarjetas de regalo en todo el mundo", "cake_pay_web_cards_subtitle": "Compre tarjetas de prepago y tarjetas de regalo en todo el mundo", "cake_pay_web_cards_title": "Tarjetas Web Cake Pay", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 7289b7511..244238975 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "Thème sombre du gâteau", "cake_pay_account_note": "Inscrivez-vous avec juste une adresse e-mail pour voir et acheter des cartes. Certaines sont même disponibles à prix réduit !", "cake_pay_learn_more": "Achetez et utilisez instantanément des cartes-cadeaux dans l'application !\nBalayer de gauche à droite pour en savoir plus.", + "cake_pay_save_order": "La carte doit être envoyée à votre e-mail dans un jour ouvrable \n Enregistrez votre identifiant de commande:", "cake_pay_subtitle": "Achetez des cartes et des cartes-cadeaux prépayées mondiales", "cake_pay_web_cards_subtitle": "Achetez des cartes prépayées et des cartes-cadeaux dans le monde entier", "cake_pay_web_cards_title": "Cartes Web Cake Pay", @@ -873,4 +874,4 @@ "you_will_get": "Convertir vers", "you_will_send": "Convertir depuis", "yy": "AA" -} +} \ No newline at end of file diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index c96568a76..292ce0614 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "Cake Dark Jigo", "cake_pay_account_note": "Yi rajista tare da adireshin imel kawai don gani da siyan katunan. Wasu ma suna samuwa a rangwame!", "cake_pay_learn_more": "Nan take siya ku kwaso katunan kyaututtuka a cikin app!\nTake hagu zuwa dama don ƙarin koyo.", + "cake_pay_save_order": "Ya kamata a aika katin zuwa e-mail ɗinku a cikin rana 1 na kasuwanci \n Ajiye id ku:", "cake_pay_subtitle": "Sayi katunan shirye-shiryen duniya da katunan kyauta", "cake_pay_web_cards_subtitle": "Sayi katunan da aka riga aka biya na duniya da katunan kyauta", "cake_pay_web_cards_title": "Cake Pay Web Cards", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index ef740b4d6..3cfb5129a 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "केक डार्क थीम", "cake_pay_account_note": "कार्ड देखने और खरीदने के लिए केवल एक ईमेल पते के साथ साइन अप करें। कुछ छूट पर भी उपलब्ध हैं!", "cake_pay_learn_more": "ऐप में उपहार कार्ड तुरंत खरीदें और रिडीम करें!\nअधिक जानने के लिए बाएं से दाएं स्वाइप करें।", + "cake_pay_save_order": "कार्ड को आपके ई-मेल को 1 व्यावसायिक दिन के भीतर भेजा जाना चाहिए \n आपकी ऑर्डर आईडी सहेजें:", "cake_pay_subtitle": "दुनिया भर में प्रीपेड कार्ड और उपहार कार्ड खरीदें", "cake_pay_web_cards_subtitle": "दुनिया भर में प्रीपेड कार्ड और गिफ्ट कार्ड खरीदें", "cake_pay_web_cards_title": "केक भुगतान वेब कार्ड", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 295a01165..5f8271504 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "TOKA DARKA TEMA", "cake_pay_account_note": "Prijavite se samo s adresom e-pošte da biste vidjeli i kupili kartice. Neke su čak dostupne uz popust!", "cake_pay_learn_more": "Azonnal vásárolhat és válthat be ajándékutalványokat az alkalmazásban!\nTovábbi információért csúsztassa balról jobbra az ujját.", + "cake_pay_save_order": "Karticu treba poslati na vašu e-poštu u roku od 1 radnog dana \n Spremi ID narudžbe:", "cake_pay_subtitle": "Kupite svjetske unaprijed plaćene kartice i poklon kartice", "cake_pay_web_cards_subtitle": "Kupujte prepaid kartice i poklon kartice diljem svijeta", "cake_pay_web_cards_title": "Cake Pay Web kartice", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 8b28e928f..9c8d91b90 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "Tema Kue Gelap", "cake_pay_account_note": "Daftar hanya dengan alamat email untuk melihat dan membeli kartu. Beberapa di antaranya bahkan tersedia dengan diskon!", "cake_pay_learn_more": "Beli dan tukar kartu hadiah secara instan di aplikasi!\nGeser ke kanan untuk informasi lebih lanjut.", + "cake_pay_save_order": "Kartu harus dikirim ke email Anda dalam 1 hari kerja \n Simpan ID pesanan Anda:", "cake_pay_subtitle": "Beli kartu prabayar di seluruh dunia dan kartu hadiah", "cake_pay_web_cards_subtitle": "Beli kartu prabayar dan kartu hadiah secara global", "cake_pay_web_cards_title": "Kartu Web Cake Pay", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index b1bd7cd6d..db55845ef 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "Tema oscuro della torta", "cake_pay_account_note": "Iscriviti con solo un indirizzo email per vedere e acquistare le carte. Alcune sono anche disponibili con uno sconto!", "cake_pay_learn_more": "Acquista e riscatta istantaneamente carte regalo nell'app!\nScorri da sinistra a destra per saperne di più.", + "cake_pay_save_order": "La carta deve essere inviata alla tua e-mail entro 1 giorno lavorativo \n Salva il tuo ID ordine:", "cake_pay_subtitle": "Acquista carte prepagate in tutto il mondo e carte regalo", "cake_pay_web_cards_subtitle": "Acquista carte prepagate e carte regalo in tutto il mondo", "cake_pay_web_cards_title": "Carte Web Cake Pay", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 5803b86d5..d24f0224d 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "ケーキ暗いテーマ", "cake_pay_account_note": "メールアドレスだけでサインアップして、カードを表示して購入できます。割引価格で利用できるカードもあります!", "cake_pay_learn_more": "アプリですぐにギフトカードを購入して引き換えましょう!\n左から右にスワイプして詳細をご覧ください。", + "cake_pay_save_order": "カードは1営業日以内に電子メールに送信する必要があります\n注文IDを保存します。", "cake_pay_subtitle": "世界中のプリペイドカードとギフトカードを購入します", "cake_pay_web_cards_subtitle": "世界中のプリペイド カードとギフト カードを購入する", "cake_pay_web_cards_title": "Cake Pay ウェブカード", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 874ec2c27..262c40e8d 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "케이크 다크 테마", "cake_pay_account_note": "이메일 주소로 가입하면 카드를 보고 구매할 수 있습니다. 일부는 할인된 가격으로 사용 가능합니다!", "cake_pay_learn_more": "앱에서 즉시 기프트 카드를 구매하고 사용하세요!\n자세히 알아보려면 왼쪽에서 오른쪽으로 스와이프하세요.", + "cake_pay_save_order": "카드는 영업일 1 일 이내에 이메일로 전자 메일로 보내야합니다. \n 주문 ID 저장 :", "cake_pay_subtitle": "전세계 선불 카드와 기프트 카드를 구입하십시오", "cake_pay_web_cards_subtitle": "전 세계 선불 카드 및 기프트 카드 구매", "cake_pay_web_cards_title": "케이크페이 웹카드", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 890362fcd..4fd55684e 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "ကိတ်မုန့် Dark Theme", "cake_pay_account_note": "ကတ်များကြည့်ရှုဝယ်ယူရန် အီးမေးလ်လိပ်စာတစ်ခုဖြင့် စာရင်းသွင်းပါ။ အချို့ကို လျှော့ဈေးဖြင့်ပင် ရနိုင်သည်။", "cake_pay_learn_more": "အက်ပ်ရှိ လက်ဆောင်ကတ်များကို ချက်ချင်းဝယ်ယူပြီး ကူပွန်ဖြင့် လဲလှယ်ပါ။\nပိုမိုလေ့လာရန် ဘယ်မှညာသို့ ပွတ်ဆွဲပါ။", + "cake_pay_save_order": "ကဒ်ကိုသင်၏အီးမေးလ်သို့ပေးပို့သင့်သည်။ သင်၏အမှာစာ ID ကိုသိမ်းပါ။", "cake_pay_subtitle": "Worldwide ကြိုတင်ငွေဖြည့်ကဒ်များနှင့်လက်ဆောင်ကဒ်များကို 0 ယ်ပါ", "cake_pay_web_cards_subtitle": "ကမ္ဘာတစ်ဝှမ်း ကြိုတင်ငွေပေးကတ်များနှင့် လက်ဆောင်ကတ်များကို ဝယ်ယူပါ။", "cake_pay_web_cards_title": "Cake Pay ဝဘ်ကတ်များ", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 25b7f6b3a..70379c95c 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "Cake Dark Theme", "cake_pay_account_note": "Meld u aan met alleen een e-mailadres om kaarten te bekijken en te kopen. Sommige zijn zelfs met korting verkrijgbaar!", "cake_pay_learn_more": "Koop en wissel cadeaubonnen direct in de app in!\nSwipe van links naar rechts voor meer informatie.", + "cake_pay_save_order": "De kaart moet binnen 1 werkdag naar uw e-mail worden verzonden.", "cake_pay_subtitle": "Koop wereldwijde prepaid -kaarten en cadeaubonnen", "cake_pay_web_cards_subtitle": "Koop wereldwijd prepaidkaarten en cadeaubonnen", "cake_pay_web_cards_title": "Cake Pay-webkaarten", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 1567a0841..6d8f05fcf 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "Cake Dark Temat", "cake_pay_account_note": "Zarejestruj się, używając tylko adresu e-mail, aby przeglądać i kupować karty. Niektóre są nawet dostępne ze zniżką!", "cake_pay_learn_more": "Kupuj i wykorzystuj karty podarunkowe od razu w aplikacji!\nPrzesuń od lewej do prawej, aby dowiedzieć się więcej.", + "cake_pay_save_order": "Karta powinna zostać wysłana na adres e-mail w ciągu 1 dnia roboczego \n Zapisz identyfikator zamówienia:", "cake_pay_subtitle": "Kup na całym świecie karty przedpłacone i karty podarunkowe", "cake_pay_web_cards_subtitle": "Kupuj na całym świecie karty przedpłacone i karty podarunkowe", "cake_pay_web_cards_title": "Cake Pay Web Cards", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 272c0862e..381514341 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "Bolo tema escuro", "cake_pay_account_note": "Inscreva-se com apenas um endereço de e-mail para ver e comprar cartões. Alguns estão até com desconto!", "cake_pay_learn_more": "Compre e resgate vales-presente instantaneamente no app!\nDeslize da esquerda para a direita para saber mais.", + "cake_pay_save_order": "O cartão deve ser enviado ao seu e-mail dentro de 1 dia útil \n Salvar seu ID do pedido:", "cake_pay_subtitle": "Compre cartões pré -pagos em todo o mundo e cartões -presente", "cake_pay_web_cards_subtitle": "Compre cartões pré-pagos e cartões-presente em todo o mundo", "cake_pay_web_cards_title": "Cartões Cake Pay Web", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index a8943db97..8611b32f8 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "Тейт темная тема", "cake_pay_account_note": "Зарегистрируйтесь, указав только адрес электронной почты, чтобы просматривать и покупать карты. Некоторые даже доступны со скидкой!", "cake_pay_learn_more": "Мгновенно покупайте и используйте подарочные карты в приложении!\nПроведите по экрану слева направо, чтобы узнать больше.", + "cake_pay_save_order": "Карта должна быть отправлена ​​на ваше электронное письмо в течение 1 рабочего дня \n Сохраните свой идентификатор заказа:", "cake_pay_subtitle": "Купить карты с предоплатой и подарочными картами по всему миру", "cake_pay_web_cards_subtitle": "Покупайте карты предоплаты и подарочные карты по всему миру", "cake_pay_web_cards_title": "Веб-карты Cake Pay", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index c4d282761..b1a96414d 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "ธีมเค้กมืด", "cake_pay_account_note": "ลงทะเบียนด้วยอีเมลเพียงอย่างเดียวเพื่อดูและซื้อบัตร บางบัตรอาจมีส่วนลด!", "cake_pay_learn_more": "ซื้อและเบิกบัตรของขวัญในแอพพลิเคชันทันที!\nกระแทกขวาไปซ้ายเพื่อเรียนรู้เพิ่มเติม", + "cake_pay_save_order": "บัตรควรส่งไปยังอีเมลของคุณภายใน 1 วันทำการ \n บันทึกรหัสคำสั่งซื้อของคุณ:", "cake_pay_subtitle": "ซื้อบัตรเติมเงินและบัตรของขวัญทั่วโลก", "cake_pay_web_cards_subtitle": "ซื้อบัตรพร้อมเงินระดับโลกและบัตรของขวัญ", "cake_pay_web_cards_title": "Cake Pay Web Cards", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 775ab4c3d..5566e4b79 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "Cake madilim na tema", "cake_pay_account_note": "Mag -sign up na may isang email address lamang upang makita at bumili ng mga kard. Ang ilan ay magagamit kahit sa isang diskwento!", "cake_pay_learn_more": "Agad na bumili at tubusin ang mga kard ng regalo sa app!\nMag -swipe pakaliwa sa kanan upang matuto nang higit pa.", + "cake_pay_save_order": "Ang card ay dapat ipadala sa iyong e-mail sa loob ng 1 araw ng negosyo \n i-save ang iyong order ID:", "cake_pay_subtitle": "Bumili ng mga pandaigdigang prepaid card at gift card", "cake_pay_web_cards_subtitle": "Bumili ng mga pandaigdigang prepaid card at gift card", "cake_pay_web_cards_title": "Cake pay web card", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 239a1aa2e..8af93e5aa 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "Kek Koyu Tema", "cake_pay_account_note": "Kartları görmek ve satın almak için sadece bir e-posta adresiyle kaydolun. Hatta bazıları indirimli olarak bile mevcut!", "cake_pay_learn_more": "Uygulamada anında hediye kartları satın alın ve harcayın!\nDaha fazla öğrenmek için soldan sağa kaydır.", + "cake_pay_save_order": "Kart, 1 İş Günü içinde e-postanıza gönderilmelidir \n Sipariş Kimliğinizi Kaydet:", "cake_pay_subtitle": "Dünya çapında ön ödemeli kartlar ve hediye kartları satın alın", "cake_pay_web_cards_subtitle": "Dünya çapında ön ödemeli kartlar ve hediye kartları satın alın", "cake_pay_web_cards_title": "Cake Pay Web Kartları", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 3a45752d6..375b9c373 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "Темна тема торта", "cake_pay_account_note": "Зареєструйтеся, використовуючи лише адресу електронної пошти, щоб переглядати та купувати картки. Деякі навіть доступні зі знижкою!", "cake_pay_learn_more": "Миттєво купуйте та активуйте подарункові картки в додатку!\nПроведіть пальцем зліва направо, щоб дізнатися більше.", + "cake_pay_save_order": "Картка повинна бути надіслана на вашу електронну пошту протягом 1 робочого дня \n Зберегти ідентифікатор замовлення:", "cake_pay_subtitle": "Купіть у всьому світі передплачені картки та подарункові картки", "cake_pay_web_cards_subtitle": "Купуйте передоплачені та подарункові картки по всьому світу", "cake_pay_web_cards_title": "Веб-картки Cake Pay", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 2ab1f927b..4f86a15b2 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "کیک ڈارک تھیم", "cake_pay_account_note": "کارڈز دیکھنے اور خریدنے کے لیے صرف ایک ای میل ایڈریس کے ساتھ سائن اپ کریں۔ کچھ رعایت پر بھی دستیاب ہیں!", "cake_pay_learn_more": "ایپ میں فوری طور پر گفٹ کارڈز خریدیں اور بھنائیں!\\nمزید جاننے کے لیے بائیں سے دائیں سوائپ کریں۔", + "cake_pay_save_order": "کارڈ 1 کاروباری دن کے اندر آپ کے ای میل پر بھیجا جانا چاہئے \n اپنے آرڈر کی شناخت کو بچائیں:", "cake_pay_subtitle": "دنیا بھر میں پری پیڈ کارڈز اور گفٹ کارڈ خریدیں", "cake_pay_web_cards_subtitle": "دنیا بھر میں پری پیڈ کارڈز اور گفٹ کارڈز خریدیں۔", "cake_pay_web_cards_title": "Cake پے ویب کارڈز", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index f9751f9f1..1bdb1115c 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "Akara oyinbo dudu koko", "cake_pay_account_note": "Ẹ fi àdírẹ́sì ímeèlì nìkan forúkọ sílẹ̀ k'ẹ́ rí àti ra àwọn káàdì. Ẹ lè fi owó tó kéré jù ra àwọn káàdì kan!", "cake_pay_learn_more": "Láìpẹ́ ra àti lo àwọn káàdí ìrajà t'á lò nínú irú kan ìtajà nínú áàpù!\nẸ tẹ̀ òsì de ọ̀tún láti kọ́ jù.", + "cake_pay_save_order": "Kaadi yẹ ki o firanṣẹ si imeeli rẹ laarin ọjọ iṣowo 1 \n Fipamọ aṣẹ rẹ:", "cake_pay_subtitle": "Ra awọn kaadi ti a san ni agbaye ati awọn kaadi ẹbun", "cake_pay_web_cards_subtitle": "Ra àwọn káàdì ìrajà t'á lò nínú ìtajà kan àti àwọn káàdì náà t'á lè lò níbikíbi", "cake_pay_web_cards_title": "Àwọn káàdì wẹ́ẹ̀bù ti Cake Pay", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index f3b8ee176..b3800f597 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -95,6 +95,7 @@ "cake_dark_theme": "蛋糕黑暗主题", "cake_pay_account_note": "只需使用電子郵件地址註冊即可查看和購買卡片。有些甚至可以打折!", "cake_pay_learn_more": "立即在应用中购买和兑换礼品卡!\n从左向右滑动以了解详情。", + "cake_pay_save_order": "该卡应在1个工作日内发送到您的电子邮件\n保存您的订单ID:", "cake_pay_subtitle": "购买全球预付费卡和礼品卡", "cake_pay_web_cards_subtitle": "购买全球预付卡和礼品卡", "cake_pay_web_cards_title": "蛋糕支付网络卡", From 415d2a35736a13fce200517c3f0e603573cf41c0 Mon Sep 17 00:00:00 2001 From: Adegoke David <64401859+Blazebrain@users.noreply.github.com> Date: Sun, 21 Jul 2024 00:04:22 +0100 Subject: [PATCH 5/9] CW-672: Enhance ETH Transaction Fee Calculation (#1545) * fix: Eth transaction fees WIP * Revert "fix: Eth transaction fees WIP" This reverts commit b9a469bc7e22134d78bf0cc4c00485e1d4515ebd. * fix: Modifying fee WIP * fix: Enhance ETH Wallet fee calculation WIP * feat: Enhance Transaction fees for ETH Transactions, Native transactions done, left with ERC20 transactions * fix: Pre PR cleanups * minor things [skip ci] --------- Co-authored-by: OmarHatem --- cw_evm/lib/contract/erc20.dart | 4 +- cw_evm/lib/evm_chain_client.dart | 65 +++++++++++-- cw_evm/lib/evm_chain_wallet.dart | 162 ++++++++++++++++++++++++------- 3 files changed, 186 insertions(+), 45 deletions(-) diff --git a/cw_evm/lib/contract/erc20.dart b/cw_evm/lib/contract/erc20.dart index 297b77e71..76d45064f 100644 --- a/cw_evm/lib/contract/erc20.dart +++ b/cw_evm/lib/contract/erc20.dart @@ -2,7 +2,7 @@ import 'dart:typed_data'; import 'package:web3dart/web3dart.dart' as web3; -final _contractAbi = web3.ContractAbi.fromJson( +final ethereumContractAbi = web3.ContractAbi.fromJson( '[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]', 'Erc20'); @@ -13,7 +13,7 @@ class ERC20 extends web3.GeneratedContract { required web3.EthereumAddress address, required web3.Web3Client client, int? chainId, - }) : super(web3.DeployedContract(_contractAbi, address), client, chainId); + }) : super(web3.DeployedContract(ethereumContractAbi, address), client, chainId); /// Returns the remaining number of tokens that [spender] will be allowed to spend on behalf of [owner] through [transferFrom]. This is zero by default. This value changes when [approve] or [transferFrom] are called. /// diff --git a/cw_evm/lib/evm_chain_client.dart b/cw_evm/lib/evm_chain_client.dart index 2185936ea..033dc7143 100644 --- a/cw_evm/lib/evm_chain_client.dart +++ b/cw_evm/lib/evm_chain_client.dart @@ -10,7 +10,7 @@ import 'package:cw_evm/evm_chain_transaction_priority.dart'; import 'package:cw_evm/evm_erc20_balance.dart'; import 'package:cw_evm/pending_evm_chain_transaction.dart'; import 'package:cw_evm/.secrets.g.dart' as secrets; -import 'package:flutter/services.dart'; +import 'package:flutter/foundation.dart'; import 'package:hex/hex.dart' as hex; import 'package:http/http.dart'; import 'package:web3dart/web3dart.dart'; @@ -65,16 +65,65 @@ abstract class EVMChainClient { Future getGasUnitPrice() async { try { final gasPrice = await _client!.getGasPrice(); + return gasPrice.getInWei.toInt(); } catch (_) { return 0; } } - Future getEstimatedGas() async { + Future getGasBaseFee() async { try { - final estimatedGas = await _client!.estimateGas(); - return estimatedGas.toInt(); + final blockInfo = await _client!.getBlockInformation(isContainFullObj: false); + final baseFee = blockInfo.baseFeePerGas; + + return baseFee!.getInWei.toInt(); + } catch (_) { + return 0; + } + } + + Future getEstimatedGas({ + String? contractAddress, + required EthereumAddress toAddress, + required EthereumAddress senderAddress, + required EtherAmount value, + EtherAmount? gasPrice, + // EtherAmount? maxFeePerGas, + // EtherAmount? maxPriorityFeePerGas, + }) async { + try { + if (contractAddress == null) { + final estimatedGas = await _client!.estimateGas( + sender: senderAddress, + gasPrice: gasPrice, + to: toAddress, + value: value, + // maxPriorityFeePerGas: maxPriorityFeePerGas, + // maxFeePerGas: maxFeePerGas, + ); + + return estimatedGas.toInt(); + } else { + final contract = DeployedContract( + ethereumContractAbi, + EthereumAddress.fromHex(contractAddress), + ); + + final transferFunction = contract.function('transferFrom'); + + final estimatedGas = await _client!.estimateGas( + sender: senderAddress, + to: toAddress, + value: value, + data: transferFunction.encodeCall([ + senderAddress, + toAddress, + value.getInWei, + ]), + ); + return estimatedGas.toInt(); + } } catch (_) { return 0; } @@ -84,7 +133,7 @@ abstract class EVMChainClient { required Credentials privateKey, required String toAddress, required BigInt amount, - required int gas, + required BigInt gas, required EVMChainTransactionPriority priority, required CryptoCurrency currency, required int exponent, @@ -97,8 +146,6 @@ abstract class EVMChainClient { bool isNativeToken = currency == CryptoCurrency.eth || currency == CryptoCurrency.maticpoly; - final price = _client!.getGasPrice(); - final Transaction transaction = createTransaction( from: privateKey.address, to: EthereumAddress.fromHex(toAddress), @@ -130,11 +177,10 @@ abstract class EVMChainClient { _sendTransaction = () async => await sendTransaction(signedTransaction); - return PendingEVMChainTransaction( signedTransaction: signedTransaction, amount: amount.toString(), - fee: BigInt.from(gas) * (await price).getInWei, + fee: gas, sendTransaction: _sendTransaction, exponent: exponent, ); @@ -233,7 +279,6 @@ abstract class EVMChainClient { final decodedResponse = jsonDecode(response.body)[0] as Map; - final symbol = (decodedResponse['symbol'] ?? '') as String; String filteredSymbol = symbol.replaceFirst(RegExp('^\\\$'), ''); diff --git a/cw_evm/lib/evm_chain_wallet.dart b/cw_evm/lib/evm_chain_wallet.dart index 2adb54746..2ab1c17a0 100644 --- a/cw_evm/lib/evm_chain_wallet.dart +++ b/cw_evm/lib/evm_chain_wallet.dart @@ -27,6 +27,7 @@ import 'package:cw_evm/evm_chain_transaction_priority.dart'; import 'package:cw_evm/evm_chain_wallet_addresses.dart'; import 'package:cw_evm/evm_ledger_credentials.dart'; import 'package:cw_evm/file.dart'; +import 'package:flutter/foundation.dart'; import 'package:hex/hex.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; @@ -102,10 +103,12 @@ abstract class EVMChainWalletBase Credentials get evmChainPrivateKey => _evmChainPrivateKey; - late EVMChainClient _client; + late final EVMChainClient _client; + + int gasPrice = 0; + int? gasBaseFee = 0; + int estimatedGasUnits = 0; - int? _gasPrice; - int? _estimatedGas; bool _isTransactionUpdating; // TODO: remove after integrating our own node and having eth_newPendingTransactionFilter @@ -173,12 +176,70 @@ abstract class EVMChainWalletBase @override int calculateEstimatedFee(TransactionPriority priority, int? amount) { + { + try { + if (priority is EVMChainTransactionPriority) { + final priorityFee = EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt(); + + int maxFeePerGas; + if (gasBaseFee != null) { + // MaxFeePerGas with EIP1559; + maxFeePerGas = gasBaseFee! + priorityFee; + } else { + // MaxFeePerGas with gasPrice; + maxFeePerGas = gasPrice; + debugPrint('MaxFeePerGas with gasPrice: $maxFeePerGas'); + } + + final totalGasFee = estimatedGasUnits * maxFeePerGas; + return totalGasFee; + } + + return 0; + } catch (e) { + return 0; + } + } + } + + /// Allows more customization to the fetch estimatedFees flow. + /// + /// We are able to pass in: + /// - The exact amount the user wants to send, + /// - The addressHex for the receiving wallet, + /// - A contract address which would be essential in determining if to calcualate the estimate for ERC20 or native ETH + Future calculateActualEstimatedFeeForCreateTransaction({ + required amount, + required String? contractAddress, + required String receivingAddressHex, + required TransactionPriority priority, + }) async { try { if (priority is EVMChainTransactionPriority) { final priorityFee = EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt(); - return (_gasPrice! + priorityFee) * (_estimatedGas ?? 0); - } + int maxFeePerGas; + if (gasBaseFee != null) { + // MaxFeePerGas with EIP1559; + maxFeePerGas = gasBaseFee! + priorityFee; + } else { + // MaxFeePerGas with gasPrice + maxFeePerGas = gasPrice; + } + + final estimatedGas = await _client.getEstimatedGas( + contractAddress: contractAddress, + senderAddress: _evmChainPrivateKey.address, + value: EtherAmount.fromBigInt(EtherUnit.wei, amount!), + gasPrice: EtherAmount.fromInt(EtherUnit.wei, gasPrice), + toAddress: EthereumAddress.fromHex(receivingAddressHex), + // maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas), + // maxPriorityFeePerGas: EtherAmount.fromInt(EtherUnit.gwei, priority.tip), + ); + + final totalGasFee = estimatedGas * maxFeePerGas; + return totalGasFee; + } return 0; } catch (e) { return 0; @@ -225,13 +286,12 @@ abstract class EVMChainWalletBase syncStatus = AttemptingSyncStatus(); await _updateBalance(); await _updateTransactions(); - _gasPrice = await _client.getGasUnitPrice(); - _estimatedGas = await _client.getEstimatedGas(); - Timer.periodic( - const Duration(minutes: 1), (timer) async => _gasPrice = await _client.getGasUnitPrice()); - Timer.periodic(const Duration(seconds: 10), - (timer) async => _estimatedGas = await _client.getEstimatedGas()); + await _updateEstimatedGasFeeParams(); + + Timer.periodic(const Duration(seconds: 10), (timer) async { + await _updateEstimatedGasFeeParams(); + }); syncStatus = SyncedSyncStatus(); } catch (e) { @@ -239,6 +299,19 @@ abstract class EVMChainWalletBase } } + Future _updateEstimatedGasFeeParams() async { + gasBaseFee = await _client.getGasBaseFee(); + + gasPrice = await _client.getGasUnitPrice(); + + estimatedGasUnits = await _client.getEstimatedGas( + senderAddress: _evmChainPrivateKey.address, + toAddress: _evmChainPrivateKey.address, + gasPrice: EtherAmount.fromInt(EtherUnit.wei, gasPrice), + value: EtherAmount.fromBigInt(EtherUnit.wei, BigInt.one), + ); + } + @override Future createTransaction(Object credentials) async { final _credentials = credentials as EVMChainTransactionCredentials; @@ -258,8 +331,17 @@ abstract class EVMChainWalletBase final erc20Balance = balance[transactionCurrency]!; BigInt totalAmount = BigInt.zero; + BigInt estimatedFeesForTransaction = BigInt.zero; int exponent = transactionCurrency is Erc20Token ? transactionCurrency.decimal : 18; num amountToEVMChainMultiplier = pow(10, exponent); + String? contractAddress; + String toAddress = _credentials.outputs.first.isParsedAddress + ? _credentials.outputs.first.extractedAddress! + : _credentials.outputs.first.address; + + if (transactionCurrency is Erc20Token) { + contractAddress = transactionCurrency.contractAddress; + } // so far this can not be made with Ethereum as Ethereum does not support multiple recipients if (hasMultiDestination) { @@ -271,35 +353,50 @@ abstract class EVMChainWalletBase outputs.fold(0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0))); totalAmount = BigInt.from(totalOriginalAmount * amountToEVMChainMultiplier); + final estimateFees = await calculateActualEstimatedFeeForCreateTransaction( + amount: totalAmount, + receivingAddressHex: toAddress, + priority: _credentials.priority!, + contractAddress: contractAddress, + ); + + estimatedFeesForTransaction = BigInt.from(estimateFees); + if (erc20Balance.balance < totalAmount) { throw EVMChainTransactionCreationException(transactionCurrency); } } else { final output = outputs.first; - // since the fees are taken from Ethereum - // then no need to subtract the fees from the amount if send all - final BigInt allAmount; - if (transactionCurrency is Erc20Token) { - allAmount = erc20Balance.balance; - } else { - final estimatedFee = BigInt.from(calculateEstimatedFee(_credentials.priority!, null)); - - if (estimatedFee > erc20Balance.balance) { - throw EVMChainTransactionFeesException(); - } - - allAmount = erc20Balance.balance - estimatedFee; - } - - if (output.sendAll) { - totalAmount = allAmount; - } else { + if (!output.sendAll) { final totalOriginalAmount = EVMChainFormatter.parseEVMChainAmountToDouble(output.formattedCryptoAmount ?? 0); totalAmount = BigInt.from(totalOriginalAmount * amountToEVMChainMultiplier); } + if (output.sendAll && transactionCurrency is Erc20Token) { + totalAmount = erc20Balance.balance; + } + + final estimateFees = await calculateActualEstimatedFeeForCreateTransaction( + amount: totalAmount, + receivingAddressHex: toAddress, + priority: _credentials.priority!, + contractAddress: contractAddress, + ); + + estimatedFeesForTransaction = BigInt.from(estimateFees); + + debugPrint('Estimated Fees for Transaction: $estimatedFeesForTransaction'); + + if (output.sendAll && transactionCurrency is! Erc20Token) { + totalAmount = (erc20Balance.balance - estimatedFeesForTransaction); + + if (estimatedFeesForTransaction > erc20Balance.balance) { + throw EVMChainTransactionFeesException(); + } + } + if (erc20Balance.balance < totalAmount) { throw EVMChainTransactionCreationException(transactionCurrency); } @@ -312,11 +409,9 @@ abstract class EVMChainWalletBase final pendingEVMChainTransaction = await _client.signTransaction( privateKey: _evmChainPrivateKey, - toAddress: _credentials.outputs.first.isParsedAddress - ? _credentials.outputs.first.extractedAddress! - : _credentials.outputs.first.address, + toAddress: toAddress, amount: totalAmount, - gas: _estimatedGas!, + gas: estimatedFeesForTransaction, priority: _credentials.priority!, currency: transactionCurrency, exponent: exponent, @@ -483,6 +578,7 @@ abstract class EVMChainWalletBase return EthPrivateKey.fromHex(HEX.encode(addressAtIndex.privateKey as List)); } + @override Future? updateBalance() async => await _updateBalance(); List get erc20Currencies => evmChainErc20TokensBox.values.toList(); From 341e06196da34df29891fb3ec28b47ca94fee3a5 Mon Sep 17 00:00:00 2001 From: Adegoke David <64401859+Blazebrain@users.noreply.github.com> Date: Sun, 21 Jul 2024 00:26:05 +0100 Subject: [PATCH 6/9] CW-674: Improve Exchange Flow With Timeout For Rates (#1536) * fix: Improve exchange flow by adding a timeout to the call to fetch rate from providers * fix: Adjust time limit for fetching rate to 7 seconds and add timelimit to fetching limits * Improve loadlimits function [skip ci] --------- Co-authored-by: OmarHatem --- .../provider/sideshift_exchange_provider.dart | 16 ++++- .../exchange/exchange_view_model.dart | 58 ++++++++++++------- 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/lib/exchange/provider/sideshift_exchange_provider.dart b/lib/exchange/provider/sideshift_exchange_provider.dart index 1be4f8045..127421bbd 100644 --- a/lib/exchange/provider/sideshift_exchange_provider.dart +++ b/lib/exchange/provider/sideshift_exchange_provider.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:developer'; import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/exchange/provider/exchange_provider.dart'; @@ -29,6 +30,7 @@ class SideShiftExchangeProvider extends ExchangeProvider { CryptoCurrency.bttc, CryptoCurrency.usdt, CryptoCurrency.eos, + CryptoCurrency.xmr, ]; static const affiliateId = secrets.sideShiftAffiliateId; @@ -137,8 +139,20 @@ class SideShiftExchangeProvider extends ExchangeProvider { final response = await get(uri); final responseJSON = json.decode(response.body) as Map; + if (response.statusCode == 500) { + final responseJSON = json.decode(response.body) as Map; + final error = responseJSON['error']['message'] as String; + + throw Exception('SideShift Internal Server Error: $error'); + } + + if (response.statusCode != 200) { + throw Exception('Unexpected http status: ${response.statusCode}'); + } + return double.parse(responseJSON['rate'] as String); - } catch (_) { + } catch (e) { + log('Error fetching rate in SideShift Provider: ${e.toString()}'); return 0.00; } } diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 404fa0f25..a97b3dc05 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -405,13 +405,22 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with .where((element) => !isFixedRateMode || element.supportsFixedRate) .toList(); - final result = await Future.wait(_providers.map((element) => element.fetchRate( - from: depositCurrency, - to: receiveCurrency, - amount: amount, - isFixedRateMode: isFixedRateMode, - isReceiveAmount: isFixedRateMode))); - + final result = await Future.wait( + _providers.map( + (element) => element + .fetchRate( + from: depositCurrency, + to: receiveCurrency, + amount: amount, + isFixedRateMode: isFixedRateMode, + isReceiveAmount: isFixedRateMode, + ) + .timeout( + Duration(seconds: 7), + onTimeout: () => 0.0, + ), + ), + ); _sortedAvailableProviders.clear(); for (int i = 0; i < result.length; i++) { @@ -441,22 +450,29 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with double? highestMax = 0.0; try { - for (var provider in selectedProviders) { - /// if this provider is not valid for the current pair, skip it - if (!providersForCurrentPair().contains(provider)) continue; + final result = await Future.wait(selectedProviders + .where((element) => providersForCurrentPair().contains(provider)) + .map((provider) => provider + .fetchLimits( + from: from, + to: to, + isFixedRateMode: isFixedRateMode, + ) + .onError((error, stackTrace) => Limits(max: 0.0, min: double.maxFinite)) + .timeout( + Duration(seconds: 7), + onTimeout: () => Limits(max: 0.0, min: double.maxFinite), + ))); - try { - final tempLimits = - await provider.fetchLimits(from: from, to: to, isFixedRateMode: isFixedRateMode); - - if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin) lowestMin = tempLimits.min; - - if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax) - highestMax = tempLimits.max; - } catch (e) { - continue; + result.forEach((tempLimits) { + if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin!) { + lowestMin = tempLimits.min; } - } + + if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax!) { + highestMax = tempLimits.max; + } + }); } on ConcurrentModificationError { /// if user changed the selected providers while fetching limits /// then delay the fetching limits a bit and try again From 7514d851cec10d122f09f5a3072a50a185401e5f Mon Sep 17 00:00:00 2001 From: cyan Date: Sun, 21 Jul 2024 02:43:09 +0200 Subject: [PATCH 7/9] store() on bad network (#1543) * fix: for storing on bad network connection. * feat from standup * fix missing declaration * remove forcing trusted daemon flag [skip ci] --------- Co-authored-by: OmarHatem --- cw_monero/lib/api/wallet.dart | 16 +++++++++++++++- cw_wownero/lib/api/wallet.dart | 15 ++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/cw_monero/lib/api/wallet.dart b/cw_monero/lib/api/wallet.dart index 59eeb1498..6ca9cd1bb 100644 --- a/cw_monero/lib/api/wallet.dart +++ b/cw_monero/lib/api/wallet.dart @@ -121,9 +121,23 @@ void setRecoveringFromSeed({required bool isRecovery}) => monero.Wallet_setRecoveringFromSeed(wptr!, recoveringFromSeed: isRecovery); final storeMutex = Mutex(); + + +int lastStorePointer = 0; +int lastStoreHeight = 0; void storeSync() async { - await storeMutex.acquire(); final addr = wptr!.address; + final synchronized = await Isolate.run(() { + return monero.Wallet_synchronized(Pointer.fromAddress(addr)); + }); + if (lastStorePointer == wptr!.address && + lastStoreHeight + 5000 < monero.Wallet_blockChainHeight(wptr!) && + !synchronized) { + return; + } + lastStorePointer = wptr!.address; + lastStoreHeight = monero.Wallet_blockChainHeight(wptr!); + await storeMutex.acquire(); await Isolate.run(() { monero.Wallet_store(Pointer.fromAddress(addr)); }); diff --git a/cw_wownero/lib/api/wallet.dart b/cw_wownero/lib/api/wallet.dart index 6a7550d84..96822dfe4 100644 --- a/cw_wownero/lib/api/wallet.dart +++ b/cw_wownero/lib/api/wallet.dart @@ -126,9 +126,22 @@ void setRecoveringFromSeed({required bool isRecovery}) => wownero.Wallet_setRecoveringFromSeed(wptr!, recoveringFromSeed: isRecovery); final storeMutex = Mutex(); + +int lastStorePointer = 0; +int lastStoreHeight = 0; void storeSync() async { - await storeMutex.acquire(); final addr = wptr!.address; + final synchronized = await Isolate.run(() { + return wownero.Wallet_synchronized(Pointer.fromAddress(addr)); + }); + if (lastStorePointer == wptr!.address && + lastStoreHeight + 5000 < wownero.Wallet_blockChainHeight(wptr!) && + !synchronized) { + return; + } + lastStorePointer = wptr!.address; + lastStoreHeight = wownero.Wallet_blockChainHeight(wptr!); + await storeMutex.acquire(); Isolate.run(() { wownero.Wallet_store(Pointer.fromAddress(addr)); }); From 311fff2c446416ecc5c17e6a994e5c9aa7b2ecde Mon Sep 17 00:00:00 2001 From: Omar Hatem Date: Sun, 21 Jul 2024 03:46:43 +0300 Subject: [PATCH 8/9] Generic fixes (#1528) * update target sdk for android * make welcome page scrollable fix moonpay url params * fix null exception when restoring from backup * fix ui issues * hopefully fix the timeout exception error report [skip ci] * validate electrum addresses * disable silent payments for hardware wallets * fixes and enhancements --- android/app/build.gradle | 2 +- cw_bitcoin/lib/electrum_wallet_addresses.dart | 17 +- cw_core/lib/crypto_currency.dart | 2 +- cw_core/lib/transaction_info.dart | 2 +- cw_monero/lib/monero_subaddress_list.dart | 3 +- cw_monero/lib/monero_transaction_info.dart | 10 +- cw_wownero/lib/wownero_transaction_info.dart | 10 +- lib/bitcoin/cw_bitcoin.dart | 2 +- lib/buy/moonpay/moonpay_provider.dart | 5 +- lib/entities/provider_types.dart | 3 +- .../screens/dashboard/pages/balance_page.dart | 2 +- lib/src/screens/rescan/rescan_page.dart | 66 ++++--- lib/src/screens/welcome/welcome_page.dart | 187 +++++++++--------- lib/src/widgets/blockchain_height_widget.dart | 148 +++++++------- lib/store/settings_store.dart | 8 +- .../dashboard/balance_view_model.dart | 2 +- .../dashboard/dashboard_view_model.dart | 2 +- .../transaction_details_view_model.dart | 26 +-- res/values/strings_ar.arb | 2 +- res/values/strings_bg.arb | 2 +- res/values/strings_cs.arb | 2 +- res/values/strings_de.arb | 2 +- res/values/strings_en.arb | 2 +- res/values/strings_es.arb | 2 +- res/values/strings_fr.arb | 2 +- res/values/strings_ha.arb | 2 +- res/values/strings_hi.arb | 2 +- res/values/strings_hr.arb | 2 +- res/values/strings_id.arb | 2 +- res/values/strings_it.arb | 2 +- res/values/strings_ja.arb | 2 +- res/values/strings_ko.arb | 2 +- res/values/strings_my.arb | 2 +- res/values/strings_nl.arb | 2 +- res/values/strings_pl.arb | 2 +- res/values/strings_pt.arb | 2 +- res/values/strings_ru.arb | 2 +- res/values/strings_th.arb | 2 +- res/values/strings_tl.arb | 2 +- res/values/strings_tr.arb | 2 +- res/values/strings_uk.arb | 2 +- res/values/strings_ur.arb | 2 +- res/values/strings_yo.arb | 2 +- res/values/strings_zh.arb | 2 +- tool/append_translation.dart | 2 + 45 files changed, 280 insertions(+), 271 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 5e27aeb9e..60defb1fd 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -46,7 +46,7 @@ android { defaultConfig { applicationId appProperties['id'] minSdkVersion 24 - targetSdkVersion 33 + targetSdkVersion 34 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index e0857a6d0..9499a6db7 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -224,6 +224,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { updateAddressesByMatch(); updateReceiveAddresses(); updateChangeAddresses(); + _validateAddresses(); await updateAddressesInBox(); if (currentReceiveAddressIndex >= receiveAddresses.length) { @@ -458,10 +459,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { Future discoverAddresses(List addressList, bool isHidden, Future Function(BitcoinAddressRecord) getAddressHistory, {BitcoinAddressType type = SegwitAddresType.p2wpkh}) async { - if (!isHidden) { - _validateSideHdAddresses(addressList.toList()); - } - final newAddresses = await _createNewAddresses(gap, startIndex: addressList.length, isHidden: isHidden, type: type); addAddresses(newAddresses); @@ -541,11 +538,15 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { updateAddressesByMatch(); } - void _validateSideHdAddresses(List addrWithTransactions) { - addrWithTransactions.forEach((element) { - if (element.address != - getAddress(index: element.index, hd: mainHd, addressType: element.type)) + void _validateAddresses() { + allAddresses.forEach((element) { + if (!element.isHidden && element.address != + getAddress(index: element.index, hd: mainHd, addressType: element.type)) { element.isHidden = true; + } else if (element.isHidden && element.address != + getAddress(index: element.index, hd: sideHd, addressType: element.type)) { + element.isHidden = false; + } }); } diff --git a/cw_core/lib/crypto_currency.dart b/cw_core/lib/crypto_currency.dart index 6881393ed..cf7a73b3c 100644 --- a/cw_core/lib/crypto_currency.dart +++ b/cw_core/lib/crypto_currency.dart @@ -281,7 +281,7 @@ class CryptoCurrency extends EnumerableItem with Serializable implemen final s = 'Unexpected token: $name for CryptoCurrency fromFullName'; throw ArgumentError.value(name, 'Fullname', s); } - return CryptoCurrency._fullNameCurrencyMap[name.toLowerCase()]!; + return CryptoCurrency._fullNameCurrencyMap[name.split("(").first.trim().toLowerCase()]!; } @override diff --git a/cw_core/lib/transaction_info.dart b/cw_core/lib/transaction_info.dart index a270c0479..e363d88db 100644 --- a/cw_core/lib/transaction_info.dart +++ b/cw_core/lib/transaction_info.dart @@ -3,7 +3,7 @@ import 'package:cw_core/keyable.dart'; abstract class TransactionInfo extends Object with Keyable { late String id; - late String txhash = id; + late String txHash = id; late int amount; int? fee; late TransactionDirection direction; diff --git a/cw_monero/lib/monero_subaddress_list.dart b/cw_monero/lib/monero_subaddress_list.dart index 623d744fd..c35afb282 100644 --- a/cw_monero/lib/monero_subaddress_list.dart +++ b/cw_monero/lib/monero_subaddress_list.dart @@ -124,8 +124,7 @@ abstract class MoneroSubaddressListBase with Store { Future> _getAllUnusedAddresses( {required int accountIndex, required String label}) async { final allAddresses = subaddress_list.getAllSubaddresses(); - final lastAddress = allAddresses.length == 0 ? allAddresses.last.address : Subaddress(id: -1, address: "", label: ""); - if (allAddresses.isEmpty || _usedAddresses.contains(lastAddress)) { + if (allAddresses.isEmpty || _usedAddresses.contains(allAddresses.last)) { final isAddressUnused = await _newSubaddress(accountIndex: accountIndex, label: label); if (!isAddressUnused) { return await _getAllUnusedAddresses(accountIndex: accountIndex, label: label); diff --git a/cw_monero/lib/monero_transaction_info.dart b/cw_monero/lib/monero_transaction_info.dart index b1a4e4229..596b26812 100644 --- a/cw_monero/lib/monero_transaction_info.dart +++ b/cw_monero/lib/monero_transaction_info.dart @@ -9,14 +9,14 @@ import 'package:cw_core/format_amount.dart'; import 'package:cw_monero/api/transaction_history.dart'; class MoneroTransactionInfo extends TransactionInfo { - MoneroTransactionInfo(this.txhash, this.height, this.direction, this.date, + MoneroTransactionInfo(this.txHash, this.height, this.direction, this.date, this.isPending, this.amount, this.accountIndex, this.addressIndex, this.fee, this.confirmations) : - id = "${txhash}_${amount}_${accountIndex}_${addressIndex}"; + id = "${txHash}_${amount}_${accountIndex}_${addressIndex}"; MoneroTransactionInfo.fromMap(Map map) : id = "${map['hash']}_${map['amount']}_${map['accountIndex']}_${map['addressIndex']}", - txhash = map['hash'] as String, + txHash = map['hash'] as String, height = (map['height'] ?? 0) as int, direction = map['direction'] != null ? parseTransactionDirectionFromNumber(map['direction'] as String) @@ -39,7 +39,7 @@ class MoneroTransactionInfo extends TransactionInfo { MoneroTransactionInfo.fromRow(TransactionInfoRow row) : id = "${row.getHash()}_${row.getAmount()}_${row.subaddrAccount}_${row.subaddrIndex}", - txhash = row.getHash(), + txHash = row.getHash(), height = row.blockHeight, direction = parseTransactionDirectionFromInt(row.direction), date = DateTime.fromMillisecondsSinceEpoch(row.getDatetime() * 1000), @@ -58,7 +58,7 @@ class MoneroTransactionInfo extends TransactionInfo { } final String id; - final String txhash; + final String txHash; final int height; final TransactionDirection direction; final DateTime date; diff --git a/cw_wownero/lib/wownero_transaction_info.dart b/cw_wownero/lib/wownero_transaction_info.dart index 6016e2e59..7b0073452 100644 --- a/cw_wownero/lib/wownero_transaction_info.dart +++ b/cw_wownero/lib/wownero_transaction_info.dart @@ -7,14 +7,14 @@ import 'package:cw_core/format_amount.dart'; import 'package:cw_wownero/api/transaction_history.dart'; class WowneroTransactionInfo extends TransactionInfo { - WowneroTransactionInfo(this.txhash, this.height, this.direction, this.date, + WowneroTransactionInfo(this.txHash, this.height, this.direction, this.date, this.isPending, this.amount, this.accountIndex, this.addressIndex, this.fee, this.confirmations) : - id = "${txhash}_${amount}_${accountIndex}_${addressIndex}"; + id = "${txHash}_${amount}_${accountIndex}_${addressIndex}"; WowneroTransactionInfo.fromMap(Map map) : id = "${map['hash']}_${map['amount']}_${map['accountIndex']}_${map['addressIndex']}", - txhash = map['hash'] as String, + txHash = map['hash'] as String, height = (map['height'] ?? 0) as int, direction = map['direction'] != null ? parseTransactionDirectionFromNumber(map['direction'] as String) @@ -37,7 +37,7 @@ class WowneroTransactionInfo extends TransactionInfo { WowneroTransactionInfo.fromRow(TransactionInfoRow row) : id = "${row.getHash()}_${row.getAmount()}_${row.subaddrAccount}_${row.subaddrIndex}", - txhash = row.getHash(), + txHash = row.getHash(), height = row.blockHeight, direction = parseTransactionDirectionFromInt(row.direction), date = DateTime.fromMillisecondsSinceEpoch(row.getDatetime() * 1000), @@ -56,7 +56,7 @@ class WowneroTransactionInfo extends TransactionInfo { } final String id; - final String txhash; + final String txHash; final int height; final TransactionDirection direction; final DateTime date; diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 86d9c4985..efb1211bc 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -560,7 +560,7 @@ class CWBitcoin extends Bitcoin { if (tweaksResponse != null) { return true; } - } on RequestFailedTimeoutException { + } on RequestFailedTimeoutException catch (_) { return false; } catch (_) { rethrow; diff --git a/lib/buy/moonpay/moonpay_provider.dart b/lib/buy/moonpay/moonpay_provider.dart index 59251e064..c89fb222f 100644 --- a/lib/buy/moonpay/moonpay_provider.dart +++ b/lib/buy/moonpay/moonpay_provider.dart @@ -149,10 +149,9 @@ class MoonPayProvider extends BuyProvider { 'colorCode': settingsStore.currentTheme.type == ThemeType.dark ? '#${Palette.blueCraiola.value.toRadixString(16).substring(2, 8)}' : '#${Palette.moderateSlateBlue.value.toRadixString(16).substring(2, 8)}', - 'defaultCurrencyCode': _normalizeCurrency(currency), - 'baseCurrencyCode': _normalizeCurrency(currency), + 'baseCurrencyCode': settingsStore.fiatCurrency.title, 'baseCurrencyAmount': amount ?? '0', - 'currencyCode': currencyCode, + 'currencyCode': _normalizeCurrency(currency), 'walletAddress': walletAddress, 'lockAmount': 'false', 'showAllCurrencies': 'false', diff --git a/lib/entities/provider_types.dart b/lib/entities/provider_types.dart index da7bae4c1..b9dd4ef2a 100644 --- a/lib/entities/provider_types.dart +++ b/lib/entities/provider_types.dart @@ -67,9 +67,8 @@ class ProvidersHelper { ]; case WalletType.litecoin: case WalletType.bitcoinCash: - return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.robinhood, ProviderType.moonpay]; case WalletType.solana: - return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.robinhood]; + return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.robinhood, ProviderType.moonpay]; case WalletType.tron: return [ ProviderType.askEachTime, diff --git a/lib/src/screens/dashboard/pages/balance_page.dart b/lib/src/screens/dashboard/pages/balance_page.dart index 7ffcf918d..d95c19dad 100644 --- a/lib/src/screens/dashboard/pages/balance_page.dart +++ b/lib/src/screens/dashboard/pages/balance_page.dart @@ -340,7 +340,7 @@ class CryptoBalanceWidget extends StatelessWidget { builder: (BuildContext context) => AlertWithTwoActions( alertTitle: S.of(context).change_current_node_title, alertContent: S.of(context).confirm_silent_payments_switch_node, - rightButtonText: S.of(context).ok, + rightButtonText: S.of(context).confirm, leftButtonText: S.of(context).cancel, actionRightButton: () { dashboardViewModel.setSilentPaymentsScanning(newValue); diff --git a/lib/src/screens/rescan/rescan_page.dart b/lib/src/screens/rescan/rescan_page.dart index 4b2327c43..b66c94878 100644 --- a/lib/src/screens/rescan/rescan_page.dart +++ b/lib/src/screens/rescan/rescan_page.dart @@ -22,38 +22,42 @@ class RescanPage extends BasePage { @override Widget body(BuildContext context) { - return Padding( - padding: EdgeInsets.only(left: 24, right: 24, bottom: 24), - child: Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Observer( - builder: (_) => BlockchainHeightWidget( - key: _blockchainHeightWidgetKey, - onHeightOrDateEntered: (value) => _rescanViewModel.isButtonEnabled = value, - isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan, - doSingleScan: _rescanViewModel.doSingleScan, - toggleSingleScan: () => - _rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan, - walletType: _rescanViewModel.wallet.type, - )), - Observer( - builder: (_) => LoadingPrimaryButton( - isLoading: _rescanViewModel.state == RescanWalletState.rescaning, - text: S.of(context).rescan, - onPressed: () async { - if (_rescanViewModel.isSilentPaymentsScan) { - return _toggleSilentPaymentsScanning(context); - } + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => FocusScope.of(context).unfocus(), + child: Padding( + padding: EdgeInsets.only(left: 24, right: 24, bottom: 24), + child: Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Observer( + builder: (_) => BlockchainHeightWidget( + key: _blockchainHeightWidgetKey, + onHeightOrDateEntered: (value) => _rescanViewModel.isButtonEnabled = value, + isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan, + doSingleScan: _rescanViewModel.doSingleScan, + toggleSingleScan: () => + _rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan, + walletType: _rescanViewModel.wallet.type, + )), + Observer( + builder: (_) => LoadingPrimaryButton( + isLoading: _rescanViewModel.state == RescanWalletState.rescaning, + text: S.of(context).rescan, + onPressed: () async { + if (_rescanViewModel.isSilentPaymentsScan) { + return _toggleSilentPaymentsScanning(context); + } - _rescanViewModel.rescanCurrentWallet( - restoreHeight: _blockchainHeightWidgetKey.currentState!.height); + _rescanViewModel.rescanCurrentWallet( + restoreHeight: _blockchainHeightWidgetKey.currentState!.height); - Navigator.of(context).pop(); - }, - color: Theme.of(context).primaryColor, - textColor: Colors.white, - isDisabled: !_rescanViewModel.isButtonEnabled, - )) - ]), + Navigator.of(context).pop(); + }, + color: Theme.of(context).primaryColor, + textColor: Colors.white, + isDisabled: !_rescanViewModel.isButtonEnabled, + )) + ]), + ), ); } @@ -71,7 +75,7 @@ class RescanPage extends BasePage { builder: (BuildContext _dialogContext) => AlertWithTwoActions( alertTitle: S.of(_dialogContext).change_current_node_title, alertContent: S.of(_dialogContext).confirm_silent_payments_switch_node, - rightButtonText: S.of(_dialogContext).ok, + rightButtonText: S.of(_dialogContext).confirm, leftButtonText: S.of(_dialogContext).cancel, actionRightButton: () async { Navigator.of(_dialogContext).pop(); diff --git a/lib/src/screens/welcome/welcome_page.dart b/lib/src/screens/welcome/welcome_page.dart index 2142fdf9b..defc8e2c8 100644 --- a/lib/src/screens/welcome/welcome_page.dart +++ b/lib/src/screens/welcome/welcome_page.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; @@ -49,115 +50,113 @@ class WelcomePage extends BasePage { @override Widget body(BuildContext context) { - final welcomeImage = currentTheme.type == ThemeType.dark - ? welcomeImageDark - : welcomeImageLight; + final welcomeImage = currentTheme.type == ThemeType.dark ? welcomeImageDark : welcomeImageLight; final newWalletImage = Image.asset('assets/images/new_wallet.png', height: 12, width: 12, color: Theme.of(context).extension()!.restoreWalletButtonTextColor); final restoreWalletImage = Image.asset('assets/images/restore_wallet.png', - height: 12, - - width: 12, - color: Theme.of(context).extension()!.titleColor); + height: 12, width: 12, color: Theme.of(context).extension()!.titleColor); return WillPopScope( - onWillPop: () async => false, - child: Container( - alignment: Alignment.center, - padding: EdgeInsets.only(top: 64, bottom: 24, left: 24, right: 24), - child: ConstrainedBox( - constraints: BoxConstraints( - maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - children: [ - AspectRatio( - aspectRatio: aspectRatioImage, - child: FittedBox( - child: welcomeImage, fit: BoxFit.contain), - ), - Padding( - padding: EdgeInsets.only(top: 24), - child: Text( - S.of(context).welcome, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w500, - color: Theme.of(context).extension()!.hintTextColor, - ), - textAlign: TextAlign.center, - ), - ), - Padding( - padding: EdgeInsets.only(top: 5), - child: Text( - appTitle(context), - style: TextStyle( - fontSize: 36, - fontWeight: FontWeight.bold, - color: Theme.of(context).extension()!.titleColor, - ), - textAlign: TextAlign.center, - ), - ), - Padding( - padding: EdgeInsets.only(top: 5), - child: Text( - appDescription(context), - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Theme.of(context).extension()!.hintTextColor, - ), - textAlign: TextAlign.center, - ), - ), - ], - ), - Column( - children: [ - Text( - S.of(context).please_make_selection, + onWillPop: () async => false, + child: ScrollableWithBottomSection( + content: Container( + alignment: Alignment.center, + padding: EdgeInsets.only(top: 64, bottom: 24, left: 24, right: 24), + child: ConstrainedBox( + constraints: + BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + AspectRatio( + aspectRatio: aspectRatioImage, + child: FittedBox(child: welcomeImage, fit: BoxFit.contain), + ), + Padding( + padding: EdgeInsets.only(top: 24), + child: Text( + S.of(context).welcome, style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.normal, + fontSize: 18, + fontWeight: FontWeight.w500, color: Theme.of(context).extension()!.hintTextColor, ), textAlign: TextAlign.center, ), - Padding( - padding: EdgeInsets.only(top: 24), - child: PrimaryImageButton( - onPressed: () => Navigator.pushNamed( - context, Routes.newWalletFromWelcome), - image: newWalletImage, - text: S.of(context).create_new, - color: Theme.of(context).extension()!.createNewWalletButtonBackgroundColor, - textColor: Theme.of(context).extension()!.restoreWalletButtonTextColor, + ), + Padding( + padding: EdgeInsets.only(top: 5), + child: Text( + appTitle(context), + style: TextStyle( + fontSize: 36, + fontWeight: FontWeight.bold, + color: Theme.of(context).extension()!.titleColor, ), + textAlign: TextAlign.center, ), - Padding( - padding: EdgeInsets.only(top: 10), - child: PrimaryImageButton( - onPressed: () { - Navigator.pushNamed( - context, Routes.restoreOptions, - arguments: true); - }, - image: restoreWalletImage, - text: S.of(context).restore_wallet, - color: Theme.of(context).cardColor, - textColor: Theme.of(context).extension()!.titleColor), - ) - ], - ) - ], + ), + Padding( + padding: EdgeInsets.only(top: 5), + child: Text( + appDescription(context), + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Theme.of(context).extension()!.hintTextColor, + ), + textAlign: TextAlign.center, + ), + ), + ], + ), + ], + ), + ), + ), + bottomSection: Column( + children: [ + Text( + S.of(context).please_make_selection, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + color: Theme.of(context).extension()!.hintTextColor, ), - ))); + textAlign: TextAlign.center, + ), + Padding( + padding: EdgeInsets.only(top: 24), + child: PrimaryImageButton( + onPressed: () => Navigator.pushNamed(context, Routes.newWalletFromWelcome), + image: newWalletImage, + text: S.of(context).create_new, + color: Theme.of(context) + .extension()! + .createNewWalletButtonBackgroundColor, + textColor: + Theme.of(context).extension()!.restoreWalletButtonTextColor, + ), + ), + Padding( + padding: EdgeInsets.only(top: 10), + child: PrimaryImageButton( + onPressed: () { + Navigator.pushNamed(context, Routes.restoreOptions, arguments: true); + }, + image: restoreWalletImage, + text: S.of(context).restore_wallet, + color: Theme.of(context).cardColor, + textColor: Theme.of(context).extension()!.titleColor), + ) + ], + ), + ), + ); } } diff --git a/lib/src/widgets/blockchain_height_widget.dart b/lib/src/widgets/blockchain_height_widget.dart index 4023e66ad..e0f83a4f4 100644 --- a/lib/src/widgets/blockchain_height_widget.dart +++ b/lib/src/widgets/blockchain_height_widget.dart @@ -65,89 +65,93 @@ class BlockchainHeightState extends State { @override Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Flexible( - child: Container( - padding: EdgeInsets.only(top: 20.0, bottom: 10.0), - child: BaseTextFormField( - focusNode: widget.focusNode, - controller: restoreHeightController, - keyboardType: TextInputType.numberWithOptions(signed: false, decimal: false), - hintText: widget.isSilentPaymentsScan - ? S.of(context).silent_payments_scan_from_height - : S.of(context).widgets_restore_from_blockheight, - ))) - ], - ), - if (widget.hasDatePicker) ...[ - Padding( - padding: EdgeInsets.only(top: 15, bottom: 15), - child: Text( - S.of(context).widgets_or, - style: TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.w500, - color: Theme.of(context).extension()!.titleColor), - ), - ), + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => FocusScope.of(context).unfocus(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ Row( children: [ Flexible( child: Container( - child: InkWell( - onTap: () => _selectDate(context), - child: IgnorePointer( + padding: EdgeInsets.only(top: 20.0, bottom: 10.0), child: BaseTextFormField( - controller: dateController, - hintText: widget.isSilentPaymentsScan - ? S.of(context).silent_payments_scan_from_date - : S.of(context).widgets_restore_from_date, - )), - ), - )) + focusNode: widget.focusNode, + controller: restoreHeightController, + keyboardType: TextInputType.numberWithOptions(signed: false, decimal: false), + hintText: widget.isSilentPaymentsScan + ? S.of(context).silent_payments_scan_from_height + : S.of(context).widgets_restore_from_blockheight, + ))) ], ), - if (widget.isSilentPaymentsScan) + if (widget.hasDatePicker) ...[ Padding( - padding: EdgeInsets.only(top: 24), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - S.of(context).scan_one_block, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.normal, - color: Theme.of(context).extension()!.titleColor, - ), - ), - Padding( - padding: const EdgeInsets.only(right: 8), - child: StandardSwitch( - value: widget.doSingleScan, - onTaped: () => widget.toggleSingleScan?.call(), - ), - ) - ], + padding: EdgeInsets.only(top: 15, bottom: 15), + child: Text( + S.of(context).widgets_or, + style: TextStyle( + fontSize: 16.0, + fontWeight: FontWeight.w500, + color: Theme.of(context).extension()!.titleColor), ), ), - Padding( - padding: EdgeInsets.only(left: 40, right: 40, top: 24), - child: Text( - widget.isSilentPaymentsScan - ? S.of(context).silent_payments_scan_from_date_or_blockheight - : S.of(context).restore_from_date_or_blockheight, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 12, fontWeight: FontWeight.normal, color: Theme.of(context).hintColor), + Row( + children: [ + Flexible( + child: Container( + child: InkWell( + onTap: () => _selectDate(context), + child: IgnorePointer( + child: BaseTextFormField( + controller: dateController, + hintText: widget.isSilentPaymentsScan + ? S.of(context).silent_payments_scan_from_date + : S.of(context).widgets_restore_from_date, + )), + ), + )) + ], ), - ) - ] - ], + if (widget.isSilentPaymentsScan) + Padding( + padding: EdgeInsets.only(top: 24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + S.of(context).scan_one_block, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.normal, + color: Theme.of(context).extension()!.titleColor, + ), + ), + Padding( + padding: const EdgeInsets.only(right: 8), + child: StandardSwitch( + value: widget.doSingleScan, + onTaped: () => widget.toggleSingleScan?.call(), + ), + ) + ], + ), + ), + Padding( + padding: EdgeInsets.only(left: 40, right: 40, top: 24), + child: Text( + widget.isSilentPaymentsScan + ? S.of(context).silent_payments_scan_from_date_or_blockheight + : S.of(context).restore_from_date_or_blockheight, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, fontWeight: FontWeight.normal, color: Theme.of(context).hintColor), + ), + ) + ] + ], + ), ); } diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 8e16adbff..8fb26df53 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -1184,9 +1184,11 @@ abstract class SettingsStoreBase with Store { raw: sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority)!) ?? priority[WalletType.monero]!; - priority[WalletType.wownero] = wownero?.deserializeWowneroTransactionPriority( - raw: sharedPreferences.getInt(PreferencesKey.wowneroTransactionPriority)!) ?? - priority[WalletType.wownero]!; + if (wownero != null && + sharedPreferences.getInt(PreferencesKey.wowneroTransactionPriority) != null) { + priority[WalletType.wownero] = wownero!.deserializeWowneroTransactionPriority( + raw: sharedPreferences.getInt(PreferencesKey.wowneroTransactionPriority)!); + } if (bitcoin != null && sharedPreferences.getInt(PreferencesKey.bitcoinTransactionPriority) != null) { diff --git a/lib/view_model/dashboard/balance_view_model.dart b/lib/view_model/dashboard/balance_view_model.dart index c8acb9c2c..8deb3b27c 100644 --- a/lib/view_model/dashboard/balance_view_model.dart +++ b/lib/view_model/dashboard/balance_view_model.dart @@ -61,7 +61,7 @@ abstract class BalanceViewModelBase with Store { WalletBase, TransactionInfo> wallet; @computed - bool get hasSilentPayments => wallet.type == WalletType.bitcoin; + bool get hasSilentPayments => wallet.type == WalletType.bitcoin && !wallet.isHardwareWallet; @computed double get price { diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index b4c5240e1..cc9cf1b7a 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -308,7 +308,7 @@ abstract class DashboardViewModelBase with Store { wallet.type == WalletType.haven; @computed - bool get hasSilentPayments => wallet.type == WalletType.bitcoin; + bool get hasSilentPayments => wallet.type == WalletType.bitcoin && !wallet.isHardwareWallet; @computed bool get showSilentPaymentsCard => hasSilentPayments && settingsStore.silentPaymentsCardDisplay; diff --git a/lib/view_model/transaction_details_view_model.dart b/lib/view_model/transaction_details_view_model.dart index 025ccab9b..9e71837a7 100644 --- a/lib/view_model/transaction_details_view_model.dart +++ b/lib/view_model/transaction_details_view_model.dart @@ -86,7 +86,7 @@ abstract class TransactionDetailsViewModelBase with Store { if (showRecipientAddress && !isRecipientAddressShown) { try { final recipientAddress = transactionDescriptionBox.values - .firstWhere((val) => val.id == transactionInfo.id) + .firstWhere((val) => val.id == transactionInfo.txHash) .recipientAddress; if (recipientAddress?.isNotEmpty ?? false) { @@ -105,14 +105,14 @@ abstract class TransactionDetailsViewModelBase with Store { value: _explorerDescription(type), onTap: () async { try { - final uri = Uri.parse(_explorerUrl(type, tx.id)); + final uri = Uri.parse(_explorerUrl(type, tx.txHash)); if (await canLaunchUrl(uri)) await launchUrl(uri, mode: LaunchMode.externalApplication); } catch (e) {} })); final description = transactionDescriptionBox.values.firstWhere( - (val) => val.id == transactionInfo.id, - orElse: () => TransactionDescription(id: transactionInfo.id)); + (val) => val.id == transactionInfo.txHash, + orElse: () => TransactionDescription(id: transactionInfo.txHash)); items.add(TextFieldListItem( title: S.current.note_tap_to_change, @@ -214,7 +214,7 @@ abstract class TransactionDetailsViewModelBase with Store { final addressIndex = tx.additionalInfo['addressIndex'] as int; final feeFormatted = tx.feeFormatted(); final _items = [ - StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txhash), + StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txHash), StandartListItem( title: S.current.transaction_details_date, value: dateFormat.format(tx.date)), StandartListItem(title: S.current.transaction_details_height, value: '${tx.height}'), @@ -250,7 +250,7 @@ abstract class TransactionDetailsViewModelBase with Store { void _addElectrumListItems(TransactionInfo tx, DateFormat dateFormat) { final _items = [ - StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id), + StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txHash), StandartListItem( title: S.current.transaction_details_date, value: dateFormat.format(tx.date)), StandartListItem(title: S.current.confirmations, value: tx.confirmations.toString()), @@ -265,7 +265,7 @@ abstract class TransactionDetailsViewModelBase with Store { void _addHavenListItems(TransactionInfo tx, DateFormat dateFormat) { items.addAll([ - StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id), + StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txHash), StandartListItem( title: S.current.transaction_details_date, value: dateFormat.format(tx.date)), StandartListItem(title: S.current.transaction_details_height, value: '${tx.height}'), @@ -277,7 +277,7 @@ abstract class TransactionDetailsViewModelBase with Store { void _addEthereumListItems(TransactionInfo tx, DateFormat dateFormat) { final _items = [ - StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id), + StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txHash), StandartListItem( title: S.current.transaction_details_date, value: dateFormat.format(tx.date)), StandartListItem(title: S.current.confirmations, value: tx.confirmations.toString()), @@ -296,7 +296,7 @@ abstract class TransactionDetailsViewModelBase with Store { void _addNanoListItems(TransactionInfo tx, DateFormat dateFormat) { final _items = [ - StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id), + StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txHash), if (showRecipientAddress && tx.to != null) StandartListItem(title: S.current.transaction_details_recipient_address, value: tx.to!), if (showRecipientAddress && tx.from != null) @@ -313,7 +313,7 @@ abstract class TransactionDetailsViewModelBase with Store { void _addPolygonListItems(TransactionInfo tx, DateFormat dateFormat) { final _items = [ - StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id), + StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txHash), StandartListItem( title: S.current.transaction_details_date, value: dateFormat.format(tx.date)), StandartListItem(title: S.current.confirmations, value: tx.confirmations.toString()), @@ -332,7 +332,7 @@ abstract class TransactionDetailsViewModelBase with Store { void _addSolanaListItems(TransactionInfo tx, DateFormat dateFormat) { final _items = [ - StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id), + StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txHash), StandartListItem( title: S.current.transaction_details_date, value: dateFormat.format(tx.date)), StandartListItem(title: S.current.transaction_details_amount, value: tx.amountFormatted()), @@ -396,7 +396,7 @@ abstract class TransactionDetailsViewModelBase with Store { void _addTronListItems(TransactionInfo tx, DateFormat dateFormat) { final _items = [ - StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id), + StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txHash), StandartListItem( title: S.current.transaction_details_date, value: dateFormat.format(tx.date)), StandartListItem(title: S.current.transaction_details_amount, value: tx.amountFormatted()), @@ -455,7 +455,7 @@ abstract class TransactionDetailsViewModelBase with Store { final addressIndex = tx.additionalInfo['addressIndex'] as int; final feeFormatted = tx.feeFormatted(); final _items = [ - StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txhash), + StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txHash), StandartListItem( title: S.current.transaction_details_date, value: dateFormat.format(tx.date)), StandartListItem(title: S.current.transaction_details_height, value: '${tx.height}'), diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 59fe26ef8..124e85270 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "تأكيد خصم الرسوم", "confirm_fee_deduction_content": "هل توافق على خصم الرسوم من الإخراج؟", "confirm_sending": "تأكيد الإرسال", - "confirm_silent_payments_switch_node": "حاليا مطلوب لتبديل العقد لمسح المدفوعات الصامتة", + "confirm_silent_payments_switch_node": "العقدة الحالية لا تدعم المدفوعات الصامتة \\ ncake wallet سوف تتحول إلى عقدة متوافقة ، فقط للمسح الضوئي", "confirmations": "التأكيدات", "confirmed": "رصيد مؤكد", "confirmed_tx": "مؤكد", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 689fc06cf..dee839ea5 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "Потвърдете приспадането на таксите", "confirm_fee_deduction_content": "Съгласни ли сте да приспадате таксата от продукцията?", "confirm_sending": "Потвърждаване на изпращането", - "confirm_silent_payments_switch_node": "Понастоящем се изисква да превключвате възлите за сканиране на мълчаливи плащания", + "confirm_silent_payments_switch_node": "Текущият ви възел не поддържа Silent Payments \\ Ncake Wallet ще премине към съвместим възел, само за сканиране", "confirmations": "потвърждения", "confirmed": "Потвърден баланс", "confirmed_tx": "Потвърдено", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 433351a5b..ddc572766 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "Potvrďte odpočet poplatků", "confirm_fee_deduction_content": "Souhlasíte s odečtením poplatku z výstupu?", "confirm_sending": "Potvrdit odeslání", - "confirm_silent_payments_switch_node": "V současné době je nutné přepínat uzly pro skenování tichých plateb", + "confirm_silent_payments_switch_node": "Váš aktuální uzel nepodporuje tiché platby \\ Ncake peněženka se přepne na kompatibilní uzel, pouze pro skenování", "confirmations": "Potvrzení", "confirmed": "Potvrzený zůstatek", "confirmed_tx": "Potvrzeno", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 624147cc2..de21d7e30 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "Gebührenabzug bestätigen", "confirm_fee_deduction_content": "Stimmen Sie zu, die Gebühr von der Ausgabe abzuziehen?", "confirm_sending": "Senden bestätigen", - "confirm_silent_payments_switch_node": "Derzeit ist es erforderlich, Knoten zu wechseln, um stille Zahlungen zu scannen", + "confirm_silent_payments_switch_node": "Ihr aktueller Knoten unterstützt keine stillen Zahlungen \\ NCAKE Wallet wechselt zu einem kompatiblen Knoten, nur zum Scannen", "confirmations": "Bestätigungen", "confirmed": "Bestätigter Saldo", "confirmed_tx": "Bestätigt", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 4c7dcbebe..48950464d 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "Confirm Fee Deduction", "confirm_fee_deduction_content": "Do you agree to deduct the fee from the output?", "confirm_sending": "Confirm sending", - "confirm_silent_payments_switch_node": "Currently it is required to switch nodes to scan silent payments", + "confirm_silent_payments_switch_node": "Your current node does not support silent payments\\nCake Wallet will switch to a compatible node, just for scanning", "confirmations": "Confirmations", "confirmed": "Confirmed Balance", "confirmed_tx": "Confirmed", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 5cf99d3c3..4aa0042c0 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "Confirmar la deducción de la tarifa", "confirm_fee_deduction_content": "¿Acepta deducir la tarifa de la producción?", "confirm_sending": "Confirmar envío", - "confirm_silent_payments_switch_node": "Actualmente se requiere cambiar los nodos para escanear pagos silenciosos", + "confirm_silent_payments_switch_node": "Su nodo actual no admite pagos silenciosos \\ ncake billet cambiará a un nodo compatible, solo para escanear", "confirmations": "Confirmaciones", "confirmed": "Saldo confirmado", "confirmed_tx": "Confirmado", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 244238975..198111909 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "Confirmer la déduction des frais", "confirm_fee_deduction_content": "Acceptez-vous de déduire les frais de la production?", "confirm_sending": "Confirmer l'envoi", - "confirm_silent_payments_switch_node": "Actuellement, il est nécessaire de changer de nœuds pour scanner les paiements silencieux", + "confirm_silent_payments_switch_node": "Votre nœud actuel ne prend pas en charge les paiements silencieux \\ ncake qui passera à un nœud compatible, juste pour la numérisation", "confirmations": "Confirmations", "confirmed": "Solde confirmé", "confirmed_tx": "Confirmé", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 292ce0614..4259162c2 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "Tabbatar da cire kudade", "confirm_fee_deduction_content": "Shin kun yarda ku cire kuɗin daga fitarwa?", "confirm_sending": "Tabbatar da aikawa", - "confirm_silent_payments_switch_node": "A halin yanzu ana buƙatar sauya nodes don bincika biyan siliki", + "confirm_silent_payments_switch_node": "Kumburinku na yanzu ba ya goyan bayan biyan shiru da shiru \\ NCADA Wallet zai canza zuwa kumburi mai dacewa, don bincika", "confirmations": "Tabbatar", "confirmed": "An tabbatar", "confirmed_tx": "Tabbatar", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 3cfb5129a..01e9dc90e 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "शुल्क कटौती की पुष्टि करें", "confirm_fee_deduction_content": "क्या आप आउटपुट से शुल्क में कटौती करने के लिए सहमत हैं?", "confirm_sending": "भेजने की पुष्टि करें", - "confirm_silent_payments_switch_node": "वर्तमान में मूक भुगतान को स्कैन करने के लिए नोड्स को स्विच करना आवश्यक है", + "confirm_silent_payments_switch_node": "आपका वर्तमान नोड मूक भुगतान का समर्थन नहीं करता है \\ ncake वॉलेट एक संगत नोड पर स्विच करेगा, बस स्कैनिंग के लिए", "confirmations": "पुष्टिकरण", "confirmed": "पुष्टि की गई शेष राशिी", "confirmed_tx": "की पुष्टि", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 5f8271504..7c9a521bf 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "Potvrdite odbitak naknade", "confirm_fee_deduction_content": "Slažete li se da ćete odbiti naknadu od izlaza?", "confirm_sending": "Potvrdi slanje", - "confirm_silent_payments_switch_node": "Trenutno je potrebno prebaciti čvorove na skeniranje tihih plaćanja", + "confirm_silent_payments_switch_node": "Vaš trenutni čvor ne podržava tiha plaćanja \\ ncake novčanik prebacit će se na kompatibilni čvor, samo za skeniranje", "confirmations": "Potvrde", "confirmed": "Potvrđeno stanje", "confirmed_tx": "Potvrđen", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 9c8d91b90..0e6a894dd 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "Konfirmasi pengurangan biaya", "confirm_fee_deduction_content": "Apakah Anda setuju untuk mengurangi biaya dari output?", "confirm_sending": "Konfirmasi pengiriman", - "confirm_silent_payments_switch_node": "Saat ini diminta untuk mengganti node untuk memindai pembayaran diam", + "confirm_silent_payments_switch_node": "Node Anda saat ini tidak mendukung pembayaran diam \\ ncake Wallet akan beralih ke simpul yang kompatibel, hanya untuk pemindaian", "confirmations": "Konfirmasi", "confirmed": "Saldo Terkonfirmasi", "confirmed_tx": "Dikonfirmasi", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index db55845ef..3c937a96a 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "Conferma la detrazione delle commissioni", "confirm_fee_deduction_content": "Accetti di detrarre la commissione dall'output?", "confirm_sending": "Conferma l'invio", - "confirm_silent_payments_switch_node": "Attualmente è necessario cambiare nodi per scansionare i pagamenti silenziosi", + "confirm_silent_payments_switch_node": "Il tuo nodo corrente non supporta i pagamenti silenziosi \\ ncake Wallet passerà a un nodo compatibile, solo per la scansione", "confirmations": "Conferme", "confirmed": "Saldo confermato", "confirmed_tx": "Confermato", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index d24f0224d..1f914113d 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "料金控除を確認します", "confirm_fee_deduction_content": "出力から料金を差し引くことに同意しますか?", "confirm_sending": "送信を確認", - "confirm_silent_payments_switch_node": "現在、ノードを切り替えてサイレント決済をスキャンする必要があります", + "confirm_silent_payments_switch_node": "現在のノードはサイレントペイメントをサポートしていません\\ ncakeウォレットは、スキャン用に互換性のあるノードに切り替えます", "confirmations": "確認", "confirmed": "確認済み残高", "confirmed_tx": "確認済み", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 262c40e8d..6f72497c7 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "수수료 공제를 확인하십시오", "confirm_fee_deduction_content": "출력에서 수수료를 공제하는 데 동의하십니까?", "confirm_sending": "전송 확인", - "confirm_silent_payments_switch_node": "현재 사일런트 결제를 스캔하려면 노드를 전환해야합니다.", + "confirm_silent_payments_switch_node": "현재 노드는 무음 지불을 지원하지 않습니다 \\ ncake 지갑은 스캔을 위해 호환 가능한 노드로 전환됩니다.", "confirmations": "확인", "confirmed": "확인된 잔액", "confirmed_tx": "확인", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 4fd55684e..fa0937561 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "အခကြေးငွေကိုနှုတ်ယူခြင်း", "confirm_fee_deduction_content": "output မှအခကြေးငွေကိုယူရန်သဘောတူပါသလား။", "confirm_sending": "ပေးပို့အတည်ပြုပါ။", - "confirm_silent_payments_switch_node": "လောလောဆယ်အသံတိတ်ငွေပေးချေမှုကိုစကင်ဖတ်စစ်ဆေးရန် node များကိုပြောင်းရန်လိုအပ်သည်", + "confirm_silent_payments_switch_node": "သင်၏လက်ရှိ node သည်အသံတိတ်ငွေပေးချေမှုကိုမပံ့ပိုးပါဟု \\ t", "confirmations": "အတည်ပြုချက်များ", "confirmed": "အတည်ပြုထားသော လက်ကျန်ငွေ", "confirmed_tx": "အတည်ပြုသည်", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 70379c95c..ebfa58377 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "Bevestig de aftrek van de kosten", "confirm_fee_deduction_content": "Stemt u ermee in om de vergoeding af te trekken van de output?", "confirm_sending": "Bevestig verzending", - "confirm_silent_payments_switch_node": "Momenteel is het vereist om knooppunten te schakelen om stille betalingen te scannen", + "confirm_silent_payments_switch_node": "Uw huidige knooppunt ondersteunt geen stille betalingen \\ ncake -portemonnee schakelt over naar een compatibele knoop", "confirmations": "Bevestigingen", "confirmed": "Bevestigd saldo", "confirmed_tx": "Bevestigd", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 6d8f05fcf..ab49e165a 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "Potwierdź odliczenie opłaty", "confirm_fee_deduction_content": "Czy zgadzasz się odliczyć opłatę od wyników?", "confirm_sending": "Potwierdź wysłanie", - "confirm_silent_payments_switch_node": "Obecnie wymagane jest zmiana węzłów w celu skanowania cichych płatności", + "confirm_silent_payments_switch_node": "Twój obecny węzeł nie obsługuje cichych płatności \\ NCAKE Portfel przełączy się na kompatybilny węzeł, tylko do skanowania", "confirmations": "Potwierdzenia", "confirmed": "Potwierdzone saldo", "confirmed_tx": "Potwierdzony", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 381514341..9536a1b24 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "Confirme dedução da taxa", "confirm_fee_deduction_content": "Você concorda em deduzir a taxa da saída?", "confirm_sending": "Confirmar o envio", - "confirm_silent_payments_switch_node": "Atualmente, é necessário trocar de nós para digitalizar pagamentos silenciosos", + "confirm_silent_payments_switch_node": "Seu nó atual não suporta pagamentos silenciosos \\ Ncake Wallet mudará para um nó compatível, apenas para digitalização", "confirmations": "Confirmações", "confirmed": "Saldo Confirmado", "confirmed_tx": "Confirmado", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 8611b32f8..a95c09ca2 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "Подтвердите вычет платы", "confirm_fee_deduction_content": "Согласны ли вы вычесть плату из вывода?", "confirm_sending": "Подтвердить отправку", - "confirm_silent_payments_switch_node": "В настоящее время требуется переключение узлов для сканирования молчаливых платежей", + "confirm_silent_payments_switch_node": "Ваш текущий узел не поддерживает Silent Payments \\ ncake Wallet переключится на совместимый узел, только для сканирования", "confirmations": "Подтверждения", "confirmed": "Подтвержденный баланс", "confirmed_tx": "Подтвержденный", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index b1a96414d..5ff6b456c 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "ยืนยันการหักค่าธรรมเนียม", "confirm_fee_deduction_content": "คุณตกลงที่จะหักค่าธรรมเนียมจากผลลัพธ์หรือไม่?", "confirm_sending": "ยืนยันการส่ง", - "confirm_silent_payments_switch_node": "ขณะนี้จำเป็นต้องเปลี่ยนโหนดเพื่อสแกนการชำระเงินแบบเงียบ", + "confirm_silent_payments_switch_node": "โหนดปัจจุบันของคุณไม่รองรับการชำระเงินแบบเงียบ \\ ncake กระเป๋าเงินจะเปลี่ยนเป็นโหนดที่เข้ากันได้เพียงเพื่อการสแกน", "confirmations": "การยืนยัน", "confirmed": "ยอดคงเหลือที่ยืนยันแล้ว", "confirmed_tx": "ซึ่งยืนยันแล้ว", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 5566e4b79..2839234c8 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "Kumpirmahin ang pagbabawas ng bayad", "confirm_fee_deduction_content": "Sumasang -ayon ka bang bawasan ang bayad mula sa output?", "confirm_sending": "Kumpirmahin ang pagpapadala", - "confirm_silent_payments_switch_node": "Sa kasalukuyan kinakailangan itong lumipat ng mga node upang i -scan ang mga tahimik na pagbabayad", + "confirm_silent_payments_switch_node": "Ang iyong kasalukuyang node ay hindi sumusuporta sa tahimik na pagbabayad \\ ncake wallet ay lilipat sa isang katugmang node, para lamang sa pag -scan", "confirmations": "Mga kumpirmasyon", "confirmed": "Nakumpirma na balanse", "confirmed_tx": "Nakumpirma", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 8af93e5aa..e7d0b702d 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "Ücret kesintisini onaylayın", "confirm_fee_deduction_content": "Ücreti çıktıdan düşürmeyi kabul ediyor musunuz?", "confirm_sending": "Göndermeyi onayla", - "confirm_silent_payments_switch_node": "Şu anda sessiz ödemeleri taramak için düğümleri değiştirmek gerekiyor", + "confirm_silent_payments_switch_node": "Mevcut düğümünüz sessiz ödemeleri desteklemiyor \\ nCake cüzdanı, sadece tarama için uyumlu bir düğüme geçecektir", "confirmations": "Onay", "confirmed": "Onaylanmış Bakiye", "confirmed_tx": "Onaylanmış", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 375b9c373..f858116cc 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "Підтвердьте відрахування комісії", "confirm_fee_deduction_content": "Чи погоджуєтесь ви вирахувати комісію з сумми одержувача?", "confirm_sending": "Підтвердити відправлення", - "confirm_silent_payments_switch_node": "В даний час потрібно перемикати вузли на сканування мовчазних платежів", + "confirm_silent_payments_switch_node": "Ваш поточний вузол не підтримує мовчазні платежі \\ ncake Wallet перейде на сумісний вузол, лише для сканування", "confirmations": "Підтвердження", "confirmed": "Підтверджений баланс", "confirmed_tx": "Підтверджений", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 4f86a15b2..fc0351f4d 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "فیس میں کٹوتی کی تصدیق کریں", "confirm_fee_deduction_content": "کیا آپ آؤٹ پٹ سے فیس کم کرنے پر راضی ہیں؟", "confirm_sending": "بھیجنے کی تصدیق کریں۔", - "confirm_silent_payments_switch_node": "فی الحال خاموش ادائیگیوں کو اسکین کرنے کے لئے نوڈس کو تبدیل کرنے کی ضرورت ہے", + "confirm_silent_payments_switch_node": "آپ کا موجودہ نوڈ خاموش ادائیگیوں کی حمایت نہیں کرتا ہے۔", "confirmations": "تصدیقات", "confirmed": "تصدیق شدہ بیلنس", "confirmed_tx": "تصدیق", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 1bdb1115c..19a98ba1a 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "Jẹrisi iyọkuro owo", "confirm_fee_deduction_content": "Ṣe o gba lati yọkuro idiyele naa kuro ni iṣejade?", "confirm_sending": "Jẹ́rìí sí ránṣẹ́", - "confirm_silent_payments_switch_node": "Lọwọlọwọ o nilo lati yi awọn apa pada si awọn sisanwo ipalọlọ", + "confirm_silent_payments_switch_node": "Ilode rẹ ti lọwọlọwọ ko ṣe atilẹyin awọn sisanwo ti o dakẹ \\ owet apamọwọ yoo yipada si oju-ọrọ ibaramu, o kan fun Scning", "confirmations": "Àwọn ẹ̀rí", "confirmed": "A ti jẹ́rìí ẹ̀", "confirmed_tx": "Jẹrisi", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index b3800f597..fee4f99ee 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -143,7 +143,7 @@ "confirm_fee_deduction": "确认费用扣除", "confirm_fee_deduction_content": "您是否同意从产出中扣除费用?", "confirm_sending": "确认发送", - "confirm_silent_payments_switch_node": "目前需要切换节点来扫描无声付款", + "confirm_silent_payments_switch_node": "您当前的节点不支持无声付款\\ ncake钱包将切换到兼容节点,仅用于扫描", "confirmations": "确认", "confirmed": "确认余额", "confirmed_tx": "确认的", diff --git a/tool/append_translation.dart b/tool/append_translation.dart index ac114bf3c..24778746c 100644 --- a/tool/append_translation.dart +++ b/tool/append_translation.dart @@ -2,6 +2,8 @@ import 'utils/translation/arb_file_utils.dart'; import 'utils/translation/translation_constants.dart'; import 'utils/translation/translation_utils.dart'; +/// flutter packages pub run tool/append_translation.dart "hello_world" "Hello World!" + void main(List args) async { if (args.length != 2) { throw Exception( From 649305acc343f75d26892b92f878e18747ddc8a2 Mon Sep 17 00:00:00 2001 From: cyan Date: Sun, 21 Jul 2024 02:58:17 +0200 Subject: [PATCH 9/9] CW-681 Add address and account list to Wownero (#1538) --- lib/di.dart | 3 +- lib/src/screens/receive/receive_page.dart | 1 + .../dashboard/balance_view_model.dart | 2 +- .../dashboard/dashboard_view_model.dart | 89 ++++++++++++++++--- ...ero_account_edit_or_create_view_model.dart | 31 ++++++- .../monero_account_list_view_model.dart | 21 +++++ .../node_create_or_edit_view_model.dart | 2 +- ...let_address_edit_or_create_view_model.dart | 16 ++++ .../wallet_address_list_view_model.dart | 39 ++++---- 9 files changed, 171 insertions(+), 33 deletions(-) diff --git a/lib/di.dart b/lib/di.dart index 9136260e5..1462370fc 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -678,7 +678,7 @@ Future setup({ getIt.registerFactory(() { final wallet = getIt.get().wallet!; - if (wallet.type == WalletType.monero || wallet.type == WalletType.haven) { + if (wallet.type == WalletType.monero || wallet.type == WalletType.wownero || wallet.type == WalletType.haven) { return MoneroAccountListViewModel(wallet); } throw Exception( @@ -709,6 +709,7 @@ Future setup({ getIt.registerFactoryParam( (AccountListItem? account, _) => MoneroAccountEditOrCreateViewModel( monero!.getAccountList(getIt.get().wallet!), + wownero?.getAccountList(getIt.get().wallet!), haven?.getAccountList(getIt.get().wallet!), wallet: getIt.get().wallet!, accountListItem: account)); diff --git a/lib/src/screens/receive/receive_page.dart b/lib/src/screens/receive/receive_page.dart index 789fb42bf..03524ef79 100644 --- a/lib/src/screens/receive/receive_page.dart +++ b/lib/src/screens/receive/receive_page.dart @@ -139,6 +139,7 @@ class ReceivePage extends BasePage { walletAddressListViewModel: addressListViewModel, trailingButtonTap: () async { if (addressListViewModel.type == WalletType.monero || + addressListViewModel.type == WalletType.wownero || addressListViewModel.type == WalletType.haven) { await showPopUp( context: context, diff --git a/lib/view_model/dashboard/balance_view_model.dart b/lib/view_model/dashboard/balance_view_model.dart index 8deb3b27c..045b55261 100644 --- a/lib/view_model/dashboard/balance_view_model.dart +++ b/lib/view_model/dashboard/balance_view_model.dart @@ -88,7 +88,7 @@ abstract class BalanceViewModelBase with Store { wallet.type == WalletType.tron; @computed - bool get hasAccounts => wallet.type == WalletType.monero; + bool get hasAccounts => wallet.type == WalletType.monero || wallet.type == WalletType.wownero; @computed SortBalanceBy get sortBalanceBy => settingsStore.sortBalanceBy; diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index cc9cf1b7a..5b5353e06 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -31,6 +31,7 @@ import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart'; import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart'; import 'package:cake_wallet/view_model/settings/sync_mode.dart'; import 'package:cake_wallet/wallet_type_utils.dart'; +import 'package:cake_wallet/wownero/wownero.dart' as wow; import 'package:cryptography/cryptography.dart'; import 'package:cw_core/balance.dart'; import 'package:cw_core/cake_hive.dart'; @@ -163,6 +164,29 @@ abstract class DashboardViewModelBase with Store { final sortedTransactions = [..._accountTransactions]; sortedTransactions.sort((a, b) => a.date.compareTo(b.date)); + transactions = ObservableList.of(sortedTransactions.map((transaction) => TransactionListItem( + transaction: transaction, + balanceViewModel: balanceViewModel, + settingsStore: appStore.settingsStore))); + } else if (_wallet.type == WalletType.wownero) { + subname = wow.wownero!.getCurrentAccount(_wallet).label; + + _onMoneroAccountChangeReaction = reaction( + (_) => wow.wownero!.getWowneroWalletDetails(wallet).account, + (wow.Account account) => _onMoneroAccountChange(_wallet)); + + _onMoneroBalanceChangeReaction = reaction( + (_) => wow.wownero!.getWowneroWalletDetails(wallet).balance, + (wow.WowneroBalance balance) => _onMoneroTransactionsUpdate(_wallet)); + + final _accountTransactions = _wallet.transactionHistory.transactions.values + .where((tx) => + wow.wownero!.getTransactionInfoAccountId(tx) == wow.wownero!.getCurrentAccount(wallet).id) + .toList(); + + final sortedTransactions = [..._accountTransactions]; + sortedTransactions.sort((a, b) => a.date.compareTo(b.date)); + transactions = ObservableList.of(sortedTransactions.map((transaction) => TransactionListItem( transaction: transaction, balanceViewModel: balanceViewModel, @@ -200,6 +224,10 @@ abstract class DashboardViewModelBase with Store { return monero!.getTransactionInfoAccountId(transaction) == monero!.getCurrentAccount(wallet).id; } + if (wallet.type == WalletType.wownero) { + return wow.wownero!.getTransactionInfoAccountId(transaction) == + wow.wownero!.getCurrentAccount(wallet).id; + } return true; }); @@ -459,6 +487,21 @@ abstract class DashboardViewModelBase with Store { (_) => monero!.getMoneroWalletDetails(wallet).balance, (MoneroBalance balance) => _onMoneroTransactionsUpdate(wallet)); + _onMoneroTransactionsUpdate(wallet); + } else if (wallet.type == WalletType.wownero) { + subname = wow.wownero!.getCurrentAccount(wallet).label; + + _onMoneroAccountChangeReaction?.reaction.dispose(); + _onMoneroBalanceChangeReaction?.reaction.dispose(); + + _onMoneroAccountChangeReaction = reaction( + (_) => wow.wownero!.getWowneroWalletDetails(wallet).account, + (wow.Account account) => _onMoneroAccountChange(wallet)); + + _onMoneroBalanceChangeReaction = reaction( + (_) => wow.wownero!.getWowneroWalletDetails(wallet).balance, + (wow.WowneroBalance balance) => _onMoneroTransactionsUpdate(wallet)); + _onMoneroTransactionsUpdate(wallet); } else { // FIX-ME: Check for side effects @@ -489,32 +532,54 @@ abstract class DashboardViewModelBase with Store { return monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id; } + if (wallet.type == WalletType.wownero) { + return wow.wownero!.getTransactionInfoAccountId(tx) == wow.wownero!.getCurrentAccount(wallet).id; + } + return true; }); } @action void _onMoneroAccountChange(WalletBase wallet) { - subname = monero!.getCurrentAccount(wallet).label; + if (wallet.type == WalletType.monero) { + subname = monero!.getCurrentAccount(wallet).label; + } else if (wallet.type == WalletType.wownero) { + subname = wow.wownero!.getCurrentAccount(wallet).label; + } _onMoneroTransactionsUpdate(wallet); } @action void _onMoneroTransactionsUpdate(WalletBase wallet) { transactions.clear(); + if (wallet.type == WalletType.monero) { + final _accountTransactions = monero! + .getTransactionHistory(wallet) + .transactions + .values + .where( + (tx) => monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id) + .toList(); - final _accountTransactions = monero! - .getTransactionHistory(wallet) - .transactions - .values - .where( - (tx) => monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id) - .toList(); + transactions.addAll(_accountTransactions.map((transaction) => TransactionListItem( + transaction: transaction, + balanceViewModel: balanceViewModel, + settingsStore: appStore.settingsStore))); + } else if (wallet.type == WalletType.wownero) { + final _accountTransactions = wow.wownero! + .getTransactionHistory(wallet) + .transactions + .values + .where( + (tx) => wow.wownero!.getTransactionInfoAccountId(tx) == wow.wownero!.getCurrentAccount(wallet).id) + .toList(); - transactions.addAll(_accountTransactions.map((transaction) => TransactionListItem( - transaction: transaction, - balanceViewModel: balanceViewModel, - settingsStore: appStore.settingsStore))); + transactions.addAll(_accountTransactions.map((transaction) => TransactionListItem( + transaction: transaction, + balanceViewModel: balanceViewModel, + settingsStore: appStore.settingsStore))); + } } void updateActions() { diff --git a/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart b/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart index 2f707c9ec..8d626e258 100644 --- a/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart +++ b/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/foundation.dart'; @@ -13,7 +14,7 @@ class MoneroAccountEditOrCreateViewModel = MoneroAccountEditOrCreateViewModelBas with _$MoneroAccountEditOrCreateViewModel; abstract class MoneroAccountEditOrCreateViewModelBase with Store { - MoneroAccountEditOrCreateViewModelBase(this._moneroAccountList, this._havenAccountList, + MoneroAccountEditOrCreateViewModelBase(this._moneroAccountList, this._wowneroAccountList, this._havenAccountList, {required WalletBase wallet, AccountListItem? accountListItem}) : state = InitialExecutionState(), isEdit = accountListItem != null, @@ -30,6 +31,7 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store { String label; final MoneroAccountList _moneroAccountList; + final WowneroAccountList? _wowneroAccountList; final HavenAccountList? _havenAccountList; final AccountListItem? _accountListItem; final WalletBase _wallet; @@ -42,6 +44,10 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store { if (_wallet.type == WalletType.haven) { await saveHaven(); } + + if (_wallet.type == WalletType.wownero) { + await saveWownero(); + } } Future saveMonero() async { @@ -91,4 +97,27 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store { state = FailureState(e.toString()); } } + + Future saveWownero() async { + try { + state = IsExecutingState(); + + if (_accountListItem != null) { + await _wowneroAccountList?.setLabelAccount( + _wallet, + accountIndex: _accountListItem!.id, + label: label); + } else { + await _wowneroAccountList?.addAccount( + _wallet, + label: label); + } + + await _wallet.save(); + state = ExecutedSuccessfullyState(); + } catch (e) { + state = FailureState(e.toString()); + } + } + } diff --git a/lib/view_model/monero_account_list/monero_account_list_view_model.dart b/lib/view_model/monero_account_list/monero_account_list_view_model.dart index 4cbf95bab..448106779 100644 --- a/lib/view_model/monero_account_list/monero_account_list_view_model.dart +++ b/lib/view_model/monero_account_list/monero_account_list_view_model.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:mobx/mobx.dart'; @@ -47,6 +48,17 @@ abstract class MoneroAccountListViewModelBase with Store { .toList(); } + if (_wallet.type == WalletType.wownero) { + return wownero + !.getAccountList(_wallet) + .accounts.map((acc) => AccountListItem( + label: acc.label, + id: acc.id, + balance: acc.balance, + isSelected: acc.id == wownero!.getCurrentAccount(_wallet).id)) + .toList(); + } + throw Exception('Unexpected wallet type: ${_wallet.type}'); } @@ -62,6 +74,15 @@ abstract class MoneroAccountListViewModelBase with Store { ); } + if (_wallet.type == WalletType.wownero) { + wownero!.setCurrentAccount( + _wallet, + item.id, + item.label, + item.balance, + ); + } + if (_wallet.type == WalletType.haven) { haven!.setCurrentAccount( _wallet, diff --git a/lib/view_model/node_list/node_create_or_edit_view_model.dart b/lib/view_model/node_list/node_create_or_edit_view_model.dart index 850e248f2..86ceb654c 100644 --- a/lib/view_model/node_list/node_create_or_edit_view_model.dart +++ b/lib/view_model/node_list/node_create_or_edit_view_model.dart @@ -65,7 +65,7 @@ abstract class NodeCreateOrEditViewModelBase with Store { bool get isReady => address.isNotEmpty && port.isNotEmpty; bool get hasAuthCredentials => - _walletType == WalletType.monero || _walletType == WalletType.haven; + _walletType == WalletType.monero || _walletType == WalletType.wownero || _walletType == WalletType.haven; bool get hasTestnetSupport => _walletType == WalletType.bitcoin; diff --git a/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart index 0b4f969cb..2edda3d29 100644 --- a/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart'; @@ -80,6 +81,16 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store { await wallet.save(); } + if (wallet.type == WalletType.wownero) { + await wownero + !.getSubaddressList(wallet) + .addSubaddress( + wallet, + accountIndex: wownero!.getCurrentAccount(wallet).id, + label: label); + await wallet.save(); + } + if (wallet.type == WalletType.haven) { await haven !.getSubaddressList(wallet) @@ -103,6 +114,11 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store { accountIndex: monero!.getCurrentAccount(wallet).id, addressIndex: index, label: label); await wallet.save(); } + if (wallet.type == WalletType.wownero) { + await wownero!.getSubaddressList(wallet).setLabelSubaddress(wallet, + accountIndex: wownero!.getCurrentAccount(wallet).id, addressIndex: index, label: label); + await wallet.save(); + } if (wallet.type == WalletType.haven) { await haven!.getSubaddressList(wallet).setLabelSubaddress(wallet, accountIndex: haven!.getCurrentAccount(wallet).id, diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart index dd7f02407..6c274bb7b 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart @@ -217,7 +217,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo selectedCurrency = walletTypeToCryptoCurrency(appStore.wallet!.type), _cryptoNumberFormat = NumberFormat(_cryptoNumberPattern), hasAccounts = - appStore.wallet!.type == WalletType.monero || appStore.wallet!.type == WalletType.haven, + appStore.wallet!.type == WalletType.monero || appStore.wallet!.type == WalletType.wownero || appStore.wallet!.type == WalletType.haven, amount = '', _settingsStore = appStore.settingsStore, super(appStore: appStore) { @@ -229,7 +229,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo _init(); selectedCurrency = walletTypeToCryptoCurrency(wallet.type); - hasAccounts = wallet.type == WalletType.monero || wallet.type == WalletType.haven; + hasAccounts = wallet.type == WalletType.monero || wallet.type == WalletType.wownero || wallet.type == WalletType.haven; } static const String _cryptoNumberPattern = '0.00000000'; @@ -340,6 +340,20 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo addressList.addAll(addressItems); } + if (wallet.type == WalletType.wownero) { + final primaryAddress = wownero!.getSubaddressList(wallet).subaddresses.first; + final addressItems = wownero!.getSubaddressList(wallet).subaddresses.map((subaddress) { + final isPrimary = subaddress == primaryAddress; + + return WalletAddressListItem( + id: subaddress.id, + isPrimary: isPrimary, + name: subaddress.label, + address: subaddress.address); + }); + addressList.addAll(addressItems); + } + if (wallet.type == WalletType.haven) { final primaryAddress = haven!.getSubaddressList(wallet).subaddresses.first; final addressItems = haven!.getSubaddressList(wallet).subaddresses.map((subaddress) { @@ -430,20 +444,6 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress)); } - if (wallet.type == WalletType.wownero) { - final primaryAddress = wownero!.getSubaddressList(wallet).subaddresses.first; - final addressItems = wownero!.getSubaddressList(wallet).subaddresses.map((subaddress) { - final isPrimary = subaddress == primaryAddress; - - return WalletAddressListItem( - id: subaddress.id, - isPrimary: isPrimary, - name: subaddress.label, - address: subaddress.address); - }); - addressList.addAll(addressItems); - } - if (searchText.isNotEmpty) { return ObservableList.of(addressList.where((item) { if (item is WalletAddressListItem) { @@ -465,6 +465,10 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo return monero!.getCurrentAccount(wallet).label; } + if (wallet.type == WalletType.wownero) { + return wownero!.getCurrentAccount(wallet).label; + } + if (wallet.type == WalletType.haven) { return haven!.getCurrentAccount(wallet).label; } @@ -475,6 +479,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo @computed bool get hasAddressList => wallet.type == WalletType.monero || + wallet.type == WalletType.wownero || wallet.type == WalletType.haven || wallet.type == WalletType.bitcoinCash || wallet.type == WalletType.bitcoin || @@ -513,7 +518,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo void _init() { _baseItems = []; - if (wallet.type == WalletType.monero || wallet.type == WalletType.haven) { + if (wallet.type == WalletType.monero || wallet.type == WalletType.wownero || wallet.type == WalletType.haven) { _baseItems.add(WalletAccountListHeader()); }