mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-18 00:24:31 +00:00
commit
210b59bb65
183 changed files with 4192 additions and 2054 deletions
16
.metadata
16
.metadata
|
@ -4,7 +4,7 @@
|
||||||
# This file should be version controlled.
|
# This file should be version controlled.
|
||||||
|
|
||||||
version:
|
version:
|
||||||
revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
revision: f92f44110e87bad5ff168335c36da6f6053036e6
|
||||||
channel: stable
|
channel: stable
|
||||||
|
|
||||||
project_type: app
|
project_type: app
|
||||||
|
@ -13,17 +13,11 @@ project_type: app
|
||||||
migration:
|
migration:
|
||||||
platforms:
|
platforms:
|
||||||
- platform: root
|
- platform: root
|
||||||
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
create_revision: f92f44110e87bad5ff168335c36da6f6053036e6
|
||||||
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
base_revision: f92f44110e87bad5ff168335c36da6f6053036e6
|
||||||
- platform: linux
|
|
||||||
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
|
||||||
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
|
||||||
- platform: macos
|
- platform: macos
|
||||||
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
create_revision: f92f44110e87bad5ff168335c36da6f6053036e6
|
||||||
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
base_revision: f92f44110e87bad5ff168335c36da6f6053036e6
|
||||||
- platform: windows
|
|
||||||
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
|
||||||
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
|
||||||
|
|
||||||
# User provided section
|
# User provided section
|
||||||
|
|
||||||
|
|
BIN
assets/icon/macos-icon.png
Normal file
BIN
assets/icon/macos-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -1 +1 @@
|
||||||
Subproject commit 3f94722254d1c9ad54036e39a620ccc0bb53863b
|
Subproject commit f677dec0b34d3f9fe8fce2bc8ff5c508c3f3bb9a
|
|
@ -1 +1 @@
|
||||||
Subproject commit ec3cf5e8e1b90e006188aa8c323d4cd52dbfa9b9
|
Subproject commit 9cd241b5ea142e21c01dd7639b42603281c43287
|
|
@ -1 +1 @@
|
||||||
Subproject commit 26a152fea3ca4b8c3f1130392a02f579c2ff218c
|
Subproject commit 407425c9fcf7a30c81f1345246c7225bc18b5cd5
|
|
@ -29,13 +29,6 @@ Then in `File > Settings > Plugins`, install the **Flutter** and **Dart** plugin
|
||||||
|
|
||||||
Make a Pixel 4 (API 30) x86_64 emulator with 2GB of storage space for emulation
|
Make a Pixel 4 (API 30) x86_64 emulator with 2GB of storage space for emulation
|
||||||
|
|
||||||
### Scripted setup
|
|
||||||
|
|
||||||
[`scripts/setup.sh`](./../scripts/setup.sh) is provided as a tool to set up installation for building: download the script and run it anywhere. This script should skip the entire [Manual setup](#manual-setup) section below and prepare you for [running](#Running).
|
|
||||||
|
|
||||||
### Manual setup
|
|
||||||
> If you used the `setup.sh` script, skip to [running](#Running)
|
|
||||||
|
|
||||||
Install basic dependencies
|
Install basic dependencies
|
||||||
```
|
```
|
||||||
sudo apt-get install libssl-dev curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev llvm python3-distutils
|
sudo apt-get install libssl-dev curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev llvm python3-distutils
|
||||||
|
@ -98,20 +91,21 @@ cd ..
|
||||||
```
|
```
|
||||||
or manually by creating the files referenced in that script with the specified content.
|
or manually by creating the files referenced in that script with the specified content.
|
||||||
|
|
||||||
### Building plugins for Android
|
### Build plugins
|
||||||
|
#### Building plugins for Android
|
||||||
> Warning: This will take a long time, please be patient
|
> Warning: This will take a long time, please be patient
|
||||||
```
|
```
|
||||||
cd scripts/android
|
cd scripts/android
|
||||||
./build_all.sh
|
./build_all.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
### Building plugins for Linux
|
#### Building plugins for Linux
|
||||||
```
|
```
|
||||||
cd scripts/linux
|
cd scripts/linux
|
||||||
./build_all.sh
|
./build_all.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
### Building plugins for Windows
|
#### Building plugins for Windows
|
||||||
```
|
```
|
||||||
cd scripts/windows
|
cd scripts/windows
|
||||||
./deps.sh
|
./deps.sh
|
||||||
|
@ -140,10 +134,28 @@ flutter run linux
|
||||||
Visual Studio is required for Windows development with the Flutter SDK. Download it at https://visualstudio.microsoft.com/downloads/ and install the "Desktop development with C++" workload, including all of its default components.
|
Visual Studio is required for Windows development with the Flutter SDK. Download it at https://visualstudio.microsoft.com/downloads/ and install the "Desktop development with C++" workload, including all of its default components.
|
||||||
|
|
||||||
### Building libraries in WSL2
|
### Building libraries in WSL2
|
||||||
Set up Ubuntu 20.04 in WSL2. Follow the entire Linux host section to get set up and build windows `dll` libraries. Copy the resulting `dll`s to their respective positions on the Windows host:
|
Set up Ubuntu 20.04 in WSL2. Follow the entire Linux host section in the WSL2 Ubuntu 20.04 host to get set up to build. You will also need to install Rust and MXE dependencies on the WSL2 Ubuntu 20.04 host:
|
||||||
|
- [Install Rust](https://rustup.rs/)
|
||||||
|
```sh
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||||
|
```
|
||||||
|
- Install MXE by running `stack_wallet/scripts/windows/deps.sh`
|
||||||
|
```sh
|
||||||
|
./stack_wallet/scripts/windows/deps.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The WSL2 host may optionally be navigated to the `stack_wallet` repository on the Windows host in order to build the plugins in-place and skip the next section in which you copy the `dll`s from WSL2 to Windows. Then build windows `dll` libraries by running the following script on the WSL2 Ubuntu 20.04 host:
|
||||||
|
|
||||||
|
- `stack_wallet/scripts/windows/build_all.sh`
|
||||||
|
|
||||||
|
Copy the resulting `dll`s to their respective positions on the Windows host:
|
||||||
|
|
||||||
- `stack_wallet/crypto_plugins/flutter_libepiccash/scripts/windows/build/libepic_cash_wallet.dll`
|
- `stack_wallet/crypto_plugins/flutter_libepiccash/scripts/windows/build/libepic_cash_wallet.dll`
|
||||||
- `stack_wallet/crypto_plugins/flutter_liblelantus/scripts/windows/build/libmobileliblelantus.dll`
|
- `stack_wallet/crypto_plugins/flutter_liblelantus/scripts/windows/build/libmobileliblelantus.dll`
|
||||||
|
<!--
|
||||||
|
- `stack_wallet/crypto_plugins/flutter_libmonero/scripts/windows/build/libcw_monero.dll`
|
||||||
|
- `stack_wallet/crypto_plugins/flutter_libmonero/scripts/windows/build/libcw_wownero.dll`
|
||||||
|
-->
|
||||||
<!-- TODO: script the copying or installation of libraries from WSL2 to the parent Windows host -->
|
<!-- TODO: script the copying or installation of libraries from WSL2 to the parent Windows host -->
|
||||||
|
|
||||||
### Install Flutter on Windows host
|
### Install Flutter on Windows host
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
PODS:
|
PODS:
|
||||||
- "app_settings (3.0.0+1)":
|
|
||||||
- Flutter
|
|
||||||
- barcode_scan2 (0.0.1):
|
- barcode_scan2 (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- MTBBarcodeScanner
|
- MTBBarcodeScanner
|
||||||
|
@ -112,7 +110,7 @@ PODS:
|
||||||
- Flutter
|
- Flutter
|
||||||
- flutter_native_splash (0.0.1):
|
- flutter_native_splash (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- flutter_secure_storage (3.3.1):
|
- flutter_secure_storage (6.0.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- integration_test (0.0.1):
|
- integration_test (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
@ -149,7 +147,6 @@ PODS:
|
||||||
- Flutter
|
- Flutter
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- app_settings (from `.symlinks/plugins/app_settings/ios`)
|
|
||||||
- barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`)
|
- barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`)
|
||||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||||
- cw_monero (from `.symlinks/plugins/cw_monero/ios`)
|
- cw_monero (from `.symlinks/plugins/cw_monero/ios`)
|
||||||
|
@ -169,10 +166,10 @@ DEPENDENCIES:
|
||||||
- lelantus (from `.symlinks/plugins/lelantus/ios`)
|
- lelantus (from `.symlinks/plugins/lelantus/ios`)
|
||||||
- local_auth (from `.symlinks/plugins/local_auth/ios`)
|
- local_auth (from `.symlinks/plugins/local_auth/ios`)
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`)
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- stack_wallet_backup (from `.symlinks/plugins/stack_wallet_backup/ios`)
|
- stack_wallet_backup (from `.symlinks/plugins/stack_wallet_backup/ios`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
- wakelock (from `.symlinks/plugins/wakelock/ios`)
|
- wakelock (from `.symlinks/plugins/wakelock/ios`)
|
||||||
|
@ -188,8 +185,6 @@ SPEC REPOS:
|
||||||
- SwiftyGif
|
- SwiftyGif
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
app_settings:
|
|
||||||
:path: ".symlinks/plugins/app_settings/ios"
|
|
||||||
barcode_scan2:
|
barcode_scan2:
|
||||||
:path: ".symlinks/plugins/barcode_scan2/ios"
|
:path: ".symlinks/plugins/barcode_scan2/ios"
|
||||||
connectivity_plus:
|
connectivity_plus:
|
||||||
|
@ -229,13 +224,13 @@ EXTERNAL SOURCES:
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
:path: ".symlinks/plugins/path_provider_foundation/ios"
|
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||||
permission_handler_apple:
|
permission_handler_apple:
|
||||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||||
share_plus:
|
share_plus:
|
||||||
:path: ".symlinks/plugins/share_plus/ios"
|
:path: ".symlinks/plugins/share_plus/ios"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
:path: ".symlinks/plugins/shared_preferences_foundation/ios"
|
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||||
stack_wallet_backup:
|
stack_wallet_backup:
|
||||||
:path: ".symlinks/plugins/stack_wallet_backup/ios"
|
:path: ".symlinks/plugins/stack_wallet_backup/ios"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
|
@ -244,13 +239,12 @@ EXTERNAL SOURCES:
|
||||||
:path: ".symlinks/plugins/wakelock/ios"
|
:path: ".symlinks/plugins/wakelock/ios"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
app_settings: d103828c9f5d515c4df9ee754dabd443f7cedcf3
|
|
||||||
barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0
|
barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0
|
||||||
connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
|
connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
|
||||||
cw_monero: 9816991daff0e3ad0a8be140e31933b5526babd4
|
cw_monero: 9816991daff0e3ad0a8be140e31933b5526babd4
|
||||||
cw_shared_external: 2972d872b8917603478117c9957dfca611845a92
|
cw_shared_external: 2972d872b8917603478117c9957dfca611845a92
|
||||||
cw_wownero: ac53899fa5c6ff46b3fb490aa3b7ca36301fa832
|
cw_wownero: ac53899fa5c6ff46b3fb490aa3b7ca36301fa832
|
||||||
device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed
|
device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea
|
||||||
devicelocale: b22617f40038496deffba44747101255cee005b0
|
devicelocale: b22617f40038496deffba44747101255cee005b0
|
||||||
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
|
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
|
||||||
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
|
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
|
||||||
|
@ -260,23 +254,23 @@ SPEC CHECKSUMS:
|
||||||
flutter_libmonero: da68a616b73dd0374a8419c684fa6b6df2c44ffe
|
flutter_libmonero: da68a616b73dd0374a8419c684fa6b6df2c44ffe
|
||||||
flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743
|
flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743
|
||||||
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
|
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
|
||||||
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
|
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
|
||||||
integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5
|
integration_test: 13825b8a9334a850581300559b8839134b124670
|
||||||
isar_flutter_libs: b69f437aeab9c521821c3f376198c4371fa21073
|
isar_flutter_libs: b69f437aeab9c521821c3f376198c4371fa21073
|
||||||
lelantus: 417f0221260013dfc052cae9cf4b741b6479edba
|
lelantus: 417f0221260013dfc052cae9cf4b741b6479edba
|
||||||
local_auth: 1740f55d7af0a2e2a8684ce225fe79d8931e808c
|
local_auth: 1740f55d7af0a2e2a8684ce225fe79d8931e808c
|
||||||
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
|
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
|
||||||
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
|
package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
|
||||||
path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
|
path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
|
||||||
permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce
|
permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce
|
||||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||||
SDWebImage: 72f86271a6f3139cc7e4a89220946489d4b9a866
|
SDWebImage: 72f86271a6f3139cc7e4a89220946489d4b9a866
|
||||||
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
|
share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028
|
||||||
shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca
|
shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c
|
||||||
stack_wallet_backup: 5b8563aba5d8ffbf2ce1944331ff7294a0ec7c03
|
stack_wallet_backup: 5b8563aba5d8ffbf2ce1944331ff7294a0ec7c03
|
||||||
SwiftProtobuf: 6ef3f0e422ef90d6605ca20b21a94f6c1324d6b3
|
SwiftProtobuf: 6ef3f0e422ef90d6605ca20b21a94f6c1324d6b3
|
||||||
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
|
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
|
||||||
url_launcher_ios: fb12c43172927bb5cf75aeebd073f883801f1993
|
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
|
||||||
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
|
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
|
||||||
|
|
||||||
PODFILE CHECKSUM: 57c8aed26fba39d3ec9424816221f294a07c58eb
|
PODFILE CHECKSUM: 57c8aed26fba39d3ec9424816221f294a07c58eb
|
||||||
|
|
|
@ -242,6 +242,7 @@
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
|
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
|
||||||
);
|
);
|
||||||
name = "Thin Binary";
|
name = "Thin Binary";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
|
@ -301,7 +302,6 @@
|
||||||
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
|
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/SwiftProtobuf/SwiftProtobuf.framework",
|
"${BUILT_PRODUCTS_DIR}/SwiftProtobuf/SwiftProtobuf.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework",
|
"${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/app_settings/app_settings.framework",
|
|
||||||
"${BUILT_PRODUCTS_DIR}/barcode_scan2/barcode_scan2.framework",
|
"${BUILT_PRODUCTS_DIR}/barcode_scan2/barcode_scan2.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/connectivity_plus/connectivity_plus.framework",
|
"${BUILT_PRODUCTS_DIR}/connectivity_plus/connectivity_plus.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/cw_monero/cw_monero.framework",
|
"${BUILT_PRODUCTS_DIR}/cw_monero/cw_monero.framework",
|
||||||
|
@ -335,7 +335,6 @@
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftProtobuf.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftProtobuf.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyGif.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyGif.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/app_settings.framework",
|
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/barcode_scan2.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/barcode_scan2.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity_plus.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity_plus.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cw_monero.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cw_monero.framework",
|
||||||
|
@ -505,7 +504,10 @@
|
||||||
);
|
);
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
@ -692,7 +694,10 @@
|
||||||
);
|
);
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
@ -771,7 +776,10 @@
|
||||||
);
|
);
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
|
|
@ -13,7 +13,6 @@ import 'dart:isolate';
|
||||||
import 'package:cw_core/wallet_info.dart' as xmr;
|
import 'package:cw_core/wallet_info.dart' as xmr;
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:mutex/mutex.dart';
|
import 'package:mutex/mutex.dart';
|
||||||
import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart';
|
|
||||||
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
|
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
|
||||||
import 'package:stackwallet/models/node_model.dart';
|
import 'package:stackwallet/models/node_model.dart';
|
||||||
import 'package:stackwallet/models/notification_model.dart';
|
import 'package:stackwallet/models/notification_model.dart';
|
||||||
|
@ -23,8 +22,13 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
|
|
||||||
class DB {
|
class DB {
|
||||||
|
// legacy (required for migrations)
|
||||||
|
@Deprecated("Left over for migration from old versions of Stack Wallet")
|
||||||
static const String boxNameAddressBook = "addressBook";
|
static const String boxNameAddressBook = "addressBook";
|
||||||
static const String boxNameDebugInfo = "debugInfoBox";
|
static const String boxNameTrades = "exchangeTransactionsBox";
|
||||||
|
|
||||||
|
// in use
|
||||||
|
// TODO: migrate
|
||||||
static const String boxNameNodeModels = "nodeModels";
|
static const String boxNameNodeModels = "nodeModels";
|
||||||
static const String boxNamePrimaryNodes = "primaryNodes";
|
static const String boxNamePrimaryNodes = "primaryNodes";
|
||||||
static const String boxNameAllWalletsData = "wallets";
|
static const String boxNameAllWalletsData = "wallets";
|
||||||
|
@ -32,33 +36,31 @@ class DB {
|
||||||
static const String boxNameWatchedTransactions =
|
static const String boxNameWatchedTransactions =
|
||||||
"watchedTxNotificationModels";
|
"watchedTxNotificationModels";
|
||||||
static const String boxNameWatchedTrades = "watchedTradesNotificationModels";
|
static const String boxNameWatchedTrades = "watchedTradesNotificationModels";
|
||||||
static const String boxNameTrades = "exchangeTransactionsBox";
|
|
||||||
static const String boxNameTradesV2 = "exchangeTradesBox";
|
static const String boxNameTradesV2 = "exchangeTradesBox";
|
||||||
static const String boxNameTradeNotes = "tradeNotesBox";
|
static const String boxNameTradeNotes = "tradeNotesBox";
|
||||||
static const String boxNameTradeLookup = "tradeToTxidLookUpBox";
|
static const String boxNameTradeLookup = "tradeToTxidLookUpBox";
|
||||||
static const String boxNameFavoriteWallets = "favoriteWallets";
|
static const String boxNameFavoriteWallets = "favoriteWallets";
|
||||||
static const String boxNamePrefs = "prefs";
|
|
||||||
static const String boxNameWalletsToDeleteOnStart = "walletsToDeleteOnStart";
|
static const String boxNameWalletsToDeleteOnStart = "walletsToDeleteOnStart";
|
||||||
static const String boxNamePriceCache = "priceAPIPrice24hCache";
|
static const String boxNamePriceCache = "priceAPIPrice24hCache";
|
||||||
static const String boxNameDBInfo = "dbInfo";
|
|
||||||
// static const String boxNameTheme = "theme";
|
|
||||||
static const String boxNameDesktopData = "desktopData";
|
|
||||||
static const String boxNameBuys = "buysBox";
|
|
||||||
|
|
||||||
String boxNameTxCache({required Coin coin}) => "${coin.name}_txCache";
|
// in use (keep for now)
|
||||||
String boxNameSetCache({required Coin coin}) =>
|
static const String boxNameDBInfo = "dbInfo";
|
||||||
|
static const String boxNamePrefs = "prefs";
|
||||||
|
|
||||||
|
String _boxNameTxCache({required Coin coin}) => "${coin.name}_txCache";
|
||||||
|
|
||||||
|
// firo only
|
||||||
|
String _boxNameSetCache({required Coin coin}) =>
|
||||||
"${coin.name}_anonymitySetCache";
|
"${coin.name}_anonymitySetCache";
|
||||||
String boxNameUsedSerialsCache({required Coin coin}) =>
|
String _boxNameUsedSerialsCache({required Coin coin}) =>
|
||||||
"${coin.name}_usedSerialsCache";
|
"${coin.name}_usedSerialsCache";
|
||||||
|
|
||||||
Box<String>? _boxDebugInfo;
|
|
||||||
Box<NodeModel>? _boxNodeModels;
|
Box<NodeModel>? _boxNodeModels;
|
||||||
Box<NodeModel>? _boxPrimaryNodes;
|
Box<NodeModel>? _boxPrimaryNodes;
|
||||||
Box<dynamic>? _boxAllWalletsData;
|
Box<dynamic>? _boxAllWalletsData;
|
||||||
Box<NotificationModel>? _boxNotifications;
|
Box<NotificationModel>? _boxNotifications;
|
||||||
Box<NotificationModel>? _boxWatchedTransactions;
|
Box<NotificationModel>? _boxWatchedTransactions;
|
||||||
Box<NotificationModel>? _boxWatchedTrades;
|
Box<NotificationModel>? _boxWatchedTrades;
|
||||||
Box<ExchangeTransaction>? _boxTrades;
|
|
||||||
Box<Trade>? _boxTradesV2;
|
Box<Trade>? _boxTradesV2;
|
||||||
Box<String>? _boxTradeNotes;
|
Box<String>? _boxTradeNotes;
|
||||||
Box<String>? _boxFavoriteWallets;
|
Box<String>? _boxFavoriteWallets;
|
||||||
|
@ -66,7 +68,7 @@ class DB {
|
||||||
Box<dynamic>? _boxPrefs;
|
Box<dynamic>? _boxPrefs;
|
||||||
Box<TradeWalletLookup>? _boxTradeLookup;
|
Box<TradeWalletLookup>? _boxTradeLookup;
|
||||||
Box<dynamic>? _boxDBInfo;
|
Box<dynamic>? _boxDBInfo;
|
||||||
Box<String>? _boxDesktopData;
|
// Box<String>? _boxDesktopData;
|
||||||
|
|
||||||
final Map<String, Box<dynamic>> _walletBoxes = {};
|
final Map<String, Box<dynamic>> _walletBoxes = {};
|
||||||
|
|
||||||
|
@ -109,8 +111,6 @@ class DB {
|
||||||
_boxPrefs = await Hive.openBox<dynamic>(boxNamePrefs);
|
_boxPrefs = await Hive.openBox<dynamic>(boxNamePrefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
_boxDebugInfo = await Hive.openBox<String>(boxNameDebugInfo);
|
|
||||||
|
|
||||||
if (Hive.isBoxOpen(boxNameNodeModels)) {
|
if (Hive.isBoxOpen(boxNameNodeModels)) {
|
||||||
_boxNodeModels = Hive.box<NodeModel>(boxNameNodeModels);
|
_boxNodeModels = Hive.box<NodeModel>(boxNameNodeModels);
|
||||||
} else {
|
} else {
|
||||||
|
@ -129,19 +129,12 @@ class DB {
|
||||||
_boxAllWalletsData = await Hive.openBox<dynamic>(boxNameAllWalletsData);
|
_boxAllWalletsData = await Hive.openBox<dynamic>(boxNameAllWalletsData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Hive.isBoxOpen(boxNameDesktopData)) {
|
|
||||||
_boxDesktopData = Hive.box<String>(boxNameDesktopData);
|
|
||||||
} else {
|
|
||||||
_boxDesktopData = await Hive.openBox<String>(boxNameDesktopData);
|
|
||||||
}
|
|
||||||
|
|
||||||
_boxNotifications =
|
_boxNotifications =
|
||||||
await Hive.openBox<NotificationModel>(boxNameNotifications);
|
await Hive.openBox<NotificationModel>(boxNameNotifications);
|
||||||
_boxWatchedTransactions =
|
_boxWatchedTransactions =
|
||||||
await Hive.openBox<NotificationModel>(boxNameWatchedTransactions);
|
await Hive.openBox<NotificationModel>(boxNameWatchedTransactions);
|
||||||
_boxWatchedTrades =
|
_boxWatchedTrades =
|
||||||
await Hive.openBox<NotificationModel>(boxNameWatchedTrades);
|
await Hive.openBox<NotificationModel>(boxNameWatchedTrades);
|
||||||
_boxTrades = await Hive.openBox<ExchangeTransaction>(boxNameTrades);
|
|
||||||
_boxTradesV2 = await Hive.openBox<Trade>(boxNameTradesV2);
|
_boxTradesV2 = await Hive.openBox<Trade>(boxNameTradesV2);
|
||||||
_boxTradeNotes = await Hive.openBox<String>(boxNameTradeNotes);
|
_boxTradeNotes = await Hive.openBox<String>(boxNameTradeNotes);
|
||||||
_boxTradeLookup = await Hive.openBox<TradeWalletLookup>(boxNameTradeLookup);
|
_boxTradeLookup = await Hive.openBox<TradeWalletLookup>(boxNameTradeLookup);
|
||||||
|
@ -152,7 +145,6 @@ class DB {
|
||||||
await Future.wait([
|
await Future.wait([
|
||||||
Hive.openBox<dynamic>(boxNamePriceCache),
|
Hive.openBox<dynamic>(boxNamePriceCache),
|
||||||
_loadWalletBoxes(),
|
_loadWalletBoxes(),
|
||||||
_loadSharedCoinCacheBoxes(),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,14 +176,39 @@ class DB {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadSharedCoinCacheBoxes() async {
|
Future<Box<dynamic>> getTxCacheBox({required Coin coin}) async {
|
||||||
for (final coin in Coin.values) {
|
return _txCacheBoxes[coin] ??=
|
||||||
_txCacheBoxes[coin] =
|
await Hive.openBox<dynamic>(_boxNameTxCache(coin: coin));
|
||||||
await Hive.openBox<dynamic>(boxNameTxCache(coin: coin));
|
}
|
||||||
_setCacheBoxes[coin] =
|
|
||||||
await Hive.openBox<dynamic>(boxNameSetCache(coin: coin));
|
Future<void> closeTxCacheBox({required Coin coin}) async {
|
||||||
_usedSerialsCacheBoxes[coin] =
|
await _txCacheBoxes[coin]?.close();
|
||||||
await Hive.openBox<dynamic>(boxNameUsedSerialsCache(coin: coin));
|
}
|
||||||
|
|
||||||
|
Future<Box<dynamic>> getAnonymitySetCacheBox({required Coin coin}) async {
|
||||||
|
return _setCacheBoxes[coin] ??=
|
||||||
|
await Hive.openBox<dynamic>(_boxNameSetCache(coin: coin));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> closeAnonymitySetCacheBox({required Coin coin}) async {
|
||||||
|
await _setCacheBoxes[coin]?.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Box<dynamic>> getUsedSerialsCacheBox({required Coin coin}) async {
|
||||||
|
return _usedSerialsCacheBoxes[coin] ??=
|
||||||
|
await Hive.openBox<dynamic>(_boxNameUsedSerialsCache(coin: coin));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> closeUsedSerialsCacheBox({required Coin coin}) async {
|
||||||
|
await _usedSerialsCacheBoxes[coin]?.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear all cached transactions for the specified coin
|
||||||
|
Future<void> clearSharedTransactionCache({required Coin coin}) async {
|
||||||
|
await deleteAll<dynamic>(boxName: _boxNameTxCache(coin: coin));
|
||||||
|
if (coin == Coin.firo) {
|
||||||
|
await deleteAll<dynamic>(boxName: _boxNameSetCache(coin: coin));
|
||||||
|
await deleteAll<dynamic>(boxName: _boxNameUsedSerialsCache(coin: coin));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,6 +270,36 @@ class DB {
|
||||||
|
|
||||||
Future<void> deleteBoxFromDisk({required String boxName}) async =>
|
Future<void> deleteBoxFromDisk({required String boxName}) async =>
|
||||||
await mutex.protect(() async => await Hive.deleteBoxFromDisk(boxName));
|
await mutex.protect(() async => await Hive.deleteBoxFromDisk(boxName));
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
Future<bool> deleteEverything() async {
|
||||||
|
try {
|
||||||
|
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameAddressBook);
|
||||||
|
await DB.instance.deleteBoxFromDisk(boxName: "debugInfoBox");
|
||||||
|
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameNodeModels);
|
||||||
|
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNamePrimaryNodes);
|
||||||
|
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameAllWalletsData);
|
||||||
|
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameNotifications);
|
||||||
|
await DB.instance
|
||||||
|
.deleteBoxFromDisk(boxName: DB.boxNameWatchedTransactions);
|
||||||
|
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameWatchedTrades);
|
||||||
|
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameTrades);
|
||||||
|
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameTradesV2);
|
||||||
|
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameTradeNotes);
|
||||||
|
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameTradeLookup);
|
||||||
|
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameFavoriteWallets);
|
||||||
|
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNamePrefs);
|
||||||
|
await DB.instance
|
||||||
|
.deleteBoxFromDisk(boxName: DB.boxNameWalletsToDeleteOnStart);
|
||||||
|
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNamePriceCache);
|
||||||
|
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameDBInfo);
|
||||||
|
await DB.instance.deleteBoxFromDisk(boxName: "theme");
|
||||||
|
return true;
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log("$e $s", level: LogLevel.Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class DBKeys {
|
abstract class DBKeys {
|
||||||
|
|
|
@ -66,7 +66,7 @@ class MainDB {
|
||||||
|
|
||||||
// contact entries
|
// contact entries
|
||||||
List<ContactEntry> getContactEntries() {
|
List<ContactEntry> getContactEntries() {
|
||||||
return isar.contactEntrys.where().findAllSync();
|
return isar.contactEntrys.where().sortByName().findAllSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> deleteContactEntry({required String id}) {
|
Future<bool> deleteContactEntry({required String id}) {
|
||||||
|
|
|
@ -46,7 +46,7 @@ class EthTokenTxExtraDTO {
|
||||||
),
|
),
|
||||||
gas: _amountFromJsonNum(map['gas']),
|
gas: _amountFromJsonNum(map['gas']),
|
||||||
gasPrice: _amountFromJsonNum(map['gasPrice']),
|
gasPrice: _amountFromJsonNum(map['gasPrice']),
|
||||||
nonce: map['nonce'] as int,
|
nonce: map['nonce'] as int?,
|
||||||
input: map['input'] as String,
|
input: map['input'] as String,
|
||||||
gasCost: _amountFromJsonNum(map['gasCost']),
|
gasCost: _amountFromJsonNum(map['gasCost']),
|
||||||
gasUsed: _amountFromJsonNum(map['gasUsed']),
|
gasUsed: _amountFromJsonNum(map['gasUsed']),
|
||||||
|
@ -63,7 +63,7 @@ class EthTokenTxExtraDTO {
|
||||||
final Amount gas;
|
final Amount gas;
|
||||||
final Amount gasPrice;
|
final Amount gasPrice;
|
||||||
final String input;
|
final String input;
|
||||||
final int nonce;
|
final int? nonce;
|
||||||
final Amount gasCost;
|
final Amount gasCost;
|
||||||
final Amount gasUsed;
|
final Amount gasUsed;
|
||||||
|
|
||||||
|
@ -116,13 +116,13 @@ class EthTokenTxExtraDTO {
|
||||||
map['timestamp'] = timestamp;
|
map['timestamp'] = timestamp;
|
||||||
map['from'] = from;
|
map['from'] = from;
|
||||||
map['to'] = to;
|
map['to'] = to;
|
||||||
map['value'] = value;
|
map['value'] = value.toJsonString();
|
||||||
map['gas'] = gas;
|
map['gas'] = gas.toJsonString();
|
||||||
map['gasPrice'] = gasPrice;
|
map['gasPrice'] = gasPrice.toJsonString();
|
||||||
map['input'] = input;
|
map['input'] = input;
|
||||||
map['nonce'] = nonce;
|
map['nonce'] = nonce;
|
||||||
map['gasCost'] = gasCost;
|
map['gasCost'] = gasCost.toJsonString();
|
||||||
map['gasUsed'] = gasUsed;
|
map['gasUsed'] = gasUsed.toJsonString();
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,16 +127,16 @@ class EthTxDTO {
|
||||||
map['timestamp'] = timestamp;
|
map['timestamp'] = timestamp;
|
||||||
map['from'] = from;
|
map['from'] = from;
|
||||||
map['to'] = to;
|
map['to'] = to;
|
||||||
map['value'] = value;
|
map['value'] = value.toString();
|
||||||
map['gas'] = gas;
|
map['gas'] = gas.toString();
|
||||||
map['gasPrice'] = gasPrice;
|
map['gasPrice'] = gasPrice.toString();
|
||||||
map['maxFeePerGas'] = maxFeePerGas;
|
map['maxFeePerGas'] = maxFeePerGas.toString();
|
||||||
map['maxPriorityFeePerGas'] = maxPriorityFeePerGas;
|
map['maxPriorityFeePerGas'] = maxPriorityFeePerGas.toString();
|
||||||
map['isError'] = isError;
|
map['isError'] = isError;
|
||||||
map['hasToken'] = hasToken;
|
map['hasToken'] = hasToken;
|
||||||
map['compressedTx'] = compressedTx;
|
map['compressedTx'] = compressedTx;
|
||||||
map['gasCost'] = gasCost;
|
map['gasCost'] = gasCost.toString();
|
||||||
map['gasUsed'] = gasUsed;
|
map['gasUsed'] = gasUsed.toString();
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,9 +38,8 @@ class CachedElectrumX {
|
||||||
required Coin coin,
|
required Coin coin,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final cachedSet = DB.instance.get<dynamic>(
|
final box = await DB.instance.getAnonymitySetCacheBox(coin: coin);
|
||||||
boxName: DB.instance.boxNameSetCache(coin: coin),
|
final cachedSet = box.get(groupId) as Map?;
|
||||||
key: groupId) as Map?;
|
|
||||||
|
|
||||||
Map<String, dynamic> set;
|
Map<String, dynamic> set;
|
||||||
|
|
||||||
|
@ -71,7 +70,7 @@ class CachedElectrumX {
|
||||||
: newSet["blockHash"];
|
: newSet["blockHash"];
|
||||||
for (int i = (newSet["coins"] as List).length - 1; i >= 0; i--) {
|
for (int i = (newSet["coins"] as List).length - 1; i >= 0; i--) {
|
||||||
dynamic newCoin = newSet["coins"][i];
|
dynamic newCoin = newSet["coins"][i];
|
||||||
List translatedCoin = [];
|
List<dynamic> translatedCoin = [];
|
||||||
translatedCoin.add(!isHexadecimal(newCoin[0] as String)
|
translatedCoin.add(!isHexadecimal(newCoin[0] as String)
|
||||||
? base64ToHex(newCoin[0] as String)
|
? base64ToHex(newCoin[0] as String)
|
||||||
: newCoin[0]);
|
: newCoin[0]);
|
||||||
|
@ -91,10 +90,7 @@ class CachedElectrumX {
|
||||||
set["coins"].insert(0, translatedCoin);
|
set["coins"].insert(0, translatedCoin);
|
||||||
}
|
}
|
||||||
// save set to db
|
// save set to db
|
||||||
await DB.instance.put<dynamic>(
|
await box.put(groupId, set);
|
||||||
boxName: DB.instance.boxNameSetCache(coin: coin),
|
|
||||||
key: groupId,
|
|
||||||
value: set);
|
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"Updated current anonymity set for ${coin.name} with group ID $groupId",
|
"Updated current anonymity set for ${coin.name} with group ID $groupId",
|
||||||
level: LogLevel.Info,
|
level: LogLevel.Info,
|
||||||
|
@ -130,8 +126,9 @@ class CachedElectrumX {
|
||||||
bool verbose = true,
|
bool verbose = true,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final cachedTx = DB.instance.get<dynamic>(
|
final box = await DB.instance.getTxCacheBox(coin: coin);
|
||||||
boxName: DB.instance.boxNameTxCache(coin: coin), key: txHash) as Map?;
|
|
||||||
|
final cachedTx = box.get(txHash) as Map?;
|
||||||
if (cachedTx == null) {
|
if (cachedTx == null) {
|
||||||
final Map<String, dynamic> result = await electrumXClient
|
final Map<String, dynamic> result = await electrumXClient
|
||||||
.getTransaction(txHash: txHash, verbose: verbose);
|
.getTransaction(txHash: txHash, verbose: verbose);
|
||||||
|
@ -141,10 +138,7 @@ class CachedElectrumX {
|
||||||
|
|
||||||
if (result["confirmations"] != null &&
|
if (result["confirmations"] != null &&
|
||||||
result["confirmations"] as int > minCacheConfirms) {
|
result["confirmations"] as int > minCacheConfirms) {
|
||||||
await DB.instance.put<dynamic>(
|
await box.put(txHash, result);
|
||||||
boxName: DB.instance.boxNameTxCache(coin: coin),
|
|
||||||
key: txHash,
|
|
||||||
value: result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Logging.instance.log("using fetched result", level: LogLevel.Info);
|
Logging.instance.log("using fetched result", level: LogLevel.Info);
|
||||||
|
@ -166,9 +160,9 @@ class CachedElectrumX {
|
||||||
int startNumber = 0,
|
int startNumber = 0,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final _list = DB.instance.get<dynamic>(
|
final box = await DB.instance.getUsedSerialsCacheBox(coin: coin);
|
||||||
boxName: DB.instance.boxNameUsedSerialsCache(coin: coin),
|
|
||||||
key: "serials") as List?;
|
final _list = box.get("serials") as List?;
|
||||||
|
|
||||||
List<String> cachedSerials =
|
List<String> cachedSerials =
|
||||||
_list == null ? [] : List<String>.from(_list);
|
_list == null ? [] : List<String>.from(_list);
|
||||||
|
@ -188,10 +182,9 @@ class CachedElectrumX {
|
||||||
}
|
}
|
||||||
cachedSerials.addAll(newSerials);
|
cachedSerials.addAll(newSerials);
|
||||||
|
|
||||||
await DB.instance.put<dynamic>(
|
await box.put(
|
||||||
boxName: DB.instance.boxNameUsedSerialsCache(coin: coin),
|
"serials",
|
||||||
key: "serials",
|
cachedSerials,
|
||||||
value: cachedSerials,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return cachedSerials;
|
return cachedSerials;
|
||||||
|
@ -205,11 +198,6 @@ class CachedElectrumX {
|
||||||
|
|
||||||
/// Clear all cached transactions for the specified coin
|
/// Clear all cached transactions for the specified coin
|
||||||
Future<void> clearSharedTransactionCache({required Coin coin}) async {
|
Future<void> clearSharedTransactionCache({required Coin coin}) async {
|
||||||
await DB.instance
|
await DB.instance.closeAnonymitySetCacheBox(coin: coin);
|
||||||
.deleteAll<dynamic>(boxName: DB.instance.boxNameTxCache(coin: coin));
|
|
||||||
await DB.instance
|
|
||||||
.deleteAll<dynamic>(boxName: DB.instance.boxNameSetCache(coin: coin));
|
|
||||||
await DB.instance.deleteAll<dynamic>(
|
|
||||||
boxName: DB.instance.boxNameUsedSerialsCache(coin: coin));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,7 +146,7 @@ class ElectrumX {
|
||||||
throw response.exception!;
|
throw response.exception!;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.data["error"] != null) {
|
if (response.data is Map && response.data["error"] != null) {
|
||||||
if (response.data["error"]
|
if (response.data["error"]
|
||||||
.toString()
|
.toString()
|
||||||
.contains("No such mempool or blockchain transaction")) {
|
.contains("No such mempool or blockchain transaction")) {
|
||||||
|
@ -160,7 +160,7 @@ class ElectrumX {
|
||||||
"JSONRPC response\n"
|
"JSONRPC response\n"
|
||||||
" command: $command\n"
|
" command: $command\n"
|
||||||
" args: $args\n"
|
" args: $args\n"
|
||||||
" error: $response.data",
|
" error: ${response.data}",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -167,6 +167,8 @@ void main() async {
|
||||||
await Hive.openBox<dynamic>(DB.boxNamePrefs);
|
await Hive.openBox<dynamic>(DB.boxNamePrefs);
|
||||||
await Prefs.instance.init();
|
await Prefs.instance.init();
|
||||||
|
|
||||||
|
await StackFileSystem.initThemesDir();
|
||||||
|
|
||||||
// Desktop migrate handled elsewhere (currently desktop_login_view.dart)
|
// Desktop migrate handled elsewhere (currently desktop_login_view.dart)
|
||||||
if (!Util.isDesktop) {
|
if (!Util.isDesktop) {
|
||||||
int dbVersion = DB.instance.get<dynamic>(
|
int dbVersion = DB.instance.get<dynamic>(
|
||||||
|
@ -312,7 +314,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
||||||
}
|
}
|
||||||
|
|
||||||
ref.read(applicationThemesDirectoryPathProvider.notifier).state =
|
ref.read(applicationThemesDirectoryPathProvider.notifier).state =
|
||||||
(await StackFileSystem.applicationThemesDirectory()).path;
|
StackFileSystem.themesDir!.path;
|
||||||
|
|
||||||
_notificationsService = ref.read(notificationsProvider);
|
_notificationsService = ref.read(notificationsProvider);
|
||||||
_nodeService = ref.read(nodeServiceChangeNotifierProvider);
|
_nodeService = ref.read(nodeServiceChangeNotifierProvider);
|
||||||
|
@ -407,7 +409,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
//Add themes path to provider
|
//Add themes path to provider
|
||||||
ref.read(applicationThemesDirectoryPathProvider.notifier).state =
|
ref.read(applicationThemesDirectoryPathProvider.notifier).state =
|
||||||
(await StackFileSystem.applicationThemesDirectory()).path;
|
StackFileSystem.themesDir!.path;
|
||||||
|
|
||||||
ref.read(themeProvider.state).state = ref.read(pThemeService).getTheme(
|
ref.read(themeProvider.state).state = ref.read(pThemeService).getTheme(
|
||||||
themeId: themeId,
|
themeId: themeId,
|
||||||
|
@ -463,6 +465,12 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeLocales(List<Locale>? locales) {
|
||||||
|
ref.read(localeServiceChangeNotifierProvider).loadLocale();
|
||||||
|
super.didChangeLocales(locales);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) async {
|
void didChangeAppLifecycleState(AppLifecycleState state) async {
|
||||||
debugPrint("didChangeAppLifecycleState: ${state.name}");
|
debugPrint("didChangeAppLifecycleState: ${state.name}");
|
||||||
|
|
|
@ -33,6 +33,22 @@ class ContactEntry {
|
||||||
@Index(unique: true, replace: true)
|
@Index(unique: true, replace: true)
|
||||||
late final String customId;
|
late final String customId;
|
||||||
|
|
||||||
|
@ignore
|
||||||
|
List<ContactAddressEntry> get addressesSorted {
|
||||||
|
final List<ContactAddressEntry> sorted = [];
|
||||||
|
for (final coin in Coin.values) {
|
||||||
|
final slice = addresses.where((e) => e.coin == coin).toList();
|
||||||
|
if (slice.isNotEmpty) {
|
||||||
|
slice.sort(
|
||||||
|
(a, b) => (a.other ?? a.label).compareTo(b.other ?? b.label),
|
||||||
|
);
|
||||||
|
sorted.addAll(slice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sorted;
|
||||||
|
}
|
||||||
|
|
||||||
ContactEntry copyWith({
|
ContactEntry copyWith({
|
||||||
bool shouldCopyEmojiWithNull = false,
|
bool shouldCopyEmojiWithNull = false,
|
||||||
String? emojiChar,
|
String? emojiChar,
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
@ -17,6 +18,7 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/extensions/impl/box_shadow.dart';
|
import 'package:stackwallet/utilities/extensions/impl/box_shadow.dart';
|
||||||
import 'package:stackwallet/utilities/extensions/impl/gradient.dart';
|
import 'package:stackwallet/utilities/extensions/impl/gradient.dart';
|
||||||
import 'package:stackwallet/utilities/extensions/impl/string.dart';
|
import 'package:stackwallet/utilities/extensions/impl/string.dart';
|
||||||
|
import 'package:stackwallet/utilities/stack_file_system.dart';
|
||||||
|
|
||||||
part 'stack_theme.g.dart';
|
part 'stack_theme.g.dart';
|
||||||
|
|
||||||
|
@ -1508,7 +1510,6 @@ class StackTheme {
|
||||||
|
|
||||||
factory StackTheme.fromJson({
|
factory StackTheme.fromJson({
|
||||||
required Map<String, dynamic> json,
|
required Map<String, dynamic> json,
|
||||||
required String applicationThemesDirectoryPath,
|
|
||||||
}) {
|
}) {
|
||||||
final version = json["version"] as int? ?? 1;
|
final version = json["version"] as int? ?? 1;
|
||||||
|
|
||||||
|
@ -1517,21 +1518,18 @@ class StackTheme {
|
||||||
..assetsV1 = version == 1
|
..assetsV1 = version == 1
|
||||||
? ThemeAssets.fromJson(
|
? ThemeAssets.fromJson(
|
||||||
json: Map<String, dynamic>.from(json["assets"] as Map),
|
json: Map<String, dynamic>.from(json["assets"] as Map),
|
||||||
applicationThemesDirectoryPath: applicationThemesDirectoryPath,
|
|
||||||
themeId: json["id"] as String,
|
themeId: json["id"] as String,
|
||||||
)
|
)
|
||||||
: null
|
: null
|
||||||
..assetsV2 = version == 2
|
..assetsV2 = version == 2
|
||||||
? ThemeAssetsV2.fromJson(
|
? ThemeAssetsV2.fromJson(
|
||||||
json: Map<String, dynamic>.from(json["assets"] as Map),
|
json: Map<String, dynamic>.from(json["assets"] as Map),
|
||||||
applicationThemesDirectoryPath: applicationThemesDirectoryPath,
|
|
||||||
themeId: json["id"] as String,
|
themeId: json["id"] as String,
|
||||||
)
|
)
|
||||||
: null
|
: null
|
||||||
..assetsV3 = version >= 3
|
..assetsV3 = version >= 3
|
||||||
? ThemeAssetsV3.fromJson(
|
? ThemeAssetsV3.fromJson(
|
||||||
json: Map<String, dynamic>.from(json["assets"] as Map),
|
json: Map<String, dynamic>.from(json["assets"] as Map),
|
||||||
applicationThemesDirectoryPath: applicationThemesDirectoryPath,
|
|
||||||
themeId: json["id"] as String,
|
themeId: json["id"] as String,
|
||||||
)
|
)
|
||||||
: null
|
: null
|
||||||
|
@ -1954,117 +1952,81 @@ class ThemeAssets implements IThemeAssets {
|
||||||
|
|
||||||
factory ThemeAssets.fromJson({
|
factory ThemeAssets.fromJson({
|
||||||
required Map<String, dynamic> json,
|
required Map<String, dynamic> json,
|
||||||
required String applicationThemesDirectoryPath,
|
|
||||||
required String themeId,
|
required String themeId,
|
||||||
}) {
|
}) {
|
||||||
return ThemeAssets()
|
return ThemeAssets()
|
||||||
..bellNew =
|
..bellNew = "$themeId/assets/${json["bell_new"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["bell_new"] as String}"
|
..buy = "$themeId/assets/${json["buy"] as String}"
|
||||||
..buy =
|
..exchange = "$themeId/assets/${json["exchange"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["buy"] as String}"
|
|
||||||
..exchange =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["exchange"] as String}"
|
|
||||||
..personaIncognito =
|
..personaIncognito =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["persona_incognito"] as String}"
|
"$themeId/assets/${json["persona_incognito"] as String}"
|
||||||
..personaEasy =
|
..personaEasy = "$themeId/assets/${json["persona_easy"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["persona_easy"] as String}"
|
..stack = "$themeId/assets/${json["stack"] as String}"
|
||||||
..stack =
|
..stackIcon = "$themeId/assets/${json["stack_icon"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["stack"] as String}"
|
..receive = "$themeId/assets/${json["receive"] as String}"
|
||||||
..stackIcon =
|
..receivePending = "$themeId/assets/${json["receive_pending"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["stack_icon"] as String}"
|
|
||||||
..receive =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["receive"] as String}"
|
|
||||||
..receivePending =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["receive_pending"] as String}"
|
|
||||||
..receiveCancelled =
|
..receiveCancelled =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["receive_cancelled"] as String}"
|
"$themeId/assets/${json["receive_cancelled"] as String}"
|
||||||
..send =
|
..send = "$themeId/assets/${json["send"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["send"] as String}"
|
..sendPending = "$themeId/assets/${json["send_pending"] as String}"
|
||||||
..sendPending =
|
..sendCancelled = "$themeId/assets/${json["send_cancelled"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["send_pending"] as String}"
|
..themeSelector = "$themeId/assets/${json["theme_selector"] as String}"
|
||||||
..sendCancelled =
|
..themePreview = "$themeId/assets/${json["theme_preview"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["send_cancelled"] as String}"
|
..txExchange = "$themeId/assets/${json["tx_exchange"] as String}"
|
||||||
..themeSelector =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["theme_selector"] as String}"
|
|
||||||
..themePreview =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["theme_preview"] as String}"
|
|
||||||
..txExchange =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["tx_exchange"] as String}"
|
|
||||||
..txExchangePending =
|
..txExchangePending =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["tx_exchange_pending"] as String}"
|
"$themeId/assets/${json["tx_exchange_pending"] as String}"
|
||||||
..txExchangeFailed =
|
..txExchangeFailed =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["tx_exchange_failed"] as String}"
|
"$themeId/assets/${json["tx_exchange_failed"] as String}"
|
||||||
..bitcoin =
|
..bitcoin = "$themeId/assets/${json["bitcoin"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["bitcoin"] as String}"
|
..litecoin = "$themeId/assets/${json["litecoin"] as String}"
|
||||||
..litecoin =
|
..bitcoincash = "$themeId/assets/${json["bitcoincash"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["litecoin"] as String}"
|
..dogecoin = "$themeId/assets/${json["dogecoin"] as String}"
|
||||||
..bitcoincash =
|
..epicCash = "$themeId/assets/${json["epicCash"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["bitcoincash"] as String}"
|
..ethereum = "$themeId/assets/${json["ethereum"] as String}"
|
||||||
..dogecoin =
|
..firo = "$themeId/assets/${json["firo"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["dogecoin"] as String}"
|
..monero = "$themeId/assets/${json["monero"] as String}"
|
||||||
..epicCash =
|
..wownero = "$themeId/assets/${json["wownero"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["epicCash"] as String}"
|
..namecoin = "$themeId/assets/${json["namecoin"] as String}"
|
||||||
..ethereum =
|
..particl = "$themeId/assets/${json["particl"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["ethereum"] as String}"
|
..bitcoinImage = "$themeId/assets/${json["bitcoin_image"] as String}"
|
||||||
..firo =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["firo"] as String}"
|
|
||||||
..monero =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["monero"] as String}"
|
|
||||||
..wownero =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["wownero"] as String}"
|
|
||||||
..namecoin =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["namecoin"] as String}"
|
|
||||||
..particl =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["particl"] as String}"
|
|
||||||
..bitcoinImage =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["bitcoin_image"] as String}"
|
|
||||||
..bitcoincashImage =
|
..bitcoincashImage =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["bitcoincash_image"] as String}"
|
"$themeId/assets/${json["bitcoincash_image"] as String}"
|
||||||
..dogecoinImage =
|
..dogecoinImage = "$themeId/assets/${json["dogecoin_image"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["dogecoin_image"] as String}"
|
..epicCashImage = "$themeId/assets/${json["epicCash_image"] as String}"
|
||||||
..epicCashImage =
|
..ethereumImage = "$themeId/assets/${json["ethereum_image"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["epicCash_image"] as String}"
|
..firoImage = "$themeId/assets/${json["firo_image"] as String}"
|
||||||
..ethereumImage =
|
..litecoinImage = "$themeId/assets/${json["litecoin_image"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["ethereum_image"] as String}"
|
..moneroImage = "$themeId/assets/${json["monero_image"] as String}"
|
||||||
..firoImage =
|
..wowneroImage = "$themeId/assets/${json["wownero_image"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["firo_image"] as String}"
|
..namecoinImage = "$themeId/assets/${json["namecoin_image"] as String}"
|
||||||
..litecoinImage =
|
..particlImage = "$themeId/assets/${json["particl_image"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["litecoin_image"] as String}"
|
|
||||||
..moneroImage =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["monero_image"] as String}"
|
|
||||||
..wowneroImage =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["wownero_image"] as String}"
|
|
||||||
..namecoinImage =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["namecoin_image"] as String}"
|
|
||||||
..particlImage =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["particl_image"] as String}"
|
|
||||||
..bitcoinImageSecondary =
|
..bitcoinImageSecondary =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["bitcoin_image_secondary"] as String}"
|
"$themeId/assets/${json["bitcoin_image_secondary"] as String}"
|
||||||
..bitcoincashImageSecondary =
|
..bitcoincashImageSecondary =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["bitcoincash_image_secondary"] as String}"
|
"$themeId/assets/${json["bitcoincash_image_secondary"] as String}"
|
||||||
..dogecoinImageSecondary =
|
..dogecoinImageSecondary =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["dogecoin_image_secondary"] as String}"
|
"$themeId/assets/${json["dogecoin_image_secondary"] as String}"
|
||||||
..epicCashImageSecondary =
|
..epicCashImageSecondary =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["epicCash_image_secondary"] as String}"
|
"$themeId/assets/${json["epicCash_image_secondary"] as String}"
|
||||||
..ethereumImageSecondary =
|
..ethereumImageSecondary =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["ethereum_image_secondary"] as String}"
|
"$themeId/assets/${json["ethereum_image_secondary"] as String}"
|
||||||
..firoImageSecondary =
|
..firoImageSecondary =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["firo_image_secondary"] as String}"
|
"$themeId/assets/${json["firo_image_secondary"] as String}"
|
||||||
..litecoinImageSecondary =
|
..litecoinImageSecondary =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["litecoin_image_secondary"] as String}"
|
"$themeId/assets/${json["litecoin_image_secondary"] as String}"
|
||||||
..moneroImageSecondary =
|
..moneroImageSecondary =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["monero_image_secondary"] as String}"
|
"$themeId/assets/${json["monero_image_secondary"] as String}"
|
||||||
..wowneroImageSecondary =
|
..wowneroImageSecondary =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["wownero_image_secondary"] as String}"
|
"$themeId/assets/${json["wownero_image_secondary"] as String}"
|
||||||
..namecoinImageSecondary =
|
..namecoinImageSecondary =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["namecoin_image_secondary"] as String}"
|
"$themeId/assets/${json["namecoin_image_secondary"] as String}"
|
||||||
..particlImageSecondary =
|
..particlImageSecondary =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["particl_image_secondary"] as String}"
|
"$themeId/assets/${json["particl_image_secondary"] as String}"
|
||||||
..loadingGif = json["loading_gif"] is String
|
..loadingGif = json["loading_gif"] is String
|
||||||
? "$applicationThemesDirectoryPath/$themeId/assets/${json["loading_gif"] as String}"
|
? "$themeId/assets/${json["loading_gif"] as String}"
|
||||||
: null
|
: null
|
||||||
..background = json["background"] is String
|
..background = json["background"] is String
|
||||||
? "$applicationThemesDirectoryPath/$themeId/assets/${json["background"] as String}"
|
? "$themeId/assets/${json["background"] as String}"
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2146,65 +2108,50 @@ class ThemeAssetsV2 implements IThemeAssets {
|
||||||
|
|
||||||
factory ThemeAssetsV2.fromJson({
|
factory ThemeAssetsV2.fromJson({
|
||||||
required Map<String, dynamic> json,
|
required Map<String, dynamic> json,
|
||||||
required String applicationThemesDirectoryPath,
|
|
||||||
required String themeId,
|
required String themeId,
|
||||||
}) {
|
}) {
|
||||||
return ThemeAssetsV2()
|
return ThemeAssetsV2()
|
||||||
..bellNew =
|
..bellNew = "$themeId/assets/${json["bell_new"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["bell_new"] as String}"
|
..buy = "$themeId/assets/${json["buy"] as String}"
|
||||||
..buy =
|
..exchange = "$themeId/assets/${json["exchange"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["buy"] as String}"
|
|
||||||
..exchange =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["exchange"] as String}"
|
|
||||||
..personaIncognito =
|
..personaIncognito =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["persona_incognito"] as String}"
|
"$themeId/assets/${json["persona_incognito"] as String}"
|
||||||
..personaEasy =
|
..personaEasy = "$themeId/assets/${json["persona_easy"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["persona_easy"] as String}"
|
..stack = "$themeId/assets/${json["stack"] as String}"
|
||||||
..stack =
|
..stackIcon = "$themeId/assets/${json["stack_icon"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["stack"] as String}"
|
..receive = "$themeId/assets/${json["receive"] as String}"
|
||||||
..stackIcon =
|
..receivePending = "$themeId/assets/${json["receive_pending"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["stack_icon"] as String}"
|
|
||||||
..receive =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["receive"] as String}"
|
|
||||||
..receivePending =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["receive_pending"] as String}"
|
|
||||||
..receiveCancelled =
|
..receiveCancelled =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["receive_cancelled"] as String}"
|
"$themeId/assets/${json["receive_cancelled"] as String}"
|
||||||
..send =
|
..send = "$themeId/assets/${json["send"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["send"] as String}"
|
..sendPending = "$themeId/assets/${json["send_pending"] as String}"
|
||||||
..sendPending =
|
..sendCancelled = "$themeId/assets/${json["send_cancelled"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["send_pending"] as String}"
|
..themeSelector = "$themeId/assets/${json["theme_selector"] as String}"
|
||||||
..sendCancelled =
|
..themePreview = "$themeId/assets/${json["theme_preview"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["send_cancelled"] as String}"
|
..txExchange = "$themeId/assets/${json["tx_exchange"] as String}"
|
||||||
..themeSelector =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["theme_selector"] as String}"
|
|
||||||
..themePreview =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["theme_preview"] as String}"
|
|
||||||
..txExchange =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["tx_exchange"] as String}"
|
|
||||||
..txExchangePending =
|
..txExchangePending =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["tx_exchange_pending"] as String}"
|
"$themeId/assets/${json["tx_exchange_pending"] as String}"
|
||||||
..txExchangeFailed =
|
..txExchangeFailed =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["tx_exchange_failed"] as String}"
|
"$themeId/assets/${json["tx_exchange_failed"] as String}"
|
||||||
..coinPlaceholder =
|
..coinPlaceholder =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["coin_placeholder"] as String}"
|
"$themeId/assets/${json["coin_placeholder"] as String}"
|
||||||
..coinIconsString = createCoinAssetsString(
|
..coinIconsString = createCoinAssetsString(
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets",
|
"$themeId/assets",
|
||||||
Map<String, dynamic>.from(json["coins"]["icons"] as Map),
|
Map<String, dynamic>.from(json["coins"]["icons"] as Map),
|
||||||
)
|
)
|
||||||
..coinImagesString = createCoinAssetsString(
|
..coinImagesString = createCoinAssetsString(
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets",
|
"$themeId/assets",
|
||||||
Map<String, dynamic>.from(json["coins"]["images"] as Map),
|
Map<String, dynamic>.from(json["coins"]["images"] as Map),
|
||||||
)
|
)
|
||||||
..coinSecondaryImagesString = createCoinAssetsString(
|
..coinSecondaryImagesString = createCoinAssetsString(
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets",
|
"$themeId/assets",
|
||||||
Map<String, dynamic>.from(json["coins"]["secondaries"] as Map),
|
Map<String, dynamic>.from(json["coins"]["secondaries"] as Map),
|
||||||
)
|
)
|
||||||
..loadingGif = json["loading_gif"] is String
|
..loadingGif = json["loading_gif"] is String
|
||||||
? "$applicationThemesDirectoryPath/$themeId/assets/${json["loading_gif"] as String}"
|
? "$themeId/assets/${json["loading_gif"] as String}"
|
||||||
: null
|
: null
|
||||||
..background = json["background"] is String
|
..background = json["background"] is String
|
||||||
? "$applicationThemesDirectoryPath/$themeId/assets/${json["background"] as String}"
|
? "$themeId/assets/${json["background"] as String}"
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2265,53 +2212,135 @@ class ThemeAssetsV2 implements IThemeAssets {
|
||||||
|
|
||||||
@Embedded(inheritance: false)
|
@Embedded(inheritance: false)
|
||||||
class ThemeAssetsV3 implements IThemeAssets {
|
class ThemeAssetsV3 implements IThemeAssets {
|
||||||
|
@Name("bellNew")
|
||||||
|
late final String bellNewRelative;
|
||||||
@override
|
@override
|
||||||
late final String bellNew;
|
@ignore
|
||||||
@override
|
String get bellNew => prependIfNeeded(bellNewRelative);
|
||||||
late final String buy;
|
|
||||||
@override
|
|
||||||
late final String exchange;
|
|
||||||
@override
|
|
||||||
late final String personaIncognito;
|
|
||||||
@override
|
|
||||||
late final String personaEasy;
|
|
||||||
@override
|
|
||||||
late final String stack;
|
|
||||||
@override
|
|
||||||
late final String stackIcon;
|
|
||||||
@override
|
|
||||||
late final String receive;
|
|
||||||
@override
|
|
||||||
late final String receivePending;
|
|
||||||
@override
|
|
||||||
late final String receiveCancelled;
|
|
||||||
@override
|
|
||||||
late final String send;
|
|
||||||
@override
|
|
||||||
late final String sendPending;
|
|
||||||
@override
|
|
||||||
late final String sendCancelled;
|
|
||||||
@override
|
|
||||||
late final String themeSelector;
|
|
||||||
@override
|
|
||||||
late final String themePreview;
|
|
||||||
@override
|
|
||||||
late final String txExchange;
|
|
||||||
@override
|
|
||||||
late final String txExchangePending;
|
|
||||||
@override
|
|
||||||
late final String txExchangeFailed;
|
|
||||||
@override
|
|
||||||
late final String? loadingGif;
|
|
||||||
@override
|
|
||||||
late final String? background;
|
|
||||||
|
|
||||||
late final String coinPlaceholder;
|
@Name("buy")
|
||||||
|
late final String buyRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String get buy => prependIfNeeded(buyRelative);
|
||||||
|
|
||||||
|
@Name("exchange")
|
||||||
|
late final String exchangeRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String get exchange => prependIfNeeded(exchangeRelative);
|
||||||
|
|
||||||
|
@Name("personaIncognito")
|
||||||
|
late final String personaIncognitoRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String get personaIncognito => prependIfNeeded(personaIncognitoRelative);
|
||||||
|
|
||||||
|
@Name("personaEasy")
|
||||||
|
late final String personaEasyRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String get personaEasy => prependIfNeeded(personaEasyRelative);
|
||||||
|
|
||||||
|
@Name("stack")
|
||||||
|
late final String stackRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String get stack => prependIfNeeded(stackRelative);
|
||||||
|
|
||||||
|
@Name("stackIcon")
|
||||||
|
late final String stackIconRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String get stackIcon => prependIfNeeded(stackIconRelative);
|
||||||
|
|
||||||
|
@Name("receive")
|
||||||
|
late final String receiveRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String get receive => prependIfNeeded(receiveRelative);
|
||||||
|
|
||||||
|
@Name("receivePending")
|
||||||
|
late final String receivePendingRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String get receivePending => prependIfNeeded(receivePendingRelative);
|
||||||
|
|
||||||
|
@Name("receiveCancelled")
|
||||||
|
late final String receiveCancelledRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String get receiveCancelled => prependIfNeeded(receiveCancelledRelative);
|
||||||
|
|
||||||
|
@Name("send")
|
||||||
|
late final String sendRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String get send => prependIfNeeded(sendRelative);
|
||||||
|
|
||||||
|
@Name("sendPending")
|
||||||
|
late final String sendPendingRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String get sendPending => prependIfNeeded(sendPendingRelative);
|
||||||
|
|
||||||
|
@Name("sendCancelled")
|
||||||
|
late final String sendCancelledRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String get sendCancelled => prependIfNeeded(sendCancelledRelative);
|
||||||
|
|
||||||
|
@Name("themeSelector")
|
||||||
|
late final String themeSelectorRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String get themeSelector => prependIfNeeded(themeSelectorRelative);
|
||||||
|
|
||||||
|
@Name("themePreview")
|
||||||
|
late final String themePreviewRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String get themePreview => prependIfNeeded(themePreviewRelative);
|
||||||
|
|
||||||
|
@Name("txExchange")
|
||||||
|
late final String txExchangeRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String get txExchange => prependIfNeeded(txExchangeRelative);
|
||||||
|
|
||||||
|
@Name("txExchangePending")
|
||||||
|
late final String txExchangePendingRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String get txExchangePending => prependIfNeeded(txExchangePendingRelative);
|
||||||
|
|
||||||
|
@Name("txExchangeFailed")
|
||||||
|
late final String txExchangeFailedRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String get txExchangeFailed => prependIfNeeded(txExchangeFailedRelative);
|
||||||
|
|
||||||
|
@Name("loadingGif")
|
||||||
|
late final String? loadingGifRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String? get loadingGif =>
|
||||||
|
loadingGifRelative != null ? prependIfNeeded(loadingGifRelative!) : null;
|
||||||
|
|
||||||
|
@Name("background")
|
||||||
|
late final String? backgroundRelative;
|
||||||
|
@override
|
||||||
|
@ignore
|
||||||
|
String? get background =>
|
||||||
|
backgroundRelative != null ? prependIfNeeded(backgroundRelative!) : null;
|
||||||
|
|
||||||
|
@Name("coinPlaceholder")
|
||||||
|
late final String coinPlaceholderRelative;
|
||||||
|
@ignore
|
||||||
|
String get coinPlaceholder => prependIfNeeded(coinPlaceholderRelative);
|
||||||
|
|
||||||
// Added some future proof params in case we want to add anything else
|
// Added some future proof params in case we want to add anything else
|
||||||
// This should provide some buffer in stead of creating assetsV4 etc
|
// This should provide some buffer in stead of creating assetsV4 etc
|
||||||
@Name("otherStringParam1")
|
|
||||||
late final String? dummy1;
|
|
||||||
@Name("otherStringParam2")
|
@Name("otherStringParam2")
|
||||||
late final String? dummy2;
|
late final String? dummy2;
|
||||||
@Name("otherStringParam3")
|
@Name("otherStringParam3")
|
||||||
|
@ -2357,81 +2386,110 @@ class ThemeAssetsV3 implements IThemeAssets {
|
||||||
Map<Coin, String>? _coinCardImages;
|
Map<Coin, String>? _coinCardImages;
|
||||||
late final String? coinCardImagesString;
|
late final String? coinCardImagesString;
|
||||||
|
|
||||||
|
@ignore
|
||||||
|
Map<Coin, String>? get coinCardFavoritesImages =>
|
||||||
|
_coinCardFavoritesImages ??= coinCardFavoritesImagesString == null
|
||||||
|
? null
|
||||||
|
: parseCoinAssetsString(
|
||||||
|
coinCardFavoritesImagesString!,
|
||||||
|
placeHolder: coinPlaceholder,
|
||||||
|
);
|
||||||
|
@ignore
|
||||||
|
Map<Coin, String>? _coinCardFavoritesImages;
|
||||||
|
@Name("otherStringParam1")
|
||||||
|
late final String? coinCardFavoritesImagesString;
|
||||||
|
|
||||||
ThemeAssetsV3();
|
ThemeAssetsV3();
|
||||||
|
|
||||||
factory ThemeAssetsV3.fromJson({
|
factory ThemeAssetsV3.fromJson({
|
||||||
required Map<String, dynamic> json,
|
required Map<String, dynamic> json,
|
||||||
required String applicationThemesDirectoryPath,
|
|
||||||
required String themeId,
|
required String themeId,
|
||||||
}) {
|
}) {
|
||||||
return ThemeAssetsV3()
|
return ThemeAssetsV3()
|
||||||
..bellNew =
|
..bellNewRelative = "$themeId/assets/${json["bell_new"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["bell_new"] as String}"
|
..buyRelative = "$themeId/assets/${json["buy"] as String}"
|
||||||
..buy =
|
..exchangeRelative = "$themeId/assets/${json["exchange"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["buy"] as String}"
|
..personaIncognitoRelative =
|
||||||
..exchange =
|
"$themeId/assets/${json["persona_incognito"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["exchange"] as String}"
|
..personaEasyRelative =
|
||||||
..personaIncognito =
|
"$themeId/assets/${json["persona_easy"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["persona_incognito"] as String}"
|
..stackRelative = "$themeId/assets/${json["stack"] as String}"
|
||||||
..personaEasy =
|
..stackIconRelative = "$themeId/assets/${json["stack_icon"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["persona_easy"] as String}"
|
..receiveRelative = "$themeId/assets/${json["receive"] as String}"
|
||||||
..stack =
|
..receivePendingRelative =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["stack"] as String}"
|
"$themeId/assets/${json["receive_pending"] as String}"
|
||||||
..stackIcon =
|
..receiveCancelledRelative =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["stack_icon"] as String}"
|
"$themeId/assets/${json["receive_cancelled"] as String}"
|
||||||
..receive =
|
..sendRelative = "$themeId/assets/${json["send"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["receive"] as String}"
|
..sendPendingRelative =
|
||||||
..receivePending =
|
"$themeId/assets/${json["send_pending"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["receive_pending"] as String}"
|
..sendCancelledRelative =
|
||||||
..receiveCancelled =
|
"$themeId/assets/${json["send_cancelled"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["receive_cancelled"] as String}"
|
..themeSelectorRelative =
|
||||||
..send =
|
"$themeId/assets/${json["theme_selector"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["send"] as String}"
|
..themePreviewRelative =
|
||||||
..sendPending =
|
"$themeId/assets/${json["theme_preview"] as String}"
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["send_pending"] as String}"
|
..txExchangeRelative = "$themeId/assets/${json["tx_exchange"] as String}"
|
||||||
..sendCancelled =
|
..txExchangePendingRelative =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["send_cancelled"] as String}"
|
"$themeId/assets/${json["tx_exchange_pending"] as String}"
|
||||||
..themeSelector =
|
..txExchangeFailedRelative =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["theme_selector"] as String}"
|
"$themeId/assets/${json["tx_exchange_failed"] as String}"
|
||||||
..themePreview =
|
..coinPlaceholderRelative =
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["theme_preview"] as String}"
|
"$themeId/assets/${json["coin_placeholder"] as String}"
|
||||||
..txExchange =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["tx_exchange"] as String}"
|
|
||||||
..txExchangePending =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["tx_exchange_pending"] as String}"
|
|
||||||
..txExchangeFailed =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["tx_exchange_failed"] as String}"
|
|
||||||
..coinPlaceholder =
|
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets/${json["coin_placeholder"] as String}"
|
|
||||||
..coinIconsString = createCoinAssetsString(
|
..coinIconsString = createCoinAssetsString(
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets",
|
"$themeId/assets",
|
||||||
Map<String, dynamic>.from(json["coins"]["icons"] as Map),
|
Map<String, dynamic>.from(json["coins"]["icons"] as Map),
|
||||||
)
|
)
|
||||||
..coinImagesString = createCoinAssetsString(
|
..coinImagesString = createCoinAssetsString(
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets",
|
"$themeId/assets",
|
||||||
Map<String, dynamic>.from(json["coins"]["images"] as Map),
|
Map<String, dynamic>.from(json["coins"]["images"] as Map),
|
||||||
)
|
)
|
||||||
..coinSecondaryImagesString = createCoinAssetsString(
|
..coinSecondaryImagesString = createCoinAssetsString(
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets",
|
"$themeId/assets",
|
||||||
Map<String, dynamic>.from(json["coins"]["secondaries"] as Map),
|
Map<String, dynamic>.from(json["coins"]["secondaries"] as Map),
|
||||||
)
|
)
|
||||||
..coinCardImagesString = json["coins"]["cards"] is Map
|
..coinCardImagesString = json["coins"]["cards"] is Map
|
||||||
? createCoinAssetsString(
|
? createCoinAssetsString(
|
||||||
"$applicationThemesDirectoryPath/$themeId/assets",
|
"$themeId/assets",
|
||||||
Map<String, dynamic>.from(json["coins"]["cards"] as Map),
|
Map<String, dynamic>.from(json["coins"]["cards"] as Map),
|
||||||
)
|
)
|
||||||
: null
|
: null
|
||||||
..loadingGif = json["loading_gif"] is String
|
..coinCardFavoritesImagesString = json["coins"]["favoriteCards"] is Map
|
||||||
? "$applicationThemesDirectoryPath/$themeId/assets/${json["loading_gif"] as String}"
|
? createCoinAssetsString(
|
||||||
|
"$themeId/assets",
|
||||||
|
Map<String, dynamic>.from(json["coins"]["favoriteCards"] as Map),
|
||||||
|
)
|
||||||
: null
|
: null
|
||||||
..background = json["background"] is String
|
..loadingGifRelative = json["loading_gif"] is String
|
||||||
? "$applicationThemesDirectoryPath/$themeId/assets/${json["background"] as String}"
|
? "$themeId/assets/${json["loading_gif"] as String}"
|
||||||
|
: null
|
||||||
|
..backgroundRelative = json["background"] is String
|
||||||
|
? "$themeId/assets/${json["background"] as String}"
|
||||||
: null
|
: null
|
||||||
..dummy1 = null
|
|
||||||
..dummy2 = null
|
..dummy2 = null
|
||||||
..dummy3 = null;
|
..dummy3 = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String prependIfNeeded(String relativePath) {
|
||||||
|
final path = StackFileSystem.themesDir!.path;
|
||||||
|
|
||||||
|
if (relativePath.startsWith(path)) {
|
||||||
|
return relativePath;
|
||||||
|
} else {
|
||||||
|
if (Platform.isIOS) {
|
||||||
|
const pattern = "/var/mobile/Containers/Data/Application/";
|
||||||
|
if (relativePath.startsWith(pattern)) {
|
||||||
|
final parts = relativePath.split("/Library/themes/");
|
||||||
|
if (parts.isNotEmpty) {
|
||||||
|
return "$path/${parts.last}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "$path/$relativePath";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static String createCoinAssetsString(String path, Map<String, dynamic> json) {
|
static String createCoinAssetsString(String path, Map<String, dynamic> json) {
|
||||||
final Map<String, dynamic> map = {};
|
final Map<String, dynamic> map = {};
|
||||||
for (final entry in json.entries) {
|
for (final entry in json.entries) {
|
||||||
|
@ -2451,6 +2509,8 @@ class ThemeAssetsV3 implements IThemeAssets {
|
||||||
|
|
||||||
for (final coin in Coin.values) {
|
for (final coin in Coin.values) {
|
||||||
result[coin] = map[coin.name] as String? ?? placeHolder;
|
result[coin] = map[coin.name] as String? ?? placeHolder;
|
||||||
|
|
||||||
|
result[coin] = prependIfNeeded(result[coin]!);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -2484,6 +2544,7 @@ class ThemeAssetsV3 implements IThemeAssets {
|
||||||
'coinImages: $coinImages, '
|
'coinImages: $coinImages, '
|
||||||
'coinSecondaryImages: $coinSecondaryImages, '
|
'coinSecondaryImages: $coinSecondaryImages, '
|
||||||
'coinCardImages: $coinCardImages'
|
'coinCardImages: $coinCardImages'
|
||||||
|
'coinCardFavoritesImages: $coinCardFavoritesImages'
|
||||||
')';
|
')';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,6 +8,8 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stackwallet/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart';
|
import 'package:stackwallet/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
|
@ -32,35 +34,37 @@ class CreateWalletButtonGroup extends StatelessWidget {
|
||||||
crossAxisAlignment:
|
crossAxisAlignment:
|
||||||
isDesktop ? CrossAxisAlignment.center : CrossAxisAlignment.stretch,
|
isDesktop ? CrossAxisAlignment.center : CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
ConstrainedBox(
|
if (Platform.isAndroid || coin != Coin.wownero)
|
||||||
constraints: BoxConstraints(
|
ConstrainedBox(
|
||||||
minHeight: isDesktop ? 70 : 0,
|
constraints: BoxConstraints(
|
||||||
minWidth: isDesktop ? 480 : 0,
|
minHeight: isDesktop ? 70 : 0,
|
||||||
),
|
minWidth: isDesktop ? 480 : 0,
|
||||||
child: TextButton(
|
),
|
||||||
style: Theme.of(context)
|
child: TextButton(
|
||||||
.extension<StackColors>()!
|
style: Theme.of(context)
|
||||||
.getPrimaryEnabledButtonStyle(context),
|
.extension<StackColors>()!
|
||||||
onPressed: () {
|
.getPrimaryEnabledButtonStyle(context),
|
||||||
Navigator.of(context).pushNamed(
|
onPressed: () {
|
||||||
NameYourWalletView.routeName,
|
Navigator.of(context).pushNamed(
|
||||||
arguments: Tuple2(
|
NameYourWalletView.routeName,
|
||||||
AddWalletType.New,
|
arguments: Tuple2(
|
||||||
coin,
|
AddWalletType.New,
|
||||||
),
|
coin,
|
||||||
);
|
),
|
||||||
},
|
);
|
||||||
child: Text(
|
},
|
||||||
"Create new wallet",
|
child: Text(
|
||||||
style: isDesktop
|
"Create new wallet",
|
||||||
? STextStyles.desktopButtonEnabled(context)
|
style: isDesktop
|
||||||
: STextStyles.button(context),
|
? STextStyles.desktopButtonEnabled(context)
|
||||||
|
: STextStyles.button(context),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
if (Platform.isAndroid || coin != Coin.wownero)
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: isDesktop ? 16 : 12,
|
height: isDesktop ? 16 : 12,
|
||||||
),
|
),
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
minHeight: isDesktop ? 70 : 0,
|
minHeight: isDesktop ? 70 : 0,
|
||||||
|
|
|
@ -98,6 +98,7 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
|
||||||
|
|
||||||
final List<TextEditingController> _controllers = [];
|
final List<TextEditingController> _controllers = [];
|
||||||
final List<FormInputStatus> _inputStatuses = [];
|
final List<FormInputStatus> _inputStatuses = [];
|
||||||
|
final List<FocusNode> _focusNodes = [];
|
||||||
|
|
||||||
late final BarcodeScannerInterface scanner;
|
late final BarcodeScannerInterface scanner;
|
||||||
|
|
||||||
|
@ -151,6 +152,7 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
|
||||||
for (int i = 0; i < _seedWordCount; i++) {
|
for (int i = 0; i < _seedWordCount; i++) {
|
||||||
_controllers.add(TextEditingController());
|
_controllers.add(TextEditingController());
|
||||||
_inputStatuses.add(FormInputStatus.empty);
|
_inputStatuses.add(FormInputStatus.empty);
|
||||||
|
_focusNodes.add(FocusNode());
|
||||||
}
|
}
|
||||||
|
|
||||||
super.initState();
|
super.initState();
|
||||||
|
@ -819,30 +821,43 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
|
||||||
i * 4 + j - 1 == 1
|
i * 4 + j - 1 == 1
|
||||||
? textSelectionControls
|
? textSelectionControls
|
||||||
: null,
|
: null,
|
||||||
|
focusNode:
|
||||||
|
_focusNodes[i * 4 + j - 1],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
|
final FormInputStatus
|
||||||
|
formInputStatus;
|
||||||
|
|
||||||
if (value.isEmpty) {
|
if (value.isEmpty) {
|
||||||
setState(() {
|
formInputStatus =
|
||||||
_inputStatuses[
|
FormInputStatus.empty;
|
||||||
i * 4 + j - 1] =
|
|
||||||
FormInputStatus.empty;
|
|
||||||
});
|
|
||||||
} else if (_isValidMnemonicWord(
|
} else if (_isValidMnemonicWord(
|
||||||
value
|
value
|
||||||
.trim()
|
.trim()
|
||||||
.toLowerCase())) {
|
.toLowerCase())) {
|
||||||
setState(() {
|
formInputStatus =
|
||||||
_inputStatuses[
|
FormInputStatus.valid;
|
||||||
i * 4 + j - 1] =
|
|
||||||
FormInputStatus.valid;
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
setState(() {
|
formInputStatus =
|
||||||
_inputStatuses[
|
FormInputStatus.invalid;
|
||||||
i * 4 + j - 1] =
|
|
||||||
FormInputStatus
|
|
||||||
.invalid;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (formInputStatus ==
|
||||||
|
FormInputStatus.valid) {
|
||||||
|
if (i * 4 + j <
|
||||||
|
_focusNodes.length) {
|
||||||
|
_focusNodes[i * 4 + j]
|
||||||
|
.requestFocus();
|
||||||
|
} else if (i * 4 + j ==
|
||||||
|
_focusNodes.length) {
|
||||||
|
_focusNodes[i * 4 + j - 1]
|
||||||
|
.unfocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_inputStatuses[i * 4 +
|
||||||
|
j -
|
||||||
|
1] = formInputStatus;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
controller:
|
controller:
|
||||||
_controllers[i * 4 + j - 1],
|
_controllers[i * 4 + j - 1],
|
||||||
|
@ -914,26 +929,45 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
|
||||||
selectionControls: i == 1
|
selectionControls: i == 1
|
||||||
? textSelectionControls
|
? textSelectionControls
|
||||||
: null,
|
: null,
|
||||||
|
focusNode: _focusNodes[i],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
|
final FormInputStatus
|
||||||
|
formInputStatus;
|
||||||
|
|
||||||
if (value.isEmpty) {
|
if (value.isEmpty) {
|
||||||
setState(() {
|
formInputStatus =
|
||||||
_inputStatuses[i] =
|
FormInputStatus.empty;
|
||||||
FormInputStatus.empty;
|
|
||||||
});
|
|
||||||
} else if (_isValidMnemonicWord(
|
} else if (_isValidMnemonicWord(
|
||||||
value
|
value
|
||||||
.trim()
|
.trim()
|
||||||
.toLowerCase())) {
|
.toLowerCase())) {
|
||||||
setState(() {
|
formInputStatus =
|
||||||
_inputStatuses[i] =
|
FormInputStatus.valid;
|
||||||
FormInputStatus.valid;
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
setState(() {
|
formInputStatus =
|
||||||
_inputStatuses[i] =
|
FormInputStatus.invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formInputStatus ==
|
||||||
FormInputStatus
|
FormInputStatus
|
||||||
.invalid;
|
.valid &&
|
||||||
});
|
(i - 1) <
|
||||||
|
_focusNodes.length) {
|
||||||
|
Focus.of(context)
|
||||||
|
.requestFocus(
|
||||||
|
_focusNodes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formInputStatus ==
|
||||||
|
FormInputStatus.valid) {
|
||||||
|
if (i + 1 <
|
||||||
|
_focusNodes.length) {
|
||||||
|
_focusNodes[i + 1]
|
||||||
|
.requestFocus();
|
||||||
|
} else if (i + 1 ==
|
||||||
|
_focusNodes.length) {
|
||||||
|
_focusNodes[i].unfocus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
controller: _controllers[i],
|
controller: _controllers[i],
|
||||||
|
@ -1034,24 +1068,34 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
|
||||||
AutovalidateMode.onUserInteraction,
|
AutovalidateMode.onUserInteraction,
|
||||||
selectionControls:
|
selectionControls:
|
||||||
i == 1 ? textSelectionControls : null,
|
i == 1 ? textSelectionControls : null,
|
||||||
|
focusNode: _focusNodes[i - 1],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
|
final FormInputStatus formInputStatus;
|
||||||
|
|
||||||
if (value.isEmpty) {
|
if (value.isEmpty) {
|
||||||
setState(() {
|
formInputStatus =
|
||||||
_inputStatuses[i - 1] =
|
FormInputStatus.empty;
|
||||||
FormInputStatus.empty;
|
|
||||||
});
|
|
||||||
} else if (_isValidMnemonicWord(
|
} else if (_isValidMnemonicWord(
|
||||||
value.trim().toLowerCase())) {
|
value.trim().toLowerCase())) {
|
||||||
setState(() {
|
formInputStatus =
|
||||||
_inputStatuses[i - 1] =
|
FormInputStatus.valid;
|
||||||
FormInputStatus.valid;
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
setState(() {
|
formInputStatus =
|
||||||
_inputStatuses[i - 1] =
|
FormInputStatus.invalid;
|
||||||
FormInputStatus.invalid;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (formInputStatus ==
|
||||||
|
FormInputStatus.valid) {
|
||||||
|
if (i < _focusNodes.length) {
|
||||||
|
_focusNodes[i].requestFocus();
|
||||||
|
} else if (i == _focusNodes.length) {
|
||||||
|
_focusNodes[i - 1].unfocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_inputStatuses[i - 1] =
|
||||||
|
formInputStatus;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
controller: _controllers[i - 1],
|
controller: _controllers[i - 1],
|
||||||
style:
|
style:
|
||||||
|
|
|
@ -302,7 +302,7 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
...contacts
|
...contacts
|
||||||
.where((element) => element.addresses
|
.where((element) => element.addressesSorted
|
||||||
.where((e) => ref.watch(addressBookFilterProvider
|
.where((e) => ref.watch(addressBookFilterProvider
|
||||||
.select((value) => value.coins.contains(e.coin))))
|
.select((value) => value.coins.contains(e.coin))))
|
||||||
.isNotEmpty)
|
.isNotEmpty)
|
||||||
|
@ -350,7 +350,7 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
...contacts
|
...contacts
|
||||||
.where((element) => element.addresses
|
.where((element) => element.addressesSorted
|
||||||
.where((e) => ref.watch(
|
.where((e) => ref.watch(
|
||||||
addressBookFilterProvider.select((value) =>
|
addressBookFilterProvider.select((value) =>
|
||||||
value.coins.contains(e.coin))))
|
value.coins.contains(e.coin))))
|
||||||
|
|
|
@ -211,7 +211,8 @@ class _AddNewContactAddressViewState
|
||||||
const Duration(milliseconds: 75),
|
const Duration(milliseconds: 75),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
List<ContactAddressEntry> entries = contact.addresses;
|
List<ContactAddressEntry> entries =
|
||||||
|
contact.addresses.toList();
|
||||||
|
|
||||||
entries.add(ref
|
entries.add(ref
|
||||||
.read(addressEntryDataProvider(0))
|
.read(addressEntryDataProvider(0))
|
||||||
|
|
|
@ -341,7 +341,7 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
|
||||||
padding: const EdgeInsets.all(0),
|
padding: const EdgeInsets.all(0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
..._contact.addresses.map(
|
..._contact.addressesSorted.map(
|
||||||
(e) => Padding(
|
(e) => Padding(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|
|
@ -63,7 +63,7 @@ class ContactPopUp extends ConsumerWidget {
|
||||||
bool isExchangeFlow =
|
bool isExchangeFlow =
|
||||||
ref.watch(exchangeFlowIsActiveStateProvider.state).state;
|
ref.watch(exchangeFlowIsActiveStateProvider.state).state;
|
||||||
|
|
||||||
final addresses = contact.addresses.where((e) {
|
final addresses = contact.addressesSorted.where((e) {
|
||||||
if (hasActiveWallet && !isExchangeFlow) {
|
if (hasActiveWallet && !isExchangeFlow) {
|
||||||
return e.coin == active[0].coin;
|
return e.coin == active[0].coin;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -9,9 +9,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stackwallet/models/buy/response_objects/order.dart';
|
import 'package:stackwallet/models/buy/response_objects/order.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/themes/theme_providers.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
@ -21,7 +23,7 @@ import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||||
|
|
||||||
class BuyOrderDetailsView extends StatefulWidget {
|
class BuyOrderDetailsView extends ConsumerStatefulWidget {
|
||||||
const BuyOrderDetailsView({
|
const BuyOrderDetailsView({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.order,
|
required this.order,
|
||||||
|
@ -32,10 +34,11 @@ class BuyOrderDetailsView extends StatefulWidget {
|
||||||
static const String routeName = "/buyOrderDetails";
|
static const String routeName = "/buyOrderDetails";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<BuyOrderDetailsView> createState() => _BuyOrderDetailsViewState();
|
ConsumerState<BuyOrderDetailsView> createState() =>
|
||||||
|
_BuyOrderDetailsViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BuyOrderDetailsViewState extends State<BuyOrderDetailsView> {
|
class _BuyOrderDetailsViewState extends ConsumerState<BuyOrderDetailsView> {
|
||||||
final isDesktop = Util.isDesktop;
|
final isDesktop = Util.isDesktop;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -245,7 +248,9 @@ class _BuyOrderDetailsViewState extends State<BuyOrderDetailsView> {
|
||||||
width: 64,
|
width: 64,
|
||||||
height: 32,
|
height: 32,
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
Assets.buy.simplexLogo(context),
|
Assets.buy.simplexLogo(
|
||||||
|
ref.watch(themeProvider).brightness,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -11,11 +11,13 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:stackwallet/models/buy/response_objects/quote.dart';
|
import 'package:stackwallet/models/buy/response_objects/quote.dart';
|
||||||
import 'package:stackwallet/pages/buy_view/sub_widgets/buy_warning_popup.dart';
|
import 'package:stackwallet/pages/buy_view/sub_widgets/buy_warning_popup.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/themes/theme_providers.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
@ -25,7 +27,7 @@ import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||||
|
|
||||||
class BuyQuotePreviewView extends StatefulWidget {
|
class BuyQuotePreviewView extends ConsumerStatefulWidget {
|
||||||
const BuyQuotePreviewView({
|
const BuyQuotePreviewView({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.quote,
|
required this.quote,
|
||||||
|
@ -36,10 +38,11 @@ class BuyQuotePreviewView extends StatefulWidget {
|
||||||
static const String routeName = "/buyQuotePreview";
|
static const String routeName = "/buyQuotePreview";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<BuyQuotePreviewView> createState() => _BuyQuotePreviewViewState();
|
ConsumerState<BuyQuotePreviewView> createState() =>
|
||||||
|
_BuyQuotePreviewViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BuyQuotePreviewViewState extends State<BuyQuotePreviewView> {
|
class _BuyQuotePreviewViewState extends ConsumerState<BuyQuotePreviewView> {
|
||||||
final isDesktop = Util.isDesktop;
|
final isDesktop = Util.isDesktop;
|
||||||
|
|
||||||
Future<void> _buyWarning() async {
|
Future<void> _buyWarning() async {
|
||||||
|
@ -222,7 +225,9 @@ class _BuyQuotePreviewViewState extends State<BuyQuotePreviewView> {
|
||||||
width: 64,
|
width: 64,
|
||||||
height: 32,
|
height: 32,
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
Assets.buy.simplexLogo(context),
|
Assets.buy.simplexLogo(
|
||||||
|
ref.watch(themeProvider).brightness,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stackwallet/models/buy/response_objects/order.dart';
|
import 'package:stackwallet/models/buy/response_objects/order.dart';
|
||||||
import 'package:stackwallet/models/buy/response_objects/quote.dart';
|
import 'package:stackwallet/models/buy/response_objects/quote.dart';
|
||||||
|
@ -18,6 +19,7 @@ import 'package:stackwallet/pages/buy_view/buy_order_details.dart';
|
||||||
import 'package:stackwallet/services/buy/buy_response.dart';
|
import 'package:stackwallet/services/buy/buy_response.dart';
|
||||||
import 'package:stackwallet/services/buy/simplex/simplex_api.dart';
|
import 'package:stackwallet/services/buy/simplex/simplex_api.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/themes/theme_providers.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
@ -28,7 +30,7 @@ import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||||
|
|
||||||
class BuyWarningPopup extends StatefulWidget {
|
class BuyWarningPopup extends ConsumerStatefulWidget {
|
||||||
const BuyWarningPopup({
|
const BuyWarningPopup({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.quote,
|
required this.quote,
|
||||||
|
@ -37,10 +39,10 @@ class BuyWarningPopup extends StatefulWidget {
|
||||||
final SimplexQuote quote;
|
final SimplexQuote quote;
|
||||||
final SimplexOrder? order;
|
final SimplexOrder? order;
|
||||||
@override
|
@override
|
||||||
State<BuyWarningPopup> createState() => _BuyWarningPopupState();
|
ConsumerState<BuyWarningPopup> createState() => _BuyWarningPopupState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BuyWarningPopupState extends State<BuyWarningPopup> {
|
class _BuyWarningPopupState extends ConsumerState<BuyWarningPopup> {
|
||||||
late final bool isDesktop;
|
late final bool isDesktop;
|
||||||
SimplexOrder? order;
|
SimplexOrder? order;
|
||||||
|
|
||||||
|
@ -236,7 +238,9 @@ class _BuyWarningPopupState extends State<BuyWarningPopup> {
|
||||||
width: 64,
|
width: 64,
|
||||||
height: 32,
|
height: 32,
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
Assets.buy.simplexLogo(context),
|
Assets.buy.simplexLogo(
|
||||||
|
ref.watch(themeProvider).brightness,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -291,7 +295,9 @@ class _BuyWarningPopupState extends State<BuyWarningPopup> {
|
||||||
width: 64,
|
width: 64,
|
||||||
height: 32,
|
height: 32,
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
Assets.buy.simplexLogo(context),
|
Assets.buy.simplexLogo(
|
||||||
|
ref.watch(themeProvider).brightness,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -35,6 +35,7 @@ import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart
|
||||||
import 'package:stackwallet/services/exchange/majestic_bank/majestic_bank_exchange.dart';
|
import 'package:stackwallet/services/exchange/majestic_bank/majestic_bank_exchange.dart';
|
||||||
import 'package:stackwallet/services/exchange/trocador/trocador_exchange.dart';
|
import 'package:stackwallet/services/exchange/trocador/trocador_exchange.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/amount/amount_unit.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
@ -160,26 +161,15 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
// wtf Dart?????
|
|
||||||
// This turns "99999999999999999999" into 100000000000000000000.0
|
|
||||||
// final numFromLocalised = NumberFormat.decimalPattern(
|
|
||||||
// ref.read(localeServiceChangeNotifierProvider).locale)
|
|
||||||
// .parse(value);
|
|
||||||
// return Decimal.tryParse(numFromLocalised.toString());
|
|
||||||
|
|
||||||
try {
|
return AmountUnit.normal
|
||||||
return Decimal.parse(value);
|
.tryParse(
|
||||||
} catch (_) {
|
value,
|
||||||
try {
|
locale: ref.read(localeServiceChangeNotifierProvider).locale,
|
||||||
return Decimal.parse(value.replaceAll(",", "."));
|
coin: Coin.bitcoin, // dummy value (not used due to override)
|
||||||
} catch (_) {
|
overrideWithDecimalPlacesFromString: true,
|
||||||
rethrow;
|
)
|
||||||
}
|
?.decimal;
|
||||||
}
|
|
||||||
} catch (_) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<AggregateCurrency> _getAggregateCurrency(Currency currency) async {
|
Future<AggregateCurrency> _getAggregateCurrency(Currency currency) async {
|
||||||
|
@ -809,6 +799,14 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
||||||
// if (_swapLock) {
|
// if (_swapLock) {
|
||||||
_sendController.text = ref.read(efSendAmountStringProvider);
|
_sendController.text = ref.read(efSendAmountStringProvider);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
if (_sendFocusNode.hasFocus) {
|
||||||
|
_sendController.selection = TextSelection.fromPosition(
|
||||||
|
TextPosition(
|
||||||
|
offset: _sendController.text.length,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ref.listen(efSendAmountStringProvider, (previous, String next) {
|
ref.listen(efSendAmountStringProvider, (previous, String next) {
|
||||||
|
@ -820,11 +818,19 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
||||||
? "-"
|
? "-"
|
||||||
: ref.read(efReceiveAmountStringProvider);
|
: ref.read(efReceiveAmountStringProvider);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
if (_receiveFocusNode.hasFocus) {
|
||||||
|
_receiveController.selection = TextSelection.fromPosition(
|
||||||
|
TextPosition(
|
||||||
|
offset: _receiveController.text.length,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ref.listen(efEstimateProvider.notifier, (previous, next) {
|
ref.listen(efEstimateProvider.notifier, (previous, next) {
|
||||||
final estimate = (next as StateController<Estimate?>).state;
|
final estimate = (next).state;
|
||||||
if (ref.read(efReversedProvider)) {
|
if (ref.read(efReversedProvider)) {
|
||||||
updateSend(estimate);
|
updateSend(estimate);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -22,6 +22,7 @@ import 'package:stackwallet/pages/receive_view/generate_receiving_uri_qr_code_vi
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
import 'package:stackwallet/route_generator.dart';
|
import 'package:stackwallet/route_generator.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/address_utils.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
|
@ -337,7 +338,11 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
QrImageView(
|
QrImageView(
|
||||||
data: "${coin.uriScheme}:$receivingAddress",
|
data: AddressUtils.buildUriString(
|
||||||
|
coin,
|
||||||
|
receivingAddress,
|
||||||
|
{},
|
||||||
|
),
|
||||||
size: MediaQuery.of(context).size.width / 2,
|
size: MediaQuery.of(context).size.width / 2,
|
||||||
foregroundColor: Theme.of(context)
|
foregroundColor: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
|
|
|
@ -468,27 +468,79 @@ class _ConfirmTransactionViewState
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
if (transactionInfo["fee"] is int &&
|
||||||
height: 12,
|
transactionInfo["vSize"] is int)
|
||||||
),
|
const SizedBox(
|
||||||
RoundedWhiteContainer(
|
height: 12,
|
||||||
child: Column(
|
),
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
if (transactionInfo["fee"] is int &&
|
||||||
children: [
|
transactionInfo["vSize"] is int)
|
||||||
Text(
|
RoundedWhiteContainer(
|
||||||
"Note",
|
child: Row(
|
||||||
style: STextStyles.smallMed12(context),
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
),
|
children: [
|
||||||
const SizedBox(
|
Text(
|
||||||
height: 4,
|
"sats/vByte",
|
||||||
),
|
style: STextStyles.smallMed12(context),
|
||||||
Text(
|
),
|
||||||
transactionInfo["note"] as String,
|
const SizedBox(
|
||||||
style: STextStyles.itemSubtitle12(context),
|
height: 4,
|
||||||
),
|
),
|
||||||
],
|
Text(
|
||||||
|
"~${(transactionInfo["fee"] / transactionInfo["vSize"]).toInt()}",
|
||||||
|
style: STextStyles.itemSubtitle12(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (coin == Coin.epicCash &&
|
||||||
|
(transactionInfo["onChainNote"] as String).isNotEmpty)
|
||||||
|
const SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
if (coin == Coin.epicCash &&
|
||||||
|
(transactionInfo["onChainNote"] as String).isNotEmpty)
|
||||||
|
RoundedWhiteContainer(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"On chain note",
|
||||||
|
style: STextStyles.smallMed12(context),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
transactionInfo["onChainNote"] as String,
|
||||||
|
style: STextStyles.itemSubtitle12(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if ((transactionInfo["note"] as String).isNotEmpty)
|
||||||
|
const SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
if ((transactionInfo["note"] as String).isNotEmpty)
|
||||||
|
RoundedWhiteContainer(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
(coin == Coin.epicCash) ? "Local Note" : "Note",
|
||||||
|
style: STextStyles.smallMed12(context),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
transactionInfo["note"] as String,
|
||||||
|
style: STextStyles.itemSubtitle12(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (isDesktop)
|
if (isDesktop)
|
||||||
|
@ -906,6 +958,43 @@ class _ConfirmTransactionViewState
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (isDesktop &&
|
||||||
|
!widget.isPaynymTransaction &&
|
||||||
|
transactionInfo["fee"] is int &&
|
||||||
|
transactionInfo["vSize"] is int)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 32,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
"sats/vByte",
|
||||||
|
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (isDesktop &&
|
||||||
|
!widget.isPaynymTransaction &&
|
||||||
|
transactionInfo["fee"] is int &&
|
||||||
|
transactionInfo["vSize"] is int)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 10,
|
||||||
|
left: 32,
|
||||||
|
right: 32,
|
||||||
|
),
|
||||||
|
child: RoundedContainer(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 18,
|
||||||
|
),
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFieldDefaultBG,
|
||||||
|
child: Text(
|
||||||
|
"~${(transactionInfo["fee"] / transactionInfo["vSize"]).toInt()}",
|
||||||
|
style: STextStyles.itemSubtitle(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
if (!isDesktop) const Spacer(),
|
if (!isDesktop) const Spacer(),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: isDesktop ? 23 : 12,
|
height: isDesktop ? 23 : 12,
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:bip47/bip47.dart';
|
import 'package:bip47/bip47.dart';
|
||||||
import 'package:cw_core/monero_transaction_priority.dart';
|
import 'package:cw_core/monero_transaction_priority.dart';
|
||||||
|
@ -41,6 +40,7 @@ import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/address_utils.dart';
|
import 'package:stackwallet/utilities/address_utils.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
||||||
|
import 'package:stackwallet/utilities/amount/amount_input_formatter.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount_unit.dart';
|
import 'package:stackwallet/utilities/amount/amount_unit.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
||||||
|
@ -56,6 +56,7 @@ import 'package:stackwallet/widgets/animated_text.dart';
|
||||||
import 'package:stackwallet/widgets/background.dart';
|
import 'package:stackwallet/widgets/background.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/fee_slider.dart';
|
||||||
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
|
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
|
||||||
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
|
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
|
||||||
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
|
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
|
||||||
|
@ -100,12 +101,14 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
late TextEditingController cryptoAmountController;
|
late TextEditingController cryptoAmountController;
|
||||||
late TextEditingController baseAmountController;
|
late TextEditingController baseAmountController;
|
||||||
late TextEditingController noteController;
|
late TextEditingController noteController;
|
||||||
|
late TextEditingController onChainNoteController;
|
||||||
late TextEditingController feeController;
|
late TextEditingController feeController;
|
||||||
|
|
||||||
late final SendViewAutoFillData? _data;
|
late final SendViewAutoFillData? _data;
|
||||||
|
|
||||||
final _addressFocusNode = FocusNode();
|
final _addressFocusNode = FocusNode();
|
||||||
final _noteFocusNode = FocusNode();
|
final _noteFocusNode = FocusNode();
|
||||||
|
final _onChainNoteFocusNode = FocusNode();
|
||||||
final _cryptoFocus = FocusNode();
|
final _cryptoFocus = FocusNode();
|
||||||
final _baseFocus = FocusNode();
|
final _baseFocus = FocusNode();
|
||||||
|
|
||||||
|
@ -127,27 +130,11 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
|
|
||||||
void _cryptoAmountChanged() async {
|
void _cryptoAmountChanged() async {
|
||||||
if (!_cryptoAmountChangeLock) {
|
if (!_cryptoAmountChangeLock) {
|
||||||
String cryptoAmount = cryptoAmountController.text;
|
final cryptoAmount = ref.read(pAmountFormatter(coin)).tryParse(
|
||||||
if (cryptoAmount.isNotEmpty &&
|
cryptoAmountController.text,
|
||||||
cryptoAmount != "." &&
|
);
|
||||||
cryptoAmount != ",") {
|
if (cryptoAmount != null) {
|
||||||
if (cryptoAmount.startsWith("~")) {
|
_amountToSend = cryptoAmount;
|
||||||
cryptoAmount = cryptoAmount.substring(1);
|
|
||||||
}
|
|
||||||
if (cryptoAmount.contains(" ")) {
|
|
||||||
cryptoAmount = cryptoAmount.split(" ").first;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure we don't shift past minimum atomic value
|
|
||||||
final shift = min(ref.read(pAmountUnit(coin)).shift, coin.decimals);
|
|
||||||
|
|
||||||
_amountToSend = cryptoAmount.contains(",")
|
|
||||||
? Decimal.parse(cryptoAmount.replaceFirst(",", "."))
|
|
||||||
.shift(0 - shift)
|
|
||||||
.toAmount(fractionDigits: coin.decimals)
|
|
||||||
: Decimal.parse(cryptoAmount)
|
|
||||||
.shift(0 - shift)
|
|
||||||
.toAmount(fractionDigits: coin.decimals);
|
|
||||||
if (_cachedAmountToSend != null &&
|
if (_cachedAmountToSend != null &&
|
||||||
_cachedAmountToSend == _amountToSend) {
|
_cachedAmountToSend == _amountToSend) {
|
||||||
return;
|
return;
|
||||||
|
@ -300,6 +287,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
case FeeRateType.slow:
|
case FeeRateType.slow:
|
||||||
feeRate = feeObject.slow;
|
feeRate = feeObject.slow;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
feeRate = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Amount fee;
|
Amount fee;
|
||||||
|
@ -315,6 +304,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
case FeeRateType.slow:
|
case FeeRateType.slow:
|
||||||
specialMoneroId = MoneroTransactionPriority.slow;
|
specialMoneroId = MoneroTransactionPriority.slow;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
throw ArgumentError("custom fee not available for monero");
|
||||||
}
|
}
|
||||||
|
|
||||||
fee = await manager.estimateFeeFor(amount, specialMoneroId.raw!);
|
fee = await manager.estimateFeeFor(amount, specialMoneroId.raw!);
|
||||||
|
@ -510,6 +501,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
isSegwit: widget.accountLite!.segwit,
|
isSegwit: widget.accountLite!.segwit,
|
||||||
amount: amount,
|
amount: amount,
|
||||||
args: {
|
args: {
|
||||||
|
"satsPerVByte": isCustomFee ? customFeeRate : null,
|
||||||
"feeRate": feeRate,
|
"feeRate": feeRate,
|
||||||
"UTXOs": (manager.hasCoinControlSupport &&
|
"UTXOs": (manager.hasCoinControlSupport &&
|
||||||
coinControlEnabled &&
|
coinControlEnabled &&
|
||||||
|
@ -524,7 +516,10 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
txDataFuture = (manager.wallet as FiroWallet).prepareSendPublic(
|
txDataFuture = (manager.wallet as FiroWallet).prepareSendPublic(
|
||||||
address: _address!,
|
address: _address!,
|
||||||
amount: amount,
|
amount: amount,
|
||||||
args: {"feeRate": ref.read(feeRateTypeStateProvider)},
|
args: {
|
||||||
|
"feeRate": ref.read(feeRateTypeStateProvider),
|
||||||
|
"satsPerVByte": isCustomFee ? customFeeRate : null,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
txDataFuture = manager.prepareSend(
|
txDataFuture = manager.prepareSend(
|
||||||
|
@ -532,6 +527,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
amount: amount,
|
amount: amount,
|
||||||
args: {
|
args: {
|
||||||
"feeRate": ref.read(feeRateTypeStateProvider),
|
"feeRate": ref.read(feeRateTypeStateProvider),
|
||||||
|
"satsPerVByte": isCustomFee ? customFeeRate : null,
|
||||||
"UTXOs": (manager.hasCoinControlSupport &&
|
"UTXOs": (manager.hasCoinControlSupport &&
|
||||||
coinControlEnabled &&
|
coinControlEnabled &&
|
||||||
selectedUTXOs.isNotEmpty)
|
selectedUTXOs.isNotEmpty)
|
||||||
|
@ -552,6 +548,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
// pop building dialog
|
// pop building dialog
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
txData["note"] = noteController.text;
|
txData["note"] = noteController.text;
|
||||||
|
txData["onChainNote"] = onChainNoteController.text;
|
||||||
if (isPaynymSend) {
|
if (isPaynymSend) {
|
||||||
txData["paynymAccountLite"] = widget.accountLite!;
|
txData["paynymAccountLite"] = widget.accountLite!;
|
||||||
} else {
|
} else {
|
||||||
|
@ -609,6 +606,10 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
|
|
||||||
bool get isPaynymSend => widget.accountLite != null;
|
bool get isPaynymSend => widget.accountLite != null;
|
||||||
|
|
||||||
|
bool isCustomFee = false;
|
||||||
|
|
||||||
|
int customFeeRate = 1;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
coin = widget.coin;
|
coin = widget.coin;
|
||||||
|
@ -626,6 +627,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
cryptoAmountController = TextEditingController();
|
cryptoAmountController = TextEditingController();
|
||||||
baseAmountController = TextEditingController();
|
baseAmountController = TextEditingController();
|
||||||
noteController = TextEditingController();
|
noteController = TextEditingController();
|
||||||
|
onChainNoteController = TextEditingController();
|
||||||
feeController = TextEditingController();
|
feeController = TextEditingController();
|
||||||
|
|
||||||
onCryptoAmountChanged = _cryptoAmountChanged;
|
onCryptoAmountChanged = _cryptoAmountChanged;
|
||||||
|
@ -700,9 +702,11 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
cryptoAmountController.dispose();
|
cryptoAmountController.dispose();
|
||||||
baseAmountController.dispose();
|
baseAmountController.dispose();
|
||||||
noteController.dispose();
|
noteController.dispose();
|
||||||
|
onChainNoteController.dispose();
|
||||||
feeController.dispose();
|
feeController.dispose();
|
||||||
|
|
||||||
_noteFocusNode.dispose();
|
_noteFocusNode.dispose();
|
||||||
|
_onChainNoteFocusNode.dispose();
|
||||||
_addressFocusNode.dispose();
|
_addressFocusNode.dispose();
|
||||||
_cryptoFocus.dispose();
|
_cryptoFocus.dispose();
|
||||||
_baseFocus.dispose();
|
_baseFocus.dispose();
|
||||||
|
@ -1553,13 +1557,21 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
|
AmountInputFormatter(
|
||||||
|
decimals: coin.decimals,
|
||||||
|
unit: ref.watch(pAmountUnit(coin)),
|
||||||
|
locale: locale,
|
||||||
|
),
|
||||||
|
|
||||||
// regex to validate a crypto amount with 8 decimal places
|
// regex to validate a crypto amount with 8 decimal places
|
||||||
TextInputFormatter.withFunction((oldValue,
|
// TextInputFormatter.withFunction((oldValue,
|
||||||
newValue) =>
|
// newValue) =>
|
||||||
RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
|
// // RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
|
||||||
.hasMatch(newValue.text)
|
// // RegExp(r'^\d{1,3}([,\.]\d+)?|[,\.\d]+$')
|
||||||
? newValue
|
// getAmountRegex(locale, coin.decimals)
|
||||||
: oldValue),
|
// .hasMatch(newValue.text)
|
||||||
|
// ? newValue
|
||||||
|
// : oldValue),
|
||||||
],
|
],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
contentPadding: const EdgeInsets.only(
|
contentPadding: const EdgeInsets.only(
|
||||||
|
@ -1614,26 +1626,25 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
|
AmountInputFormatter(
|
||||||
|
decimals: 2,
|
||||||
|
locale: locale,
|
||||||
|
),
|
||||||
// regex to validate a fiat amount with 2 decimal places
|
// regex to validate a fiat amount with 2 decimal places
|
||||||
TextInputFormatter.withFunction((oldValue,
|
// TextInputFormatter.withFunction((oldValue,
|
||||||
newValue) =>
|
// newValue) =>
|
||||||
RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
|
// // RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
|
||||||
.hasMatch(newValue.text)
|
// getAmountRegex(locale, 2)
|
||||||
? newValue
|
// .hasMatch(newValue.text)
|
||||||
: oldValue),
|
// ? newValue
|
||||||
|
// : oldValue),
|
||||||
],
|
],
|
||||||
onChanged: (baseAmountString) {
|
onChanged: (baseAmountString) {
|
||||||
if (baseAmountString.isNotEmpty &&
|
final baseAmount = Amount.tryParseFiatString(
|
||||||
baseAmountString != "." &&
|
baseAmountString,
|
||||||
baseAmountString != ",") {
|
locale: locale,
|
||||||
final Amount baseAmount =
|
);
|
||||||
baseAmountString.contains(",")
|
if (baseAmount != null) {
|
||||||
? Decimal.parse(baseAmountString
|
|
||||||
.replaceFirst(",", "."))
|
|
||||||
.toAmount(fractionDigits: 2)
|
|
||||||
: Decimal.parse(baseAmountString)
|
|
||||||
.toAmount(fractionDigits: 2);
|
|
||||||
|
|
||||||
final Decimal _price = ref
|
final Decimal _price = ref
|
||||||
.read(priceAnd24hChangeNotifierProvider)
|
.read(priceAnd24hChangeNotifierProvider)
|
||||||
.getPrice(coin)
|
.getPrice(coin)
|
||||||
|
@ -1789,8 +1800,64 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 12,
|
height: 12,
|
||||||
),
|
),
|
||||||
|
if (coin == Coin.epicCash)
|
||||||
|
Text(
|
||||||
|
"On chain Note (optional)",
|
||||||
|
style: STextStyles.smallMed12(context),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
if (coin == Coin.epicCash)
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
if (coin == Coin.epicCash)
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(
|
||||||
|
Constants.size.circularBorderRadius,
|
||||||
|
),
|
||||||
|
child: TextField(
|
||||||
|
autocorrect: Util.isDesktop ? false : true,
|
||||||
|
enableSuggestions: Util.isDesktop ? false : true,
|
||||||
|
maxLength: 256,
|
||||||
|
controller: onChainNoteController,
|
||||||
|
focusNode: _onChainNoteFocusNode,
|
||||||
|
style: STextStyles.field(context),
|
||||||
|
onChanged: (_) => setState(() {}),
|
||||||
|
decoration: standardInputDecoration(
|
||||||
|
"Type something...",
|
||||||
|
_onChainNoteFocusNode,
|
||||||
|
context,
|
||||||
|
).copyWith(
|
||||||
|
suffixIcon: onChainNoteController.text.isNotEmpty
|
||||||
|
? Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.only(right: 0),
|
||||||
|
child: UnconstrainedBox(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
TextFieldIconButton(
|
||||||
|
child: const XIcon(),
|
||||||
|
onTap: () async {
|
||||||
|
setState(() {
|
||||||
|
onChainNoteController.text = "";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (coin == Coin.epicCash)
|
||||||
|
const SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
"Note (optional)",
|
(coin == Coin.epicCash) ? "Local Note (optional)"
|
||||||
|
: "Note (optional)",
|
||||||
style: STextStyles.smallMed12(context),
|
style: STextStyles.smallMed12(context),
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
|
@ -1839,17 +1906,23 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 12,
|
height: 12,
|
||||||
),
|
),
|
||||||
if (coin != Coin.epicCash)
|
if (coin != Coin.epicCash &&
|
||||||
|
coin != Coin.nano &&
|
||||||
|
coin != Coin.banano)
|
||||||
Text(
|
Text(
|
||||||
"Transaction fee (estimated)",
|
"Transaction fee (estimated)",
|
||||||
style: STextStyles.smallMed12(context),
|
style: STextStyles.smallMed12(context),
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
if (coin != Coin.epicCash)
|
if (coin != Coin.epicCash &&
|
||||||
|
coin != Coin.nano &&
|
||||||
|
coin != Coin.banano)
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 8,
|
height: 8,
|
||||||
),
|
),
|
||||||
if (coin != Coin.epicCash)
|
if (coin != Coin.epicCash &&
|
||||||
|
coin != Coin.nano &&
|
||||||
|
coin != Coin.banano)
|
||||||
Stack(
|
Stack(
|
||||||
children: [
|
children: [
|
||||||
TextField(
|
TextField(
|
||||||
|
@ -1907,6 +1980,15 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
fractionDigits: coin.decimals,
|
fractionDigits: coin.decimals,
|
||||||
),
|
),
|
||||||
updateChosen: (String fee) {
|
updateChosen: (String fee) {
|
||||||
|
if (fee == "custom") {
|
||||||
|
if (!isCustomFee) {
|
||||||
|
setState(() {
|
||||||
|
isCustomFee = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_setCurrentFee(
|
_setCurrentFee(
|
||||||
fee,
|
fee,
|
||||||
true,
|
true,
|
||||||
|
@ -1914,6 +1996,9 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
setState(() {
|
setState(() {
|
||||||
_calculateFeesFuture =
|
_calculateFeesFuture =
|
||||||
Future(() => fee);
|
Future(() => fee);
|
||||||
|
if (isCustomFee) {
|
||||||
|
isCustomFee = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -1937,11 +2022,11 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
.done &&
|
.done &&
|
||||||
snapshot.hasData) {
|
snapshot.hasData) {
|
||||||
_setCurrentFee(
|
_setCurrentFee(
|
||||||
snapshot.data! as String,
|
snapshot.data!,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
return Text(
|
return Text(
|
||||||
"~${snapshot.data! as String}",
|
"~${snapshot.data!}",
|
||||||
style: STextStyles
|
style: STextStyles
|
||||||
.itemSubtitle(
|
.itemSubtitle(
|
||||||
context),
|
context),
|
||||||
|
@ -1993,12 +2078,13 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
.done &&
|
.done &&
|
||||||
snapshot.hasData) {
|
snapshot.hasData) {
|
||||||
_setCurrentFee(
|
_setCurrentFee(
|
||||||
snapshot.data!
|
snapshot.data!,
|
||||||
as String,
|
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
return Text(
|
return Text(
|
||||||
"~${snapshot.data! as String}",
|
isCustomFee
|
||||||
|
? ""
|
||||||
|
: "~${snapshot.data!}",
|
||||||
style: STextStyles
|
style: STextStyles
|
||||||
.itemSubtitle(
|
.itemSubtitle(
|
||||||
context),
|
context),
|
||||||
|
@ -2034,6 +2120,19 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
if (isCustomFee)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
bottom: 12,
|
||||||
|
top: 16,
|
||||||
|
),
|
||||||
|
child: FeeSlider(
|
||||||
|
coin: coin,
|
||||||
|
onSatVByteChanged: (rate) {
|
||||||
|
customFeeRate = rate;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 12,
|
height: 12,
|
||||||
|
|
|
@ -161,6 +161,9 @@ class _TransactionFeeSelectionSheetState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ref.read(feeSheetSessionCacheProvider).slow[amount]!;
|
return ref.read(feeSheetSessionCacheProvider).slow[amount]!;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Amount.zero;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -567,8 +570,6 @@ class _TransactionFeeSelectionSheetState
|
||||||
.watch(feeRateTypeStateProvider.state)
|
.watch(feeRateTypeStateProvider.state)
|
||||||
.state,
|
.state,
|
||||||
onChanged: (x) {
|
onChanged: (x) {
|
||||||
//todo: check if print needed
|
|
||||||
// debugPrint(x.toString());
|
|
||||||
ref
|
ref
|
||||||
.read(feeRateTypeStateProvider.state)
|
.read(feeRateTypeStateProvider.state)
|
||||||
.state = FeeRateType.slow;
|
.state = FeeRateType.slow;
|
||||||
|
@ -672,6 +673,79 @@ class _TransactionFeeSelectionSheetState
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 24,
|
height: 24,
|
||||||
),
|
),
|
||||||
|
if (manager.coin.isElectrumXCoin)
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
final state =
|
||||||
|
ref.read(feeRateTypeStateProvider.state).state;
|
||||||
|
if (state != FeeRateType.custom) {
|
||||||
|
ref.read(feeRateTypeStateProvider.state).state =
|
||||||
|
FeeRateType.custom;
|
||||||
|
}
|
||||||
|
widget.updateChosen("custom");
|
||||||
|
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
child: Radio(
|
||||||
|
activeColor: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.radioButtonIconEnabled,
|
||||||
|
value: FeeRateType.custom,
|
||||||
|
groupValue: ref
|
||||||
|
.watch(feeRateTypeStateProvider.state)
|
||||||
|
.state,
|
||||||
|
onChanged: (x) {
|
||||||
|
ref
|
||||||
|
.read(
|
||||||
|
feeRateTypeStateProvider.state)
|
||||||
|
.state = FeeRateType.custom;
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 12,
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
FeeRateType.custom.prettyName,
|
||||||
|
style:
|
||||||
|
STextStyles.titleBold12(context),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 2,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (manager.coin.isElectrumXCoin)
|
||||||
|
const SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -714,6 +788,8 @@ class _TransactionFeeSelectionSheetState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
case FeeRateType.custom:
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logging.instance.log("$e $s", level: LogLevel.Warning);
|
Logging.instance.log("$e $s", level: LogLevel.Warning);
|
||||||
|
|
|
@ -31,6 +31,7 @@ import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/address_utils.dart';
|
import 'package:stackwallet/utilities/address_utils.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
||||||
|
import 'package:stackwallet/utilities/amount/amount_input_formatter.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
||||||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||||
|
@ -218,16 +219,11 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onFiatAmountFieldChanged(String baseAmountString) {
|
void _onFiatAmountFieldChanged(String baseAmountString) {
|
||||||
if (baseAmountString.isNotEmpty &&
|
final baseAmount = Amount.tryParseFiatString(
|
||||||
baseAmountString != "." &&
|
baseAmountString,
|
||||||
baseAmountString != ",") {
|
locale: ref.read(localeServiceChangeNotifierProvider).locale,
|
||||||
final baseAmount = Amount.fromDecimal(
|
);
|
||||||
baseAmountString.contains(",")
|
if (baseAmount != null) {
|
||||||
? Decimal.parse(baseAmountString.replaceFirst(",", "."))
|
|
||||||
: Decimal.parse(baseAmountString),
|
|
||||||
fractionDigits: tokenContract.decimals,
|
|
||||||
);
|
|
||||||
|
|
||||||
final _price = ref
|
final _price = ref
|
||||||
.read(priceAnd24hChangeNotifierProvider)
|
.read(priceAnd24hChangeNotifierProvider)
|
||||||
.getTokenPrice(tokenContract.address)
|
.getTokenPrice(tokenContract.address)
|
||||||
|
@ -272,22 +268,12 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
||||||
|
|
||||||
void _cryptoAmountChanged() async {
|
void _cryptoAmountChanged() async {
|
||||||
if (!_cryptoAmountChangeLock) {
|
if (!_cryptoAmountChangeLock) {
|
||||||
String cryptoAmount = cryptoAmountController.text;
|
final cryptoAmount = ref.read(pAmountFormatter(coin)).tryParse(
|
||||||
if (cryptoAmount.isNotEmpty &&
|
cryptoAmountController.text,
|
||||||
cryptoAmount != "." &&
|
ethContract: tokenContract,
|
||||||
cryptoAmount != ",") {
|
);
|
||||||
if (cryptoAmount.startsWith("~")) {
|
if (cryptoAmount != null) {
|
||||||
cryptoAmount = cryptoAmount.substring(1);
|
_amountToSend = cryptoAmount;
|
||||||
}
|
|
||||||
if (cryptoAmount.contains(" ")) {
|
|
||||||
cryptoAmount = cryptoAmount.split(" ").first;
|
|
||||||
}
|
|
||||||
|
|
||||||
_amountToSend = Amount.fromDecimal(
|
|
||||||
cryptoAmount.contains(",")
|
|
||||||
? Decimal.parse(cryptoAmount.replaceFirst(",", "."))
|
|
||||||
: Decimal.parse(cryptoAmount),
|
|
||||||
fractionDigits: tokenContract.decimals);
|
|
||||||
if (_cachedAmountToSend != null &&
|
if (_cachedAmountToSend != null &&
|
||||||
_cachedAmountToSend == _amountToSend) {
|
_cachedAmountToSend == _amountToSend) {
|
||||||
return;
|
return;
|
||||||
|
@ -374,6 +360,8 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
||||||
case FeeRateType.slow:
|
case FeeRateType.slow:
|
||||||
feeRate = feeObject.slow;
|
feeRate = feeObject.slow;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
feeRate = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Amount fee = wallet.estimateFeeFor(feeRate);
|
final Amount fee = wallet.estimateFeeFor(feeRate);
|
||||||
|
@ -950,13 +938,18 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
// regex to validate a crypto amount with 8 decimal places
|
AmountInputFormatter(
|
||||||
TextInputFormatter.withFunction((oldValue,
|
decimals: tokenContract.decimals,
|
||||||
newValue) =>
|
unit: ref.watch(pAmountUnit(coin)),
|
||||||
RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
|
locale: locale,
|
||||||
.hasMatch(newValue.text)
|
),
|
||||||
? newValue
|
// // regex to validate a crypto amount with 8 decimal places
|
||||||
: oldValue),
|
// TextInputFormatter.withFunction((oldValue,
|
||||||
|
// newValue) =>
|
||||||
|
// RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
|
||||||
|
// .hasMatch(newValue.text)
|
||||||
|
// ? newValue
|
||||||
|
// : oldValue),
|
||||||
],
|
],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
contentPadding: const EdgeInsets.only(
|
contentPadding: const EdgeInsets.only(
|
||||||
|
@ -1009,13 +1002,17 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
// regex to validate a fiat amount with 2 decimal places
|
AmountInputFormatter(
|
||||||
TextInputFormatter.withFunction((oldValue,
|
decimals: 2,
|
||||||
newValue) =>
|
locale: locale,
|
||||||
RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
|
),
|
||||||
.hasMatch(newValue.text)
|
// // regex to validate a fiat amount with 2 decimal places
|
||||||
? newValue
|
// TextInputFormatter.withFunction((oldValue,
|
||||||
: oldValue),
|
// newValue) =>
|
||||||
|
// RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
|
||||||
|
// .hasMatch(newValue.text)
|
||||||
|
// ? newValue
|
||||||
|
// : oldValue),
|
||||||
],
|
],
|
||||||
onChanged: _onFiatAmountFieldChanged,
|
onChanged: _onFiatAmountFieldChanged,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
@ -1185,7 +1182,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
||||||
ConnectionState.done &&
|
ConnectionState.done &&
|
||||||
snapshot.hasData) {
|
snapshot.hasData) {
|
||||||
return Text(
|
return Text(
|
||||||
"~${snapshot.data! as String}",
|
"~${snapshot.data!}",
|
||||||
style:
|
style:
|
||||||
STextStyles.itemSubtitle(
|
STextStyles.itemSubtitle(
|
||||||
context),
|
context),
|
||||||
|
|
|
@ -126,7 +126,7 @@ class _StackThemeCardState extends ConsumerState<StackThemeCard> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getThemeDirectorySize() async {
|
Future<String> getThemeDirectorySize() async {
|
||||||
final themesDir = await StackFileSystem.applicationThemesDirectory();
|
final themesDir = StackFileSystem.themesDir!;
|
||||||
final themeDir = Directory("${themesDir.path}/${widget.data.id}");
|
final themeDir = Directory("${themesDir.path}/${widget.data.id}");
|
||||||
int bytes = 0;
|
int bytes = 0;
|
||||||
if (await themeDir.exists()) {
|
if (await themeDir.exists()) {
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stackwallet/db/hive/db.dart';
|
||||||
import 'package:stackwallet/pages/intro_view.dart';
|
import 'package:stackwallet/pages/intro_view.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/delete_everything.dart';
|
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
|
@ -36,7 +36,7 @@ class _DeleteAccountViewState extends State<DeleteAccountView> {
|
||||||
Future<void> onConfirmDeleteAccount() async {
|
Future<void> onConfirmDeleteAccount() async {
|
||||||
// TODO delete everything then pop to intro view
|
// TODO delete everything then pop to intro view
|
||||||
|
|
||||||
await showDialog(
|
await showDialog<void>(
|
||||||
barrierDismissible: true,
|
barrierDismissible: true,
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => StackDialog(
|
builder: (_) => StackDialog(
|
||||||
|
@ -61,12 +61,14 @@ class _DeleteAccountViewState extends State<DeleteAccountView> {
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.getPrimaryEnabledButtonStyle(context),
|
.getPrimaryEnabledButtonStyle(context),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await deleteEverything();
|
await DB.instance.deleteEverything();
|
||||||
|
|
||||||
await Navigator.of(context).pushNamedAndRemoveUntil(
|
if (mounted) {
|
||||||
IntroView.routeName,
|
await Navigator.of(context).pushNamedAndRemoveUntil(
|
||||||
(route) => false,
|
IntroView.routeName,
|
||||||
);
|
(route) => false,
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
"Delete",
|
"Delete",
|
||||||
|
@ -82,7 +84,7 @@ class _DeleteAccountViewState extends State<DeleteAccountView> {
|
||||||
return MasterScaffold(
|
return MasterScaffold(
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
appBar: isDesktop
|
appBar: isDesktop
|
||||||
? DesktopAppBar(isCompactHeight: true)
|
? const DesktopAppBar(isCompactHeight: true)
|
||||||
: AppBar(
|
: AppBar(
|
||||||
leading: AppBarBackButton(
|
leading: AppBarBackButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
|
|
@ -52,6 +52,7 @@ class WalletSummary extends StatelessWidget {
|
||||||
walletId: walletId,
|
walletId: walletId,
|
||||||
width: constraints.maxWidth,
|
width: constraints.maxWidth,
|
||||||
height: constraints.maxHeight,
|
height: constraints.maxHeight,
|
||||||
|
isFavorite: false,
|
||||||
),
|
),
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
|
@ -358,6 +358,8 @@ class _TransactionDetailsViewState
|
||||||
final currentHeight = ref.watch(walletsChangeNotifierProvider
|
final currentHeight = ref.watch(walletsChangeNotifierProvider
|
||||||
.select((value) => value.getManager(walletId).currentHeight));
|
.select((value) => value.getManager(walletId).currentHeight));
|
||||||
|
|
||||||
|
print("THIS TRANSACTION IS $_transaction");
|
||||||
|
|
||||||
return ConditionalParent(
|
return ConditionalParent(
|
||||||
condition: !isDesktop,
|
condition: !isDesktop,
|
||||||
builder: (child) => Background(
|
builder: (child) => Background(
|
||||||
|
@ -471,7 +473,9 @@ class _TransactionDetailsViewState
|
||||||
),
|
),
|
||||||
SelectableText(
|
SelectableText(
|
||||||
_transaction.isCancelled
|
_transaction.isCancelled
|
||||||
? "Cancelled"
|
? coin == Coin.ethereum
|
||||||
|
? "Failed"
|
||||||
|
: "Cancelled"
|
||||||
: whatIsIt(
|
: whatIsIt(
|
||||||
_transaction,
|
_transaction,
|
||||||
currentHeight,
|
currentHeight,
|
||||||
|
@ -582,7 +586,9 @@ class _TransactionDetailsViewState
|
||||||
// child:
|
// child:
|
||||||
SelectableText(
|
SelectableText(
|
||||||
_transaction.isCancelled
|
_transaction.isCancelled
|
||||||
? "Cancelled"
|
? coin == Coin.ethereum
|
||||||
|
? "Failed"
|
||||||
|
: "Cancelled"
|
||||||
: whatIsIt(
|
: whatIsIt(
|
||||||
_transaction,
|
_transaction,
|
||||||
currentHeight,
|
currentHeight,
|
||||||
|
@ -774,12 +780,69 @@ class _TransactionDetailsViewState
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (coin == Coin.epicCash)
|
||||||
|
isDesktop
|
||||||
|
? const _Divider()
|
||||||
|
: const SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
if (coin == Coin.epicCash)
|
||||||
|
RoundedWhiteContainer(
|
||||||
|
padding: isDesktop
|
||||||
|
? const EdgeInsets.all(16)
|
||||||
|
: const EdgeInsets.all(12),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"On chain note",
|
||||||
|
style: isDesktop
|
||||||
|
? STextStyles
|
||||||
|
.desktopTextExtraExtraSmall(
|
||||||
|
context)
|
||||||
|
: STextStyles.itemSubtitle(
|
||||||
|
context),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
SelectableText(
|
||||||
|
_transaction.otherData ?? "",
|
||||||
|
style: isDesktop
|
||||||
|
? STextStyles
|
||||||
|
.desktopTextExtraExtraSmall(
|
||||||
|
context)
|
||||||
|
.copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<
|
||||||
|
StackColors>()!
|
||||||
|
.textDark,
|
||||||
|
)
|
||||||
|
: STextStyles.itemSubtitle12(
|
||||||
|
context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (isDesktop)
|
||||||
|
IconCopyButton(
|
||||||
|
data: _transaction.address.value!.value,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
isDesktop
|
isDesktop
|
||||||
? const _Divider()
|
? const _Divider()
|
||||||
: const SizedBox(
|
: const SizedBox(
|
||||||
height: 12,
|
height: 12,
|
||||||
),
|
),
|
||||||
|
|
||||||
RoundedWhiteContainer(
|
RoundedWhiteContainer(
|
||||||
padding: isDesktop
|
padding: isDesktop
|
||||||
? const EdgeInsets.all(16)
|
? const EdgeInsets.all(16)
|
||||||
|
@ -792,7 +855,9 @@ class _TransactionDetailsViewState
|
||||||
MainAxisAlignment.spaceBetween,
|
MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Note",
|
(coin == Coin.epicCash)
|
||||||
|
? "Local Note"
|
||||||
|
: "Note ",
|
||||||
style: isDesktop
|
style: isDesktop
|
||||||
? STextStyles
|
? STextStyles
|
||||||
.desktopTextExtraExtraSmall(
|
.desktopTextExtraExtraSmall(
|
||||||
|
@ -861,7 +926,9 @@ class _TransactionDetailsViewState
|
||||||
notesServiceChangeNotifierProvider(
|
notesServiceChangeNotifierProvider(
|
||||||
walletId)
|
walletId)
|
||||||
.select((value) => value.getNoteFor(
|
.select((value) => value.getNoteFor(
|
||||||
txid: _transaction.txid))),
|
txid: (coin == Coin.epicCash)
|
||||||
|
? _transaction.slateId!
|
||||||
|
: _transaction.txid))),
|
||||||
builder: (builderContext,
|
builder: (builderContext,
|
||||||
AsyncSnapshot<String> snapshot) {
|
AsyncSnapshot<String> snapshot) {
|
||||||
if (snapshot.connectionState ==
|
if (snapshot.connectionState ==
|
||||||
|
|
|
@ -10,16 +10,17 @@
|
||||||
|
|
||||||
import 'package:decimal/decimal.dart';
|
import 'package:decimal/decimal.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_rounded_date_picker/flutter_rounded_date_picker.dart';
|
import 'package:flutter_rounded_date_picker/flutter_rounded_date_picker.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stackwallet/models/transaction_filter.dart';
|
import 'package:stackwallet/models/transaction_filter.dart';
|
||||||
|
import 'package:stackwallet/providers/global/locale_provider.dart';
|
||||||
import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';
|
import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
import 'package:stackwallet/themes/theme_providers.dart';
|
import 'package:stackwallet/themes/theme_providers.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
||||||
|
import 'package:stackwallet/utilities/amount/amount_input_formatter.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
@ -757,12 +758,20 @@ class _TransactionSearchViewState
|
||||||
decimal: true,
|
decimal: true,
|
||||||
),
|
),
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
// regex to validate a crypto amount with 8 decimal places
|
AmountInputFormatter(
|
||||||
TextInputFormatter.withFunction((oldValue, newValue) =>
|
decimals: widget.coin.decimals,
|
||||||
RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
|
unit: ref.watch(pAmountUnit(widget.coin)),
|
||||||
.hasMatch(newValue.text)
|
locale: ref.watch(
|
||||||
? newValue
|
localeServiceChangeNotifierProvider
|
||||||
: oldValue),
|
.select((value) => value.locale),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// // regex to validate a crypto amount with 8 decimal places
|
||||||
|
// TextInputFormatter.withFunction((oldValue, newValue) =>
|
||||||
|
// RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
|
||||||
|
// .hasMatch(newValue.text)
|
||||||
|
// ? newValue
|
||||||
|
// : oldValue),
|
||||||
],
|
],
|
||||||
style: isDesktop
|
style: isDesktop
|
||||||
? STextStyles.desktopTextExtraSmall(context).copyWith(
|
? STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||||
|
|
|
@ -680,6 +680,7 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: WalletSummary(
|
child: WalletSummary(
|
||||||
walletId: walletId,
|
walletId: walletId,
|
||||||
|
aspectRatio: 1.75,
|
||||||
initialSyncStatus: ref.watch(managerProvider
|
initialSyncStatus: ref.watch(managerProvider
|
||||||
.select((value) => value.isRefreshing))
|
.select((value) => value.isRefreshing))
|
||||||
? WalletSyncStatus.syncing
|
? WalletSyncStatus.syncing
|
||||||
|
|
|
@ -149,6 +149,7 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
|
||||||
walletId: widget.walletId,
|
walletId: widget.walletId,
|
||||||
width: widget.width,
|
width: widget.width,
|
||||||
height: widget.height,
|
height: widget.height,
|
||||||
|
isFavorite: true,
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(12.0),
|
padding: const EdgeInsets.all(12.0),
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
@ -43,8 +45,10 @@ class DesktopAddressCard extends ConsumerWidget {
|
||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SvgPicture.asset(
|
SvgPicture.file(
|
||||||
ref.watch(coinIconProvider(entry.coin)),
|
File(
|
||||||
|
ref.watch(coinIconProvider(entry.coin)),
|
||||||
|
),
|
||||||
height: 32,
|
height: 32,
|
||||||
width: 32,
|
width: 32,
|
||||||
),
|
),
|
||||||
|
|
|
@ -53,15 +53,6 @@ class DesktopContactDetails extends ConsumerStatefulWidget {
|
||||||
class _DesktopContactDetailsState extends ConsumerState<DesktopContactDetails> {
|
class _DesktopContactDetailsState extends ConsumerState<DesktopContactDetails> {
|
||||||
List<Tuple2<String, Transaction>> _cachedTransactions = [];
|
List<Tuple2<String, Transaction>> _cachedTransactions = [];
|
||||||
|
|
||||||
bool _contactHasAddress(String address, ContactEntry contact) {
|
|
||||||
for (final entry in contact.addresses) {
|
|
||||||
if (entry.address == address) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<Tuple2<String, Transaction>>> _filteredTransactionsByContact(
|
Future<List<Tuple2<String, Transaction>>> _filteredTransactionsByContact(
|
||||||
List<Manager> managers,
|
List<Manager> managers,
|
||||||
) async {
|
) async {
|
||||||
|
@ -259,7 +250,9 @@ class _DesktopContactDetailsState extends ConsumerState<DesktopContactDetails> {
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
for (int i = 0; i < contact.addresses.length; i++)
|
for (int i = 0;
|
||||||
|
i < contact.addressesSorted.length;
|
||||||
|
i++)
|
||||||
Column(
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
@ -273,7 +266,7 @@ class _DesktopContactDetailsState extends ConsumerState<DesktopContactDetails> {
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(18),
|
padding: const EdgeInsets.all(18),
|
||||||
child: DesktopAddressCard(
|
child: DesktopAddressCard(
|
||||||
entry: contact.addresses[i],
|
entry: contact.addressesSorted[i],
|
||||||
contactId: contact.customId,
|
contactId: contact.customId,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -69,8 +69,8 @@ class _AddressBookAddressChooserState extends State<AddressBookAddressChooser> {
|
||||||
|
|
||||||
List<ContactEntry> filter(List<ContactEntry> contacts, String searchTerm) {
|
List<ContactEntry> filter(List<ContactEntry> contacts, String searchTerm) {
|
||||||
if (widget.coin != null) {
|
if (widget.coin != null) {
|
||||||
contacts.removeWhere(
|
contacts.removeWhere((e) =>
|
||||||
(e) => e.addresses.where((a) => a.coin == widget.coin!).isEmpty);
|
e.addressesSorted.where((a) => a.coin == widget.coin!).isEmpty);
|
||||||
}
|
}
|
||||||
|
|
||||||
contacts.retainWhere((e) => _matches(searchTerm, e));
|
contacts.retainWhere((e) => _matches(searchTerm, e));
|
||||||
|
|
|
@ -78,7 +78,7 @@ class _ContactListItemState extends ConsumerState<ContactListItem> {
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
// filter addresses by coin is provided before building address list
|
// filter addresses by coin is provided before building address list
|
||||||
...contact.addresses
|
...contact.addressesSorted
|
||||||
.where((e) =>
|
.where((e) =>
|
||||||
filterByCoin != null ? e.coin == filterByCoin! : true)
|
filterByCoin != null ? e.coin == filterByCoin! : true)
|
||||||
.map(
|
.map(
|
||||||
|
|
|
@ -179,6 +179,8 @@ class _DesktopFeeDropDownState extends ConsumerState<DesktopFeeDropDown> {
|
||||||
? tokenFeeSessionCacheProvider
|
? tokenFeeSessionCacheProvider
|
||||||
: feeSheetSessionCacheProvider)
|
: feeSheetSessionCacheProvider)
|
||||||
.slow[amount]!;
|
.slow[amount]!;
|
||||||
|
default:
|
||||||
|
return Amount.zero;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import 'package:stackwallet/pages/token_view/token_view.dart';
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
import 'package:stackwallet/route_generator.dart';
|
import 'package:stackwallet/route_generator.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/address_utils.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
|
@ -236,7 +237,11 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
child: QrImageView(
|
child: QrImageView(
|
||||||
data: "${coin.uriScheme}:$receivingAddress",
|
data: AddressUtils.buildUriString(
|
||||||
|
coin,
|
||||||
|
receivingAddress,
|
||||||
|
{},
|
||||||
|
),
|
||||||
size: 200,
|
size: 200,
|
||||||
foregroundColor:
|
foregroundColor:
|
||||||
Theme.of(context).extension<StackColors>()!.accentColorDark,
|
Theme.of(context).extension<StackColors>()!.accentColorDark,
|
||||||
|
|
|
@ -9,16 +9,16 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:bip47/bip47.dart';
|
import 'package:bip47/bip47.dart';
|
||||||
|
import 'package:cw_core/monero_transaction_priority.dart';
|
||||||
import 'package:decimal/decimal.dart';
|
import 'package:decimal/decimal.dart';
|
||||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:stackwallet/models/contact_address_entry.dart';
|
import 'package:stackwallet/models/isar/models/contact_entry.dart';
|
||||||
import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
|
import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
|
||||||
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
|
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
|
||||||
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
|
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
|
||||||
|
@ -39,22 +39,27 @@ import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/address_utils.dart';
|
import 'package:stackwallet/utilities/address_utils.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
||||||
|
import 'package:stackwallet/utilities/amount/amount_input_formatter.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount_unit.dart';
|
import 'package:stackwallet/utilities/amount/amount_unit.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
||||||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
import 'package:stackwallet/utilities/prefs.dart';
|
import 'package:stackwallet/utilities/prefs.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
import 'package:stackwallet/widgets/animated_text.dart';
|
import 'package:stackwallet/widgets/animated_text.dart';
|
||||||
|
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/desktop_fee_dialog.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/fee_slider.dart';
|
||||||
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
|
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
|
||||||
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
|
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
|
||||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||||
|
@ -115,6 +120,17 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
|
|
||||||
bool get isPaynymSend => widget.accountLite != null;
|
bool get isPaynymSend => widget.accountLite != null;
|
||||||
|
|
||||||
|
bool isCustomFee = false;
|
||||||
|
int customFeeRate = 1;
|
||||||
|
(FeeRateType, String?, String?)? feeSelectionResult;
|
||||||
|
|
||||||
|
final stringsToLoopThrough = [
|
||||||
|
"Calculating",
|
||||||
|
"Calculating.",
|
||||||
|
"Calculating..",
|
||||||
|
"Calculating...",
|
||||||
|
];
|
||||||
|
|
||||||
Future<void> previewSend() async {
|
Future<void> previewSend() async {
|
||||||
final manager =
|
final manager =
|
||||||
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||||
|
@ -283,6 +299,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
isSegwit: widget.accountLite!.segwit,
|
isSegwit: widget.accountLite!.segwit,
|
||||||
amount: amount,
|
amount: amount,
|
||||||
args: {
|
args: {
|
||||||
|
"satsPerVByte": isCustomFee ? customFeeRate : null,
|
||||||
"feeRate": feeRate,
|
"feeRate": feeRate,
|
||||||
"UTXOs": (manager.hasCoinControlSupport &&
|
"UTXOs": (manager.hasCoinControlSupport &&
|
||||||
coinControlEnabled &&
|
coinControlEnabled &&
|
||||||
|
@ -299,6 +316,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
amount: amount,
|
amount: amount,
|
||||||
args: {
|
args: {
|
||||||
"feeRate": ref.read(feeRateTypeStateProvider),
|
"feeRate": ref.read(feeRateTypeStateProvider),
|
||||||
|
"satsPerVByte": isCustomFee ? customFeeRate : null,
|
||||||
"UTXOs": (manager.hasCoinControlSupport &&
|
"UTXOs": (manager.hasCoinControlSupport &&
|
||||||
coinControlEnabled &&
|
coinControlEnabled &&
|
||||||
ref.read(desktopUseUTXOs).isNotEmpty)
|
ref.read(desktopUseUTXOs).isNotEmpty)
|
||||||
|
@ -312,6 +330,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
amount: amount,
|
amount: amount,
|
||||||
args: {
|
args: {
|
||||||
"feeRate": ref.read(feeRateTypeStateProvider),
|
"feeRate": ref.read(feeRateTypeStateProvider),
|
||||||
|
"satsPerVByte": isCustomFee ? customFeeRate : null,
|
||||||
"UTXOs": (manager.hasCoinControlSupport &&
|
"UTXOs": (manager.hasCoinControlSupport &&
|
||||||
coinControlEnabled &&
|
coinControlEnabled &&
|
||||||
ref.read(desktopUseUTXOs).isNotEmpty)
|
ref.read(desktopUseUTXOs).isNotEmpty)
|
||||||
|
@ -442,27 +461,11 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
|
|
||||||
void _cryptoAmountChanged() async {
|
void _cryptoAmountChanged() async {
|
||||||
if (!_cryptoAmountChangeLock) {
|
if (!_cryptoAmountChangeLock) {
|
||||||
String cryptoAmount = cryptoAmountController.text;
|
final cryptoAmount = ref.read(pAmountFormatter(coin)).tryParse(
|
||||||
if (cryptoAmount.isNotEmpty &&
|
cryptoAmountController.text,
|
||||||
cryptoAmount != "." &&
|
);
|
||||||
cryptoAmount != ",") {
|
if (cryptoAmount != null) {
|
||||||
if (cryptoAmount.startsWith("~")) {
|
_amountToSend = cryptoAmount;
|
||||||
cryptoAmount = cryptoAmount.substring(1);
|
|
||||||
}
|
|
||||||
if (cryptoAmount.contains(" ")) {
|
|
||||||
cryptoAmount = cryptoAmount.split(" ").first;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure we don't shift past minimum atomic value
|
|
||||||
final shift = min(ref.read(pAmountUnit(coin)).shift, coin.decimals);
|
|
||||||
|
|
||||||
_amountToSend = cryptoAmount.contains(",")
|
|
||||||
? Decimal.parse(cryptoAmount.replaceFirst(",", "."))
|
|
||||||
.shift(0 - shift)
|
|
||||||
.toAmount(fractionDigits: coin.decimals)
|
|
||||||
: Decimal.parse(cryptoAmount)
|
|
||||||
.shift(0 - shift)
|
|
||||||
.toAmount(fractionDigits: coin.decimals);
|
|
||||||
if (_cachedAmountToSend != null &&
|
if (_cachedAmountToSend != null &&
|
||||||
_cachedAmountToSend == _amountToSend) {
|
_cachedAmountToSend == _amountToSend) {
|
||||||
return;
|
return;
|
||||||
|
@ -561,12 +564,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return AnimatedText(
|
return AnimatedText(
|
||||||
stringsToLoopThrough: const [
|
stringsToLoopThrough: stringsToLoopThrough,
|
||||||
"Loading balance",
|
|
||||||
"Loading balance.",
|
|
||||||
"Loading balance..",
|
|
||||||
"Loading balance...",
|
|
||||||
],
|
|
||||||
style: STextStyles.itemSubtitle(context),
|
style: STextStyles.itemSubtitle(context),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -662,15 +660,12 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void fiatTextFieldOnChanged(String baseAmountString) {
|
void fiatTextFieldOnChanged(String baseAmountString) {
|
||||||
if (baseAmountString.isNotEmpty &&
|
final baseAmount = Amount.tryParseFiatString(
|
||||||
baseAmountString != "." &&
|
baseAmountString,
|
||||||
baseAmountString != ",") {
|
locale: ref.read(localeServiceChangeNotifierProvider).locale,
|
||||||
final baseAmount = baseAmountString.contains(",")
|
);
|
||||||
? Decimal.parse(baseAmountString.replaceFirst(",", "."))
|
if (baseAmount != null) {
|
||||||
.toAmount(fractionDigits: 2)
|
final _price =
|
||||||
: Decimal.parse(baseAmountString).toAmount(fractionDigits: 2);
|
|
||||||
|
|
||||||
var _price =
|
|
||||||
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
|
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
|
||||||
|
|
||||||
if (_price == Decimal.zero) {
|
if (_price == Decimal.zero) {
|
||||||
|
@ -1040,12 +1035,17 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
// regex to validate a crypto amount with 8 decimal places
|
AmountInputFormatter(
|
||||||
TextInputFormatter.withFunction((oldValue, newValue) =>
|
decimals: coin.decimals,
|
||||||
RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
|
unit: ref.watch(pAmountUnit(coin)),
|
||||||
.hasMatch(newValue.text)
|
locale: locale,
|
||||||
? newValue
|
),
|
||||||
: oldValue),
|
// // regex to validate a crypto amount with 8 decimal places
|
||||||
|
// TextInputFormatter.withFunction((oldValue, newValue) =>
|
||||||
|
// RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
|
||||||
|
// .hasMatch(newValue.text)
|
||||||
|
// ? newValue
|
||||||
|
// : oldValue),
|
||||||
],
|
],
|
||||||
onChanged: (newValue) {},
|
onChanged: (newValue) {},
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
@ -1097,12 +1097,16 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
// regex to validate a fiat amount with 2 decimal places
|
AmountInputFormatter(
|
||||||
TextInputFormatter.withFunction((oldValue, newValue) =>
|
decimals: 2,
|
||||||
RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
|
locale: locale,
|
||||||
.hasMatch(newValue.text)
|
),
|
||||||
? newValue
|
// // regex to validate a fiat amount with 2 decimal places
|
||||||
: oldValue),
|
// TextInputFormatter.withFunction((oldValue, newValue) =>
|
||||||
|
// RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
|
||||||
|
// .hasMatch(newValue.text)
|
||||||
|
// ? newValue
|
||||||
|
// : oldValue),
|
||||||
],
|
],
|
||||||
onChanged: fiatTextFieldOnChanged,
|
onChanged: fiatTextFieldOnChanged,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
@ -1359,94 +1363,212 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// const SizedBox(
|
|
||||||
// height: 20,
|
|
||||||
// ),
|
|
||||||
// Text(
|
|
||||||
// "Note (optional)",
|
|
||||||
// style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
|
||||||
// color: Theme.of(context)
|
|
||||||
// .extension<StackColors>()!
|
|
||||||
// .textFieldActiveSearchIconRight,
|
|
||||||
// ),
|
|
||||||
// textAlign: TextAlign.left,
|
|
||||||
// ),
|
|
||||||
// const SizedBox(
|
|
||||||
// height: 10,
|
|
||||||
// ),
|
|
||||||
// ClipRRect(
|
|
||||||
// borderRadius: BorderRadius.circular(
|
|
||||||
// Constants.size.circularBorderRadius,
|
|
||||||
// ),
|
|
||||||
// child: TextField(
|
|
||||||
// minLines: 1,
|
|
||||||
// maxLines: 5,
|
|
||||||
// autocorrect: Util.isDesktop ? false : true,
|
|
||||||
// enableSuggestions: Util.isDesktop ? false : true,
|
|
||||||
// controller: noteController,
|
|
||||||
// focusNode: _noteFocusNode,
|
|
||||||
// style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
|
||||||
// color: Theme.of(context)
|
|
||||||
// .extension<StackColors>()!
|
|
||||||
// .textFieldActiveText,
|
|
||||||
// height: 1.8,
|
|
||||||
// ),
|
|
||||||
// onChanged: (_) => setState(() {}),
|
|
||||||
// decoration: standardInputDecoration(
|
|
||||||
// "Type something...",
|
|
||||||
// _noteFocusNode,
|
|
||||||
// context,
|
|
||||||
// desktopMed: true,
|
|
||||||
// ).copyWith(
|
|
||||||
// contentPadding: const EdgeInsets.only(
|
|
||||||
// left: 16,
|
|
||||||
// top: 11,
|
|
||||||
// bottom: 12,
|
|
||||||
// right: 5,
|
|
||||||
// ),
|
|
||||||
// suffixIcon: noteController.text.isNotEmpty
|
|
||||||
// ? Padding(
|
|
||||||
// padding: const EdgeInsets.only(right: 0),
|
|
||||||
// child: UnconstrainedBox(
|
|
||||||
// child: Row(
|
|
||||||
// children: [
|
|
||||||
// TextFieldIconButton(
|
|
||||||
// child: const XIcon(),
|
|
||||||
// onTap: () async {
|
|
||||||
// setState(() {
|
|
||||||
// noteController.text = "";
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// )
|
|
||||||
// : null,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
if (!isPaynymSend)
|
if (!isPaynymSend)
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
),
|
),
|
||||||
if (!([Coin.nano, Coin.banano, Coin.epicCash].contains(coin)))
|
if (!([Coin.nano, Coin.banano, Coin.epicCash].contains(coin)))
|
||||||
Text(
|
ConditionalParent(
|
||||||
"Transaction fee (${coin == Coin.ethereum ? "max" : "estimated"})",
|
condition: coin.isElectrumXCoin &&
|
||||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
!(((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||||
color: Theme.of(context)
|
ref.read(publicPrivateBalanceStateProvider.state).state ==
|
||||||
.extension<StackColors>()!
|
"Private")),
|
||||||
.textFieldActiveSearchIconRight,
|
builder: (child) => Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
child,
|
||||||
|
CustomTextButton(
|
||||||
|
text: "Edit",
|
||||||
|
onTap: () async {
|
||||||
|
feeSelectionResult = await showDialog<
|
||||||
|
(
|
||||||
|
FeeRateType,
|
||||||
|
String?,
|
||||||
|
String?,
|
||||||
|
)?>(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => DesktopFeeDialog(
|
||||||
|
walletId: walletId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (feeSelectionResult != null) {
|
||||||
|
if (isCustomFee &&
|
||||||
|
feeSelectionResult!.$1 != FeeRateType.custom) {
|
||||||
|
isCustomFee = false;
|
||||||
|
} else if (!isCustomFee &&
|
||||||
|
feeSelectionResult!.$1 == FeeRateType.custom) {
|
||||||
|
isCustomFee = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
"Transaction fee"
|
||||||
|
"${isCustomFee ? "" : " (${coin == Coin.ethereum ? "max" : "estimated"})"}",
|
||||||
|
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFieldActiveSearchIconRight,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.left,
|
|
||||||
),
|
),
|
||||||
if (!([Coin.nano, Coin.banano, Coin.epicCash].contains(coin)))
|
if (!([Coin.nano, Coin.banano, Coin.epicCash].contains(coin)))
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
if (!([Coin.nano, Coin.banano, Coin.epicCash].contains(coin)))
|
if (!([Coin.nano, Coin.banano, Coin.epicCash].contains(coin)))
|
||||||
DesktopFeeDropDown(
|
if (!isCustomFee)
|
||||||
walletId: walletId,
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
child: (feeSelectionResult?.$2 == null)
|
||||||
|
? FutureBuilder(
|
||||||
|
future: ref.watch(
|
||||||
|
walletsChangeNotifierProvider.select(
|
||||||
|
(value) => value.getManager(walletId).fees,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.done &&
|
||||||
|
snapshot.hasData) {
|
||||||
|
return DesktopFeeItem(
|
||||||
|
feeObject: snapshot.data,
|
||||||
|
feeRateType: FeeRateType.average,
|
||||||
|
walletId: walletId,
|
||||||
|
isButton: false,
|
||||||
|
feeFor: ({
|
||||||
|
required Amount amount,
|
||||||
|
required FeeRateType feeRateType,
|
||||||
|
required int feeRate,
|
||||||
|
required Coin coin,
|
||||||
|
}) async {
|
||||||
|
if (ref
|
||||||
|
.read(feeSheetSessionCacheProvider)
|
||||||
|
.average[amount] ==
|
||||||
|
null) {
|
||||||
|
final manager = ref
|
||||||
|
.read(walletsChangeNotifierProvider)
|
||||||
|
.getManager(walletId);
|
||||||
|
|
||||||
|
if (coin == Coin.monero ||
|
||||||
|
coin == Coin.wownero) {
|
||||||
|
final fee = await manager.estimateFeeFor(
|
||||||
|
amount,
|
||||||
|
MoneroTransactionPriority.regular.raw!);
|
||||||
|
ref
|
||||||
|
.read(feeSheetSessionCacheProvider)
|
||||||
|
.average[amount] = fee;
|
||||||
|
} else if ((coin == Coin.firo ||
|
||||||
|
coin == Coin.firoTestNet) &&
|
||||||
|
ref
|
||||||
|
.read(
|
||||||
|
publicPrivateBalanceStateProvider
|
||||||
|
.state)
|
||||||
|
.state !=
|
||||||
|
"Private") {
|
||||||
|
ref
|
||||||
|
.read(feeSheetSessionCacheProvider)
|
||||||
|
.average[amount] = await (manager.wallet
|
||||||
|
as FiroWallet)
|
||||||
|
.estimateFeeForPublic(amount, feeRate);
|
||||||
|
} else {
|
||||||
|
ref
|
||||||
|
.read(feeSheetSessionCacheProvider)
|
||||||
|
.average[amount] =
|
||||||
|
await manager.estimateFeeFor(
|
||||||
|
amount, feeRate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ref
|
||||||
|
.read(feeSheetSessionCacheProvider)
|
||||||
|
.average[amount]!;
|
||||||
|
},
|
||||||
|
isSelected: true,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
AnimatedText(
|
||||||
|
stringsToLoopThrough: stringsToLoopThrough,
|
||||||
|
style: STextStyles.desktopTextExtraExtraSmall(
|
||||||
|
context)
|
||||||
|
.copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFieldActiveText,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: (coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||||
|
ref
|
||||||
|
.watch(
|
||||||
|
publicPrivateBalanceStateProvider.state)
|
||||||
|
.state ==
|
||||||
|
"Private"
|
||||||
|
? Text(
|
||||||
|
"~${ref.watch(pAmountFormatter(coin)).format(
|
||||||
|
Amount(
|
||||||
|
rawValue: BigInt.parse("3794"),
|
||||||
|
fractionDigits: coin.decimals,
|
||||||
|
),
|
||||||
|
indicatePrecisionLoss: false,
|
||||||
|
)}",
|
||||||
|
style: STextStyles.desktopTextExtraExtraSmall(context)
|
||||||
|
.copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFieldActiveText,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
)
|
||||||
|
: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
feeSelectionResult?.$2 ?? "",
|
||||||
|
style: STextStyles.desktopTextExtraExtraSmall(
|
||||||
|
context)
|
||||||
|
.copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFieldActiveText,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
feeSelectionResult?.$3 ?? "",
|
||||||
|
style: STextStyles.desktopTextExtraExtraSmall(
|
||||||
|
context)
|
||||||
|
.copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFieldActiveSearchIconRight,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (isCustomFee)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
bottom: 12,
|
||||||
|
top: 16,
|
||||||
|
),
|
||||||
|
child: FeeSlider(
|
||||||
|
coin: coin,
|
||||||
|
onSatVByteChanged: (rate) {
|
||||||
|
customFeeRate = rate;
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 36,
|
height: 36,
|
||||||
|
|
|
@ -14,7 +14,7 @@ import 'package:decimal/decimal.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:stackwallet/models/contact_address_entry.dart';
|
import 'package:stackwallet/models/isar/models/contact_entry.dart';
|
||||||
import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
|
import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
|
||||||
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
|
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
|
||||||
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
|
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
|
||||||
|
@ -31,6 +31,7 @@ import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/address_utils.dart';
|
import 'package:stackwallet/utilities/address_utils.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
||||||
|
import 'package:stackwallet/utilities/amount/amount_input_formatter.dart';
|
||||||
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
||||||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
|
@ -50,7 +51,7 @@ import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||||
|
|
||||||
const _kCryptoAmountRegex = r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$';
|
// const _kCryptoAmountRegex = r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$';
|
||||||
|
|
||||||
class DesktopTokenSend extends ConsumerStatefulWidget {
|
class DesktopTokenSend extends ConsumerStatefulWidget {
|
||||||
const DesktopTokenSend({
|
const DesktopTokenSend({
|
||||||
|
@ -717,15 +718,23 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
|
AmountInputFormatter(
|
||||||
|
decimals: tokenContract.decimals,
|
||||||
|
unit: ref.watch(pAmountUnit(coin)),
|
||||||
|
locale: ref.watch(
|
||||||
|
localeServiceChangeNotifierProvider
|
||||||
|
.select((value) => value.locale),
|
||||||
|
),
|
||||||
|
),
|
||||||
// regex to validate a crypto amount with 8 decimal places
|
// regex to validate a crypto amount with 8 decimal places
|
||||||
TextInputFormatter.withFunction((oldValue, newValue) => RegExp(
|
// TextInputFormatter.withFunction((oldValue, newValue) => RegExp(
|
||||||
_kCryptoAmountRegex.replaceAll(
|
// _kCryptoAmountRegex.replaceAll(
|
||||||
"0,8",
|
// "0,8",
|
||||||
"0,${tokenContract.decimals}",
|
// "0,${tokenContract.decimals}",
|
||||||
),
|
// ),
|
||||||
).hasMatch(newValue.text)
|
// ).hasMatch(newValue.text)
|
||||||
? newValue
|
// ? newValue
|
||||||
: oldValue),
|
// : oldValue),
|
||||||
],
|
],
|
||||||
onChanged: (newValue) {},
|
onChanged: (newValue) {},
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
@ -777,12 +786,19 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
// regex to validate a fiat amount with 2 decimal places
|
AmountInputFormatter(
|
||||||
TextInputFormatter.withFunction((oldValue, newValue) =>
|
decimals: 2,
|
||||||
RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
|
locale: ref.watch(
|
||||||
.hasMatch(newValue.text)
|
localeServiceChangeNotifierProvider
|
||||||
? newValue
|
.select((value) => value.locale),
|
||||||
: oldValue),
|
),
|
||||||
|
),
|
||||||
|
// // regex to validate a fiat amount with 2 decimal places
|
||||||
|
// TextInputFormatter.withFunction((oldValue, newValue) =>
|
||||||
|
// RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
|
||||||
|
// .hasMatch(newValue.text)
|
||||||
|
// ? newValue
|
||||||
|
// : oldValue),
|
||||||
],
|
],
|
||||||
onChanged: fiatTextFieldOnChanged,
|
onChanged: fiatTextFieldOnChanged,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
|
|
@ -15,7 +15,6 @@ import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
|
||||||
import 'package:stackwallet/db/hive/db.dart';
|
import 'package:stackwallet/db/hive/db.dart';
|
||||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart';
|
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart';
|
||||||
|
@ -213,8 +212,8 @@ class _ForgottenPassphraseRestoreFromSWBState
|
||||||
await (ref.read(secureStoreProvider).store as DesktopSecureStore)
|
await (ref.read(secureStoreProvider).store as DesktopSecureStore)
|
||||||
.close();
|
.close();
|
||||||
ref.refresh(secureStoreProvider);
|
ref.refresh(secureStoreProvider);
|
||||||
|
await ref.read(storageCryptoHandlerProvider).deleteBox();
|
||||||
ref.refresh(storageCryptoHandlerProvider);
|
ref.refresh(storageCryptoHandlerProvider);
|
||||||
await Hive.deleteBoxFromDisk(DB.boxNameDesktopData);
|
|
||||||
await DB.instance.init();
|
await DB.instance.init();
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
Navigator.of(context)
|
Navigator.of(context)
|
||||||
|
|
|
@ -83,10 +83,10 @@ class _DesktopSettingsViewState extends ConsumerState<DesktopSettingsView> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DesktopScaffold(
|
return DesktopScaffold(
|
||||||
background: Theme.of(context).extension<StackColors>()!.background,
|
background: Theme.of(context).extension<StackColors>()!.background,
|
||||||
appBar: DesktopAppBar(
|
appBar: const DesktopAppBar(
|
||||||
isCompactHeight: true,
|
isCompactHeight: true,
|
||||||
leading: Row(
|
leading: Row(
|
||||||
children: const [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
|
@ -97,7 +97,10 @@ class _DesktopSettingsViewState extends ConsumerState<DesktopSettingsView> {
|
||||||
),
|
),
|
||||||
body: Row(
|
body: Row(
|
||||||
children: [
|
children: [
|
||||||
const SettingsMenu(),
|
const Padding(
|
||||||
|
padding: EdgeInsets.all(15.0),
|
||||||
|
child: SettingsMenu(),
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: contentViews[
|
child: contentViews[
|
||||||
ref.watch(selectedSettingsMenuItemStateProvider.state).state],
|
ref.watch(selectedSettingsMenuItemStateProvider.state).state],
|
||||||
|
|
|
@ -45,10 +45,10 @@ class _SettingsMenuState extends ConsumerState<SettingsMenu> {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
SizedBox(
|
||||||
padding: const EdgeInsets.only(left: 15),
|
width: 250,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
for (int i = 0; i < labels.length; i++)
|
for (int i = 0; i < labels.length; i++)
|
||||||
Column(
|
Column(
|
||||||
|
@ -83,157 +83,7 @@ class _SettingsMenuState extends ConsumerState<SettingsMenu> {
|
||||||
.state = newValue,
|
.state = newValue,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
|
|
||||||
// SettingsMenuItem(
|
|
||||||
// icon: SvgPicture.asset(
|
|
||||||
// Assets.svg.polygon,
|
|
||||||
// width: 11,
|
|
||||||
// height: 11,
|
|
||||||
// color: selectedMenuItem == 0
|
|
||||||
// ? Theme.of(context)
|
|
||||||
// .extension<StackColors>()!
|
|
||||||
// .accentColorBlue
|
|
||||||
// : Colors.transparent,
|
|
||||||
// ),
|
|
||||||
// label: "Backup and restore",
|
|
||||||
// value: 0,
|
|
||||||
// group: selectedMenuItem,
|
|
||||||
// onChanged: updateSelectedMenuItem,
|
|
||||||
// ),
|
|
||||||
// const SizedBox(
|
|
||||||
// height: 2,
|
|
||||||
// ),
|
|
||||||
// SettingsMenuItem(
|
|
||||||
// icon: SvgPicture.asset(
|
|
||||||
// Assets.svg.polygon,
|
|
||||||
// width: 11,
|
|
||||||
// height: 11,
|
|
||||||
// color: selectedMenuItem == 1
|
|
||||||
// ? Theme.of(context)
|
|
||||||
// .extension<StackColors>()!
|
|
||||||
// .accentColorBlue
|
|
||||||
// : Colors.transparent,
|
|
||||||
// ),
|
|
||||||
// label: "Security",
|
|
||||||
// value: 1,
|
|
||||||
// group: selectedMenuItem,
|
|
||||||
// onChanged: updateSelectedMenuItem,
|
|
||||||
// ),
|
|
||||||
// const SizedBox(
|
|
||||||
// height: 2,
|
|
||||||
// ),
|
|
||||||
// SettingsMenuItem(
|
|
||||||
// icon: SvgPicture.asset(
|
|
||||||
// Assets.svg.polygon,
|
|
||||||
// width: 11,
|
|
||||||
// height: 11,
|
|
||||||
// color: selectedMenuItem == 2
|
|
||||||
// ? Theme.of(context)
|
|
||||||
// .extension<StackColors>()!
|
|
||||||
// .accentColorBlue
|
|
||||||
// : Colors.transparent,
|
|
||||||
// ),
|
|
||||||
// label: "Currency",
|
|
||||||
// value: 2,
|
|
||||||
// group: selectedMenuItem,
|
|
||||||
// onChanged: updateSelectedMenuItem,
|
|
||||||
// ),
|
|
||||||
// const SizedBox(
|
|
||||||
// height: 2,
|
|
||||||
// ),
|
|
||||||
// SettingsMenuItem(
|
|
||||||
// icon: SvgPicture.asset(
|
|
||||||
// Assets.svg.polygon,
|
|
||||||
// width: 11,
|
|
||||||
// height: 11,
|
|
||||||
// color: selectedMenuItem == 3
|
|
||||||
// ? Theme.of(context)
|
|
||||||
// .extension<StackColors>()!
|
|
||||||
// .accentColorBlue
|
|
||||||
// : Colors.transparent,
|
|
||||||
// ),
|
|
||||||
// label: "Language",
|
|
||||||
// value: 3,
|
|
||||||
// group: selectedMenuItem,
|
|
||||||
// onChanged: updateSelectedMenuItem,
|
|
||||||
// ),
|
|
||||||
// const SizedBox(
|
|
||||||
// height: 2,
|
|
||||||
// ),
|
|
||||||
// SettingsMenuItem(
|
|
||||||
// icon: SvgPicture.asset(
|
|
||||||
// Assets.svg.polygon,
|
|
||||||
// width: 11,
|
|
||||||
// height: 11,
|
|
||||||
// color: selectedMenuItem == 4
|
|
||||||
// ? Theme.of(context)
|
|
||||||
// .extension<StackColors>()!
|
|
||||||
// .accentColorBlue
|
|
||||||
// : Colors.transparent,
|
|
||||||
// ),
|
|
||||||
// label: "Nodes",
|
|
||||||
// value: 4,
|
|
||||||
// group: selectedMenuItem,
|
|
||||||
// onChanged: updateSelectedMenuItem,
|
|
||||||
// ),
|
|
||||||
// const SizedBox(
|
|
||||||
// height: 2,
|
|
||||||
// ),
|
|
||||||
// SettingsMenuItem(
|
|
||||||
// icon: SvgPicture.asset(
|
|
||||||
// Assets.svg.polygon,
|
|
||||||
// width: 11,
|
|
||||||
// height: 11,
|
|
||||||
// color: selectedMenuItem == 5
|
|
||||||
// ? Theme.of(context)
|
|
||||||
// .extension<StackColors>()!
|
|
||||||
// .accentColorBlue
|
|
||||||
// : Colors.transparent,
|
|
||||||
// ),
|
|
||||||
// label: "Syncing preferences",
|
|
||||||
// value: 5,
|
|
||||||
// group: selectedMenuItem,
|
|
||||||
// onChanged: updateSelectedMenuItem,
|
|
||||||
// ),
|
|
||||||
// const SizedBox(
|
|
||||||
// height: 2,
|
|
||||||
// ),
|
|
||||||
// SettingsMenuItem(
|
|
||||||
// icon: SvgPicture.asset(
|
|
||||||
// Assets.svg.polygon,
|
|
||||||
// width: 11,
|
|
||||||
// height: 11,
|
|
||||||
// color: selectedMenuItem == 6
|
|
||||||
// ? Theme.of(context)
|
|
||||||
// .extension<StackColors>()!
|
|
||||||
// .accentColorBlue
|
|
||||||
// : Colors.transparent,
|
|
||||||
// ),
|
|
||||||
// label: "Appearance",
|
|
||||||
// value: 6,
|
|
||||||
// group: selectedMenuItem,
|
|
||||||
// onChanged: updateSelectedMenuItem,
|
|
||||||
// ),
|
|
||||||
// const SizedBox(
|
|
||||||
// height: 2,
|
|
||||||
// ),
|
|
||||||
// SettingsMenuItem(
|
|
||||||
// icon: SvgPicture.asset(
|
|
||||||
// Assets.svg.polygon,
|
|
||||||
// width: 11,
|
|
||||||
// height: 11,
|
|
||||||
// color: selectedMenuItem == 7
|
|
||||||
// ? Theme.of(context)
|
|
||||||
// .extension<StackColors>()!
|
|
||||||
// .accentColorBlue
|
|
||||||
// : Colors.transparent,
|
|
||||||
// ),
|
|
||||||
// label: "Advanced",
|
|
||||||
// value: 7,
|
|
||||||
// group: selectedMenuItem,
|
|
||||||
// onChanged: updateSelectedMenuItem,
|
|
||||||
// ),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -219,44 +219,6 @@ class _AdvancedSettings extends ConsumerState<AdvancedSettings> {
|
||||||
thickness: 0.5,
|
thickness: 0.5,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(10),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Debug info",
|
|
||||||
style: STextStyles.desktopTextExtraSmall(context)
|
|
||||||
.copyWith(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.textDark),
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
),
|
|
||||||
PrimaryButton(
|
|
||||||
buttonHeight: ButtonHeight.xs,
|
|
||||||
label: "Show logs",
|
|
||||||
width: 101,
|
|
||||||
onPressed: () async {
|
|
||||||
await showDialog<dynamic>(
|
|
||||||
context: context,
|
|
||||||
useSafeArea: false,
|
|
||||||
barrierDismissible: true,
|
|
||||||
builder: (context) {
|
|
||||||
return const DebugInfoDialog();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Padding(
|
|
||||||
padding: EdgeInsets.all(10.0),
|
|
||||||
child: Divider(
|
|
||||||
thickness: 0.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
@ -327,6 +289,44 @@ class _AdvancedSettings extends ConsumerState<AdvancedSettings> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.all(10.0),
|
||||||
|
child: Divider(
|
||||||
|
thickness: 0.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Debug info",
|
||||||
|
style: STextStyles.desktopTextExtraSmall(context)
|
||||||
|
.copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textDark),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
PrimaryButton(
|
||||||
|
buttonHeight: ButtonHeight.xs,
|
||||||
|
label: "Show logs",
|
||||||
|
width: 101,
|
||||||
|
onPressed: () async {
|
||||||
|
await showDialog<dynamic>(
|
||||||
|
context: context,
|
||||||
|
useSafeArea: false,
|
||||||
|
barrierDismissible: true,
|
||||||
|
builder: (context) {
|
||||||
|
return const DebugInfoDialog();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
|
|
|
@ -13,8 +13,12 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:stackwallet/models/exchange/active_pair.dart';
|
import 'package:stackwallet/models/exchange/active_pair.dart';
|
||||||
import 'package:stackwallet/models/exchange/response_objects/estimate.dart';
|
import 'package:stackwallet/models/exchange/response_objects/estimate.dart';
|
||||||
import 'package:stackwallet/models/exchange/response_objects/range.dart';
|
import 'package:stackwallet/models/exchange/response_objects/range.dart';
|
||||||
|
import 'package:stackwallet/providers/global/locale_provider.dart';
|
||||||
import 'package:stackwallet/services/exchange/exchange.dart';
|
import 'package:stackwallet/services/exchange/exchange.dart';
|
||||||
import 'package:stackwallet/services/exchange/exchange_response.dart';
|
import 'package:stackwallet/services/exchange/exchange_response.dart';
|
||||||
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
|
import 'package:stackwallet/utilities/amount/amount_unit.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/enums/exchange_rate_type_enum.dart';
|
import 'package:stackwallet/utilities/enums/exchange_rate_type_enum.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
@ -44,7 +48,22 @@ final efSendAmountStringProvider = StateProvider<String>((ref) {
|
||||||
if (refreshing && reversed) {
|
if (refreshing && reversed) {
|
||||||
return "-";
|
return "-";
|
||||||
} else {
|
} else {
|
||||||
return ref.watch(efSendAmountProvider)?.toString() ?? "";
|
final decimal = ref.watch(efSendAmountProvider);
|
||||||
|
String string = "";
|
||||||
|
if (decimal != null) {
|
||||||
|
final amount = Amount.fromDecimal(decimal, fractionDigits: decimal.scale);
|
||||||
|
final locale = ref.watch(localeServiceChangeNotifierProvider).locale;
|
||||||
|
string = AmountUnit.normal.displayAmount(
|
||||||
|
amount: amount,
|
||||||
|
locale: locale,
|
||||||
|
coin: Coin
|
||||||
|
.nano, // use nano just to ensure decimal.scale < Coin.value.decimals
|
||||||
|
withUnitName: false,
|
||||||
|
maxDecimalPlaces: decimal.scale,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
final efReceiveAmountStringProvider = StateProvider<String>((ref) {
|
final efReceiveAmountStringProvider = StateProvider<String>((ref) {
|
||||||
|
@ -54,7 +73,22 @@ final efReceiveAmountStringProvider = StateProvider<String>((ref) {
|
||||||
if (refreshing && reversed == false) {
|
if (refreshing && reversed == false) {
|
||||||
return "-";
|
return "-";
|
||||||
} else {
|
} else {
|
||||||
return ref.watch(efReceiveAmountProvider)?.toString() ?? "";
|
final decimal = ref.watch(efReceiveAmountProvider);
|
||||||
|
String string = "";
|
||||||
|
if (decimal != null) {
|
||||||
|
final amount = Amount.fromDecimal(decimal, fractionDigits: decimal.scale);
|
||||||
|
final locale = ref.watch(localeServiceChangeNotifierProvider).locale;
|
||||||
|
string = AmountUnit.normal.displayAmount(
|
||||||
|
amount: amount,
|
||||||
|
locale: locale,
|
||||||
|
coin: Coin
|
||||||
|
.nano, // use nano just to ensure decimal.scale < Coin.value.decimals
|
||||||
|
withUnitName: false,
|
||||||
|
maxDecimalPlaces: decimal.scale,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -287,8 +287,8 @@ class BitcoinWallet extends CoinServiceAPI
|
||||||
@override
|
@override
|
||||||
Future<int> get maxFee async {
|
Future<int> get maxFee async {
|
||||||
final fee = (await fees).fast as String;
|
final fee = (await fees).fast as String;
|
||||||
final satsFee =
|
final satsFee = Decimal.parse(fee) *
|
||||||
Decimal.parse(fee) * Decimal.fromInt(Constants.satsPerCoin(coin).toInt());
|
Decimal.fromInt(Constants.satsPerCoin(coin).toInt());
|
||||||
return satsFee.floor().toBigInt().toInt();
|
return satsFee.floor().toBigInt().toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1070,9 +1070,60 @@ class BitcoinWallet extends CoinServiceAPI
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final feeRateType = args?["feeRate"];
|
final feeRateType = args?["feeRate"];
|
||||||
|
final customSatsPerVByte = args?["satsPerVByte"] as int?;
|
||||||
final feeRateAmount = args?["feeRateAmount"];
|
final feeRateAmount = args?["feeRateAmount"];
|
||||||
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
||||||
if (feeRateType is FeeRateType || feeRateAmount is int) {
|
|
||||||
|
if (customSatsPerVByte != null) {
|
||||||
|
// check for send all
|
||||||
|
bool isSendAll = false;
|
||||||
|
if (amount == balance.spendable) {
|
||||||
|
isSendAll = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final bool coinControl = utxos != null;
|
||||||
|
|
||||||
|
final result = await coinSelection(
|
||||||
|
satoshiAmountToSend: amount.raw.toInt(),
|
||||||
|
selectedTxFeeRate: -1,
|
||||||
|
satsPerVByte: customSatsPerVByte,
|
||||||
|
recipientAddress: address,
|
||||||
|
isSendAll: isSendAll,
|
||||||
|
utxos: utxos?.toList(),
|
||||||
|
coinControl: coinControl,
|
||||||
|
);
|
||||||
|
|
||||||
|
Logging.instance
|
||||||
|
.log("PREPARE SEND RESULT: $result", level: LogLevel.Info);
|
||||||
|
if (result is int) {
|
||||||
|
switch (result) {
|
||||||
|
case 1:
|
||||||
|
throw Exception("Insufficient balance!");
|
||||||
|
case 2:
|
||||||
|
throw Exception("Insufficient funds to pay for transaction fee!");
|
||||||
|
default:
|
||||||
|
throw Exception("Transaction failed with error code $result");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final hex = result["hex"];
|
||||||
|
if (hex is String) {
|
||||||
|
final fee = result["fee"] as int;
|
||||||
|
final vSize = result["vSize"] as int;
|
||||||
|
|
||||||
|
Logging.instance.log("txHex: $hex", level: LogLevel.Info);
|
||||||
|
Logging.instance.log("fee: $fee", level: LogLevel.Info);
|
||||||
|
Logging.instance.log("vsize: $vSize", level: LogLevel.Info);
|
||||||
|
// fee should never be less than vSize sanity check
|
||||||
|
if (fee < vSize) {
|
||||||
|
throw Exception(
|
||||||
|
"Error in fee calculation: Transaction fee cannot be less than vSize");
|
||||||
|
}
|
||||||
|
return result as Map<String, dynamic>;
|
||||||
|
} else {
|
||||||
|
throw Exception("sent hex is not a String!!!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||||
late final int rate;
|
late final int rate;
|
||||||
if (feeRateType is FeeRateType) {
|
if (feeRateType is FeeRateType) {
|
||||||
int fee = 0;
|
int fee = 0;
|
||||||
|
@ -1087,6 +1138,8 @@ class BitcoinWallet extends CoinServiceAPI
|
||||||
case FeeRateType.slow:
|
case FeeRateType.slow:
|
||||||
fee = feeObject.slow;
|
fee = feeObject.slow;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
throw ArgumentError("Invalid use of custom fee");
|
||||||
}
|
}
|
||||||
rate = fee;
|
rate = fee;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2201,6 +2254,7 @@ class BitcoinWallet extends CoinServiceAPI
|
||||||
required String recipientAddress,
|
required String recipientAddress,
|
||||||
required bool coinControl,
|
required bool coinControl,
|
||||||
required bool isSendAll,
|
required bool isSendAll,
|
||||||
|
int? satsPerVByte,
|
||||||
int additionalOutputs = 0,
|
int additionalOutputs = 0,
|
||||||
List<isar_models.UTXO>? utxos,
|
List<isar_models.UTXO>? utxos,
|
||||||
}) async {
|
}) async {
|
||||||
|
@ -2308,18 +2362,22 @@ class BitcoinWallet extends CoinServiceAPI
|
||||||
recipients: [recipientAddress],
|
recipients: [recipientAddress],
|
||||||
satoshiAmounts: [satoshisBeingUsed - 1],
|
satoshiAmounts: [satoshisBeingUsed - 1],
|
||||||
))["vSize"] as int;
|
))["vSize"] as int;
|
||||||
int feeForOneOutput = estimateTxFee(
|
int feeForOneOutput = satsPerVByte != null
|
||||||
vSize: vSizeForOneOutput,
|
? (satsPerVByte * vSizeForOneOutput)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForOneOutput,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
|
|
||||||
final int roughEstimate = roughFeeEstimate(
|
if (satsPerVByte == null) {
|
||||||
spendableOutputs.length,
|
final int roughEstimate = roughFeeEstimate(
|
||||||
1,
|
spendableOutputs.length,
|
||||||
selectedTxFeeRate,
|
1,
|
||||||
).raw.toInt();
|
selectedTxFeeRate,
|
||||||
if (feeForOneOutput < roughEstimate) {
|
).raw.toInt();
|
||||||
feeForOneOutput = roughEstimate;
|
if (feeForOneOutput < roughEstimate) {
|
||||||
|
feeForOneOutput = roughEstimate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final int amount = satoshiAmountToSend - feeForOneOutput;
|
final int amount = satoshiAmountToSend - feeForOneOutput;
|
||||||
|
@ -2373,15 +2431,19 @@ class BitcoinWallet extends CoinServiceAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assume 1 output, only for recipient and no change
|
// Assume 1 output, only for recipient and no change
|
||||||
final feeForOneOutput = estimateTxFee(
|
final feeForOneOutput = satsPerVByte != null
|
||||||
vSize: vSizeForOneOutput,
|
? (satsPerVByte * vSizeForOneOutput)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForOneOutput,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
// Assume 2 outputs, one for recipient and one for change
|
// Assume 2 outputs, one for recipient and one for change
|
||||||
final feeForTwoOutputs = estimateTxFee(
|
final feeForTwoOutputs = satsPerVByte != null
|
||||||
vSize: vSizeForTwoOutPuts,
|
? (satsPerVByte * vSizeForTwoOutPuts)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForTwoOutPuts,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
|
|
||||||
Logging.instance
|
Logging.instance
|
||||||
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
||||||
|
@ -2576,6 +2638,7 @@ class BitcoinWallet extends CoinServiceAPI
|
||||||
return coinSelection(
|
return coinSelection(
|
||||||
satoshiAmountToSend: satoshiAmountToSend,
|
satoshiAmountToSend: satoshiAmountToSend,
|
||||||
selectedTxFeeRate: selectedTxFeeRate,
|
selectedTxFeeRate: selectedTxFeeRate,
|
||||||
|
satsPerVByte: satsPerVByte,
|
||||||
recipientAddress: recipientAddress,
|
recipientAddress: recipientAddress,
|
||||||
isSendAll: isSendAll,
|
isSendAll: isSendAll,
|
||||||
additionalOutputs: additionalOutputs + 1,
|
additionalOutputs: additionalOutputs + 1,
|
||||||
|
|
|
@ -955,9 +955,60 @@ class BitcoinCashWallet extends CoinServiceAPI
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final feeRateType = args?["feeRate"];
|
final feeRateType = args?["feeRate"];
|
||||||
|
final customSatsPerVByte = args?["satsPerVByte"] as int?;
|
||||||
final feeRateAmount = args?["feeRateAmount"];
|
final feeRateAmount = args?["feeRateAmount"];
|
||||||
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
||||||
if (feeRateType is FeeRateType || feeRateAmount is int) {
|
|
||||||
|
if (customSatsPerVByte != null) {
|
||||||
|
// check for send all
|
||||||
|
bool isSendAll = false;
|
||||||
|
if (amount == balance.spendable) {
|
||||||
|
isSendAll = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final bool coinControl = utxos != null;
|
||||||
|
|
||||||
|
final result = await coinSelection(
|
||||||
|
satoshiAmountToSend: amount.raw.toInt(),
|
||||||
|
selectedTxFeeRate: -1,
|
||||||
|
satsPerVByte: customSatsPerVByte,
|
||||||
|
recipientAddress: address,
|
||||||
|
isSendAll: isSendAll,
|
||||||
|
utxos: utxos?.toList(),
|
||||||
|
coinControl: coinControl,
|
||||||
|
);
|
||||||
|
|
||||||
|
Logging.instance
|
||||||
|
.log("PREPARE SEND RESULT: $result", level: LogLevel.Info);
|
||||||
|
if (result is int) {
|
||||||
|
switch (result) {
|
||||||
|
case 1:
|
||||||
|
throw Exception("Insufficient balance!");
|
||||||
|
case 2:
|
||||||
|
throw Exception("Insufficient funds to pay for transaction fee!");
|
||||||
|
default:
|
||||||
|
throw Exception("Transaction failed with error code $result");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final hex = result["hex"];
|
||||||
|
if (hex is String) {
|
||||||
|
final fee = result["fee"] as int;
|
||||||
|
final vSize = result["vSize"] as int;
|
||||||
|
|
||||||
|
Logging.instance.log("txHex: $hex", level: LogLevel.Info);
|
||||||
|
Logging.instance.log("fee: $fee", level: LogLevel.Info);
|
||||||
|
Logging.instance.log("vsize: $vSize", level: LogLevel.Info);
|
||||||
|
// fee should never be less than vSize sanity check
|
||||||
|
if (fee < vSize) {
|
||||||
|
throw Exception(
|
||||||
|
"Error in fee calculation: Transaction fee cannot be less than vSize");
|
||||||
|
}
|
||||||
|
return result as Map<String, dynamic>;
|
||||||
|
} else {
|
||||||
|
throw Exception("sent hex is not a String!!!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||||
late final int rate;
|
late final int rate;
|
||||||
if (feeRateType is FeeRateType) {
|
if (feeRateType is FeeRateType) {
|
||||||
int fee = 0;
|
int fee = 0;
|
||||||
|
@ -972,6 +1023,8 @@ class BitcoinCashWallet extends CoinServiceAPI
|
||||||
case FeeRateType.slow:
|
case FeeRateType.slow:
|
||||||
fee = feeObject.slow;
|
fee = feeObject.slow;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
throw ArgumentError("Invalid use of custom fee");
|
||||||
}
|
}
|
||||||
rate = fee;
|
rate = fee;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2187,6 +2240,7 @@ class BitcoinCashWallet extends CoinServiceAPI
|
||||||
required String recipientAddress,
|
required String recipientAddress,
|
||||||
required bool coinControl,
|
required bool coinControl,
|
||||||
required bool isSendAll,
|
required bool isSendAll,
|
||||||
|
int? satsPerVByte,
|
||||||
int additionalOutputs = 0,
|
int additionalOutputs = 0,
|
||||||
List<isar_models.UTXO>? utxos,
|
List<isar_models.UTXO>? utxos,
|
||||||
}) async {
|
}) async {
|
||||||
|
@ -2303,10 +2357,12 @@ class BitcoinCashWallet extends CoinServiceAPI
|
||||||
recipients: [recipientAddress],
|
recipients: [recipientAddress],
|
||||||
satoshiAmounts: [satoshisBeingUsed - 1],
|
satoshiAmounts: [satoshisBeingUsed - 1],
|
||||||
))["vSize"] as int;
|
))["vSize"] as int;
|
||||||
int feeForOneOutput = estimateTxFee(
|
int feeForOneOutput = satsPerVByte != null
|
||||||
vSize: vSizeForOneOutput,
|
? (satsPerVByte * vSizeForOneOutput)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForOneOutput,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
if (feeForOneOutput < (vSizeForOneOutput + 1)) {
|
if (feeForOneOutput < (vSizeForOneOutput + 1)) {
|
||||||
feeForOneOutput = (vSizeForOneOutput + 1);
|
feeForOneOutput = (vSizeForOneOutput + 1);
|
||||||
}
|
}
|
||||||
|
@ -2350,20 +2406,21 @@ class BitcoinCashWallet extends CoinServiceAPI
|
||||||
satoshisBeingUsed - satoshiAmountToSend - 1,
|
satoshisBeingUsed - satoshiAmountToSend - 1,
|
||||||
], // dust limit is the minimum amount a change output should be
|
], // dust limit is the minimum amount a change output should be
|
||||||
))["vSize"] as int;
|
))["vSize"] as int;
|
||||||
//todo: check if print needed
|
|
||||||
// debugPrint("vSizeForOneOutput $vSizeForOneOutput");
|
|
||||||
// debugPrint("vSizeForTwoOutPuts $vSizeForTwoOutPuts");
|
|
||||||
|
|
||||||
// Assume 1 output, only for recipient and no change
|
// Assume 1 output, only for recipient and no change
|
||||||
var feeForOneOutput = estimateTxFee(
|
int feeForOneOutput = satsPerVByte != null
|
||||||
vSize: vSizeForOneOutput,
|
? (satsPerVByte * vSizeForOneOutput)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForOneOutput,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
// Assume 2 outputs, one for recipient and one for change
|
// Assume 2 outputs, one for recipient and one for change
|
||||||
var feeForTwoOutputs = estimateTxFee(
|
int feeForTwoOutputs = satsPerVByte != null
|
||||||
vSize: vSizeForTwoOutPuts,
|
? (satsPerVByte * vSizeForTwoOutPuts)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForTwoOutPuts,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
|
|
||||||
Logging.instance
|
Logging.instance
|
||||||
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
||||||
|
@ -2575,6 +2632,7 @@ class BitcoinCashWallet extends CoinServiceAPI
|
||||||
return coinSelection(
|
return coinSelection(
|
||||||
satoshiAmountToSend: satoshiAmountToSend,
|
satoshiAmountToSend: satoshiAmountToSend,
|
||||||
selectedTxFeeRate: selectedTxFeeRate,
|
selectedTxFeeRate: selectedTxFeeRate,
|
||||||
|
satsPerVByte: satsPerVByte,
|
||||||
recipientAddress: recipientAddress,
|
recipientAddress: recipientAddress,
|
||||||
isSendAll: isSendAll,
|
isSendAll: isSendAll,
|
||||||
additionalOutputs: additionalOutputs + 1,
|
additionalOutputs: additionalOutputs + 1,
|
||||||
|
|
|
@ -941,9 +941,60 @@ class DogecoinWallet extends CoinServiceAPI
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final feeRateType = args?["feeRate"];
|
final feeRateType = args?["feeRate"];
|
||||||
|
final customSatsPerVByte = args?["satsPerVByte"] as int?;
|
||||||
final feeRateAmount = args?["feeRateAmount"];
|
final feeRateAmount = args?["feeRateAmount"];
|
||||||
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
||||||
if (feeRateType is FeeRateType || feeRateAmount is int) {
|
|
||||||
|
if (customSatsPerVByte != null) {
|
||||||
|
// check for send all
|
||||||
|
bool isSendAll = false;
|
||||||
|
if (amount == balance.spendable) {
|
||||||
|
isSendAll = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final bool coinControl = utxos != null;
|
||||||
|
|
||||||
|
final result = await coinSelection(
|
||||||
|
satoshiAmountToSend: amount.raw.toInt(),
|
||||||
|
selectedTxFeeRate: -1,
|
||||||
|
satsPerVByte: customSatsPerVByte,
|
||||||
|
recipientAddress: address,
|
||||||
|
isSendAll: isSendAll,
|
||||||
|
utxos: utxos?.toList(),
|
||||||
|
coinControl: coinControl,
|
||||||
|
);
|
||||||
|
|
||||||
|
Logging.instance
|
||||||
|
.log("PREPARE SEND RESULT: $result", level: LogLevel.Info);
|
||||||
|
if (result is int) {
|
||||||
|
switch (result) {
|
||||||
|
case 1:
|
||||||
|
throw Exception("Insufficient balance!");
|
||||||
|
case 2:
|
||||||
|
throw Exception("Insufficient funds to pay for transaction fee!");
|
||||||
|
default:
|
||||||
|
throw Exception("Transaction failed with error code $result");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final hex = result["hex"];
|
||||||
|
if (hex is String) {
|
||||||
|
final fee = result["fee"] as int;
|
||||||
|
final vSize = result["vSize"] as int;
|
||||||
|
|
||||||
|
Logging.instance.log("txHex: $hex", level: LogLevel.Info);
|
||||||
|
Logging.instance.log("fee: $fee", level: LogLevel.Info);
|
||||||
|
Logging.instance.log("vsize: $vSize", level: LogLevel.Info);
|
||||||
|
// fee should never be less than vSize sanity check
|
||||||
|
if (fee < vSize) {
|
||||||
|
throw Exception(
|
||||||
|
"Error in fee calculation: Transaction fee cannot be less than vSize");
|
||||||
|
}
|
||||||
|
return result as Map<String, dynamic>;
|
||||||
|
} else {
|
||||||
|
throw Exception("sent hex is not a String!!!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||||
late final int rate;
|
late final int rate;
|
||||||
if (feeRateType is FeeRateType) {
|
if (feeRateType is FeeRateType) {
|
||||||
int fee = 0;
|
int fee = 0;
|
||||||
|
@ -958,6 +1009,8 @@ class DogecoinWallet extends CoinServiceAPI
|
||||||
case FeeRateType.slow:
|
case FeeRateType.slow:
|
||||||
fee = feeObject.slow;
|
fee = feeObject.slow;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
throw ArgumentError("Invalid use of custom fee");
|
||||||
}
|
}
|
||||||
rate = fee;
|
rate = fee;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2092,6 +2145,7 @@ class DogecoinWallet extends CoinServiceAPI
|
||||||
required String recipientAddress,
|
required String recipientAddress,
|
||||||
required bool coinControl,
|
required bool coinControl,
|
||||||
required bool isSendAll,
|
required bool isSendAll,
|
||||||
|
int? satsPerVByte,
|
||||||
int additionalOutputs = 0,
|
int additionalOutputs = 0,
|
||||||
List<isar_models.UTXO>? utxos,
|
List<isar_models.UTXO>? utxos,
|
||||||
}) async {
|
}) async {
|
||||||
|
@ -2199,10 +2253,12 @@ class DogecoinWallet extends CoinServiceAPI
|
||||||
recipients: [recipientAddress],
|
recipients: [recipientAddress],
|
||||||
satoshiAmounts: [satoshisBeingUsed - 1],
|
satoshiAmounts: [satoshisBeingUsed - 1],
|
||||||
))["vSize"] as int;
|
))["vSize"] as int;
|
||||||
int feeForOneOutput = estimateTxFee(
|
int feeForOneOutput = satsPerVByte != null
|
||||||
vSize: vSizeForOneOutput,
|
? (satsPerVByte * vSizeForOneOutput)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForOneOutput,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
if (feeForOneOutput < (vSizeForOneOutput + 1) * 1000) {
|
if (feeForOneOutput < (vSizeForOneOutput + 1) * 1000) {
|
||||||
feeForOneOutput = (vSizeForOneOutput + 1) * 1000;
|
feeForOneOutput = (vSizeForOneOutput + 1) * 1000;
|
||||||
}
|
}
|
||||||
|
@ -2248,15 +2304,19 @@ class DogecoinWallet extends CoinServiceAPI
|
||||||
debugPrint("vSizeForTwoOutPuts $vSizeForTwoOutPuts");
|
debugPrint("vSizeForTwoOutPuts $vSizeForTwoOutPuts");
|
||||||
|
|
||||||
// Assume 1 output, only for recipient and no change
|
// Assume 1 output, only for recipient and no change
|
||||||
var feeForOneOutput = estimateTxFee(
|
var feeForOneOutput = satsPerVByte != null
|
||||||
vSize: vSizeForOneOutput,
|
? (satsPerVByte * vSizeForOneOutput)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForOneOutput,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
// Assume 2 outputs, one for recipient and one for change
|
// Assume 2 outputs, one for recipient and one for change
|
||||||
var feeForTwoOutputs = estimateTxFee(
|
var feeForTwoOutputs = satsPerVByte != null
|
||||||
vSize: vSizeForTwoOutPuts,
|
? (satsPerVByte * vSizeForTwoOutPuts)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForTwoOutPuts,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
|
|
||||||
Logging.instance
|
Logging.instance
|
||||||
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
||||||
|
@ -2463,6 +2523,7 @@ class DogecoinWallet extends CoinServiceAPI
|
||||||
return coinSelection(
|
return coinSelection(
|
||||||
satoshiAmountToSend: satoshiAmountToSend,
|
satoshiAmountToSend: satoshiAmountToSend,
|
||||||
selectedTxFeeRate: selectedTxFeeRate,
|
selectedTxFeeRate: selectedTxFeeRate,
|
||||||
|
satsPerVByte: satsPerVByte,
|
||||||
recipientAddress: recipientAddress,
|
recipientAddress: recipientAddress,
|
||||||
isSendAll: isSendAll,
|
isSendAll: isSendAll,
|
||||||
additionalOutputs: additionalOutputs + 1,
|
additionalOutputs: additionalOutputs + 1,
|
||||||
|
@ -2604,7 +2665,10 @@ class DogecoinWallet extends CoinServiceAPI
|
||||||
Logging.instance
|
Logging.instance
|
||||||
.log("Starting buildTransaction ----------", level: LogLevel.Info);
|
.log("Starting buildTransaction ----------", level: LogLevel.Info);
|
||||||
|
|
||||||
final txb = TransactionBuilder(network: network);
|
final txb = TransactionBuilder(
|
||||||
|
network: network,
|
||||||
|
maximumFeeRate: 2500000, // 1000x default value in bitcoindart lib
|
||||||
|
);
|
||||||
txb.setVersion(1);
|
txb.setVersion(1);
|
||||||
|
|
||||||
// Add transaction inputs
|
// Add transaction inputs
|
||||||
|
|
|
@ -1409,6 +1409,7 @@ class ECashWallet extends CoinServiceAPI
|
||||||
required String recipientAddress,
|
required String recipientAddress,
|
||||||
required bool coinControl,
|
required bool coinControl,
|
||||||
required bool isSendAll,
|
required bool isSendAll,
|
||||||
|
int? satsPerVByte,
|
||||||
int additionalOutputs = 0,
|
int additionalOutputs = 0,
|
||||||
List<isar_models.UTXO>? utxos,
|
List<isar_models.UTXO>? utxos,
|
||||||
}) async {
|
}) async {
|
||||||
|
@ -1517,18 +1518,22 @@ class ECashWallet extends CoinServiceAPI
|
||||||
recipients: [recipientAddress],
|
recipients: [recipientAddress],
|
||||||
satoshiAmounts: [satoshisBeingUsed - 1],
|
satoshiAmounts: [satoshisBeingUsed - 1],
|
||||||
))["vSize"] as int;
|
))["vSize"] as int;
|
||||||
int feeForOneOutput = estimateTxFee(
|
int feeForOneOutput = satsPerVByte != null
|
||||||
vSize: vSizeForOneOutput,
|
? (satsPerVByte * vSizeForOneOutput)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForOneOutput,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
|
|
||||||
final int roughEstimate = roughFeeEstimate(
|
if (satsPerVByte == null) {
|
||||||
spendableOutputs.length,
|
final int roughEstimate = roughFeeEstimate(
|
||||||
1,
|
spendableOutputs.length,
|
||||||
selectedTxFeeRate,
|
1,
|
||||||
).raw.toInt();
|
selectedTxFeeRate,
|
||||||
if (feeForOneOutput < roughEstimate) {
|
).raw.toInt();
|
||||||
feeForOneOutput = roughEstimate;
|
if (feeForOneOutput < roughEstimate) {
|
||||||
|
feeForOneOutput = roughEstimate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final int amount = satoshiAmountToSend - feeForOneOutput;
|
final int amount = satoshiAmountToSend - feeForOneOutput;
|
||||||
|
@ -1586,15 +1591,19 @@ class ECashWallet extends CoinServiceAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assume 1 output, only for recipient and no change
|
// Assume 1 output, only for recipient and no change
|
||||||
final feeForOneOutput = estimateTxFee(
|
final feeForOneOutput = satsPerVByte != null
|
||||||
vSize: vSizeForOneOutput,
|
? (satsPerVByte * vSizeForOneOutput)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForOneOutput,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
// Assume 2 outputs, one for recipient and one for change
|
// Assume 2 outputs, one for recipient and one for change
|
||||||
final feeForTwoOutputs = estimateTxFee(
|
final feeForTwoOutputs = satsPerVByte != null
|
||||||
vSize: vSizeForTwoOutPuts,
|
? (satsPerVByte * vSizeForTwoOutPuts)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForTwoOutPuts,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
|
|
||||||
Logging.instance
|
Logging.instance
|
||||||
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
||||||
|
@ -1795,6 +1804,7 @@ class ECashWallet extends CoinServiceAPI
|
||||||
return coinSelection(
|
return coinSelection(
|
||||||
satoshiAmountToSend: satoshiAmountToSend,
|
satoshiAmountToSend: satoshiAmountToSend,
|
||||||
selectedTxFeeRate: selectedTxFeeRate,
|
selectedTxFeeRate: selectedTxFeeRate,
|
||||||
|
satsPerVByte: satsPerVByte,
|
||||||
recipientAddress: recipientAddress,
|
recipientAddress: recipientAddress,
|
||||||
isSendAll: isSendAll,
|
isSendAll: isSendAll,
|
||||||
additionalOutputs: additionalOutputs + 1,
|
additionalOutputs: additionalOutputs + 1,
|
||||||
|
@ -1977,8 +1987,6 @@ class ECashWallet extends CoinServiceAPI
|
||||||
final tx = builder.build();
|
final tx = builder.build();
|
||||||
final txHex = tx.toHex();
|
final txHex = tx.toHex();
|
||||||
final vSize = tx.virtualSize();
|
final vSize = tx.virtualSize();
|
||||||
//todo: check if print needed
|
|
||||||
Logger.print("ecash raw hex: $txHex");
|
|
||||||
|
|
||||||
return {"hex": txHex, "vSize": vSize};
|
return {"hex": txHex, "vSize": vSize};
|
||||||
}
|
}
|
||||||
|
@ -2549,9 +2557,60 @@ class ECashWallet extends CoinServiceAPI
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final feeRateType = args?["feeRate"];
|
final feeRateType = args?["feeRate"];
|
||||||
|
final customSatsPerVByte = args?["satsPerVByte"] as int?;
|
||||||
final feeRateAmount = args?["feeRateAmount"];
|
final feeRateAmount = args?["feeRateAmount"];
|
||||||
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
||||||
if (feeRateType is FeeRateType || feeRateAmount is int) {
|
|
||||||
|
if (customSatsPerVByte != null) {
|
||||||
|
// check for send all
|
||||||
|
bool isSendAll = false;
|
||||||
|
if (amount == balance.spendable) {
|
||||||
|
isSendAll = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final bool coinControl = utxos != null;
|
||||||
|
|
||||||
|
final result = await coinSelection(
|
||||||
|
satoshiAmountToSend: amount.raw.toInt(),
|
||||||
|
selectedTxFeeRate: -1,
|
||||||
|
satsPerVByte: customSatsPerVByte,
|
||||||
|
recipientAddress: address,
|
||||||
|
isSendAll: isSendAll,
|
||||||
|
utxos: utxos?.toList(),
|
||||||
|
coinControl: coinControl,
|
||||||
|
);
|
||||||
|
|
||||||
|
Logging.instance
|
||||||
|
.log("PREPARE SEND RESULT: $result", level: LogLevel.Info);
|
||||||
|
if (result is int) {
|
||||||
|
switch (result) {
|
||||||
|
case 1:
|
||||||
|
throw Exception("Insufficient balance!");
|
||||||
|
case 2:
|
||||||
|
throw Exception("Insufficient funds to pay for transaction fee!");
|
||||||
|
default:
|
||||||
|
throw Exception("Transaction failed with error code $result");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final hex = result["hex"];
|
||||||
|
if (hex is String) {
|
||||||
|
final fee = result["fee"] as int;
|
||||||
|
final vSize = result["vSize"] as int;
|
||||||
|
|
||||||
|
Logging.instance.log("txHex: $hex", level: LogLevel.Info);
|
||||||
|
Logging.instance.log("fee: $fee", level: LogLevel.Info);
|
||||||
|
Logging.instance.log("vsize: $vSize", level: LogLevel.Info);
|
||||||
|
// fee should never be less than vSize sanity check
|
||||||
|
if (fee < vSize) {
|
||||||
|
throw Exception(
|
||||||
|
"Error in fee calculation: Transaction fee cannot be less than vSize");
|
||||||
|
}
|
||||||
|
return result as Map<String, dynamic>;
|
||||||
|
} else {
|
||||||
|
throw Exception("sent hex is not a String!!!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||||
late final int rate;
|
late final int rate;
|
||||||
if (feeRateType is FeeRateType) {
|
if (feeRateType is FeeRateType) {
|
||||||
int fee = 0;
|
int fee = 0;
|
||||||
|
@ -2566,6 +2625,9 @@ class ECashWallet extends CoinServiceAPI
|
||||||
case FeeRateType.slow:
|
case FeeRateType.slow:
|
||||||
fee = feeObject.slow;
|
fee = feeObject.slow;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw ArgumentError("Invalid use of custom fee");
|
||||||
}
|
}
|
||||||
rate = fee;
|
rate = fee;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -156,6 +156,7 @@ Future<void> executeNative(Map<String, dynamic> arguments) async {
|
||||||
final secretKeyIndex = arguments['secretKeyIndex'] as int?;
|
final secretKeyIndex = arguments['secretKeyIndex'] as int?;
|
||||||
final epicboxConfig = arguments['epicboxConfig'] as String?;
|
final epicboxConfig = arguments['epicboxConfig'] as String?;
|
||||||
final minimumConfirmations = arguments['minimumConfirmations'] as int?;
|
final minimumConfirmations = arguments['minimumConfirmations'] as int?;
|
||||||
|
final onChainNote = arguments['onChainNote'] as String?;
|
||||||
|
|
||||||
Map<String, dynamic> result = {};
|
Map<String, dynamic> result = {};
|
||||||
if (!(wallet == null ||
|
if (!(wallet == null ||
|
||||||
|
@ -165,7 +166,7 @@ Future<void> executeNative(Map<String, dynamic> arguments) async {
|
||||||
epicboxConfig == null ||
|
epicboxConfig == null ||
|
||||||
minimumConfirmations == null)) {
|
minimumConfirmations == null)) {
|
||||||
var res = await createTransaction(wallet, amount, address,
|
var res = await createTransaction(wallet, amount, address,
|
||||||
secretKeyIndex, epicboxConfig, minimumConfirmations);
|
secretKeyIndex, epicboxConfig, minimumConfirmations, onChainNote!);
|
||||||
result['result'] = res;
|
result['result'] = res;
|
||||||
sendPort.send(result);
|
sendPort.send(result);
|
||||||
return;
|
return;
|
||||||
|
@ -175,7 +176,7 @@ Future<void> executeNative(Map<String, dynamic> arguments) async {
|
||||||
final selectionStrategyIsAll =
|
final selectionStrategyIsAll =
|
||||||
arguments['selectionStrategyIsAll'] as int?;
|
arguments['selectionStrategyIsAll'] as int?;
|
||||||
final minimumConfirmations = arguments['minimumConfirmations'] as int?;
|
final minimumConfirmations = arguments['minimumConfirmations'] as int?;
|
||||||
final message = arguments['message'] as String?;
|
final message = arguments['onChainNote'] as String?;
|
||||||
final amount = arguments['amount'] as int?;
|
final amount = arguments['amount'] as int?;
|
||||||
final address = arguments['address'] as String?;
|
final address = arguments['address'] as String?;
|
||||||
|
|
||||||
|
@ -459,6 +460,7 @@ class EpicCashWallet extends CoinServiceAPI
|
||||||
|
|
||||||
// TODO determine whether it is worth sending change to a change address.
|
// TODO determine whether it is worth sending change to a change address.
|
||||||
dynamic message;
|
dynamic message;
|
||||||
|
print("THIS TX DATA IS $txData");
|
||||||
|
|
||||||
String receiverAddress = txData['addresss'] as String;
|
String receiverAddress = txData['addresss'] as String;
|
||||||
|
|
||||||
|
@ -480,7 +482,7 @@ class EpicCashWallet extends CoinServiceAPI
|
||||||
"wallet": wallet!,
|
"wallet": wallet!,
|
||||||
"selectionStrategyIsAll": selectionStrategyIsAll,
|
"selectionStrategyIsAll": selectionStrategyIsAll,
|
||||||
"minimumConfirmations": MINIMUM_CONFIRMATIONS,
|
"minimumConfirmations": MINIMUM_CONFIRMATIONS,
|
||||||
"message": "",
|
"message": txData['onChainNote'],
|
||||||
"amount": (txData['recipientAmt'] as Amount).raw.toInt(),
|
"amount": (txData['recipientAmt'] as Amount).raw.toInt(),
|
||||||
"address": txData['addresss'] as String,
|
"address": txData['addresss'] as String,
|
||||||
}, name: walletName);
|
}, name: walletName);
|
||||||
|
@ -504,6 +506,7 @@ class EpicCashWallet extends CoinServiceAPI
|
||||||
"secretKeyIndex": 0,
|
"secretKeyIndex": 0,
|
||||||
"epicboxConfig": epicboxConfig.toString(),
|
"epicboxConfig": epicboxConfig.toString(),
|
||||||
"minimumConfirmations": MINIMUM_CONFIRMATIONS,
|
"minimumConfirmations": MINIMUM_CONFIRMATIONS,
|
||||||
|
"onChainNote": txData['onChainNote'],
|
||||||
}, name: walletName);
|
}, name: walletName);
|
||||||
|
|
||||||
message = await receivePort.first;
|
message = await receivePort.first;
|
||||||
|
@ -1721,6 +1724,8 @@ class EpicCashWallet extends CoinServiceAPI
|
||||||
"";
|
"";
|
||||||
String? commitId = slatesToCommits[slateId]?['commitId'] as String?;
|
String? commitId = slatesToCommits[slateId]?['commitId'] as String?;
|
||||||
tx['numberOfMessages'] = tx['messages']?['messages']?.length;
|
tx['numberOfMessages'] = tx['messages']?['messages']?.length;
|
||||||
|
tx['onChainNote'] = tx['messages']?['messages']?[0]?['message'];
|
||||||
|
print("ON CHAIN MESSAGE IS ${tx['onChainNote']}");
|
||||||
|
|
||||||
int? height;
|
int? height;
|
||||||
|
|
||||||
|
@ -1754,7 +1759,8 @@ class EpicCashWallet extends CoinServiceAPI
|
||||||
isLelantus: false,
|
isLelantus: false,
|
||||||
slateId: slateId,
|
slateId: slateId,
|
||||||
nonce: null,
|
nonce: null,
|
||||||
otherData: tx["id"].toString(),
|
// otherData: tx["id"].toString(),
|
||||||
|
otherData: tx['onChainNote'].toString(),
|
||||||
inputs: [],
|
inputs: [],
|
||||||
outputs: [],
|
outputs: [],
|
||||||
numberOfMessages: ((tx["numberOfMessages"] == null) ? 0 : tx["numberOfMessages"]) as int,
|
numberOfMessages: ((tx["numberOfMessages"] == null) ? 0 : tx["numberOfMessages"]) as int,
|
||||||
|
|
|
@ -265,17 +265,23 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
||||||
@override
|
@override
|
||||||
Future<FeeObject> get fees => EthereumAPI.getFees();
|
Future<FeeObject> get fees => EthereumAPI.getFees();
|
||||||
|
|
||||||
//Full rescan is not needed for ETH since we have a balance
|
|
||||||
@override
|
@override
|
||||||
Future<void> fullRescan(
|
Future<void> fullRescan(
|
||||||
int maxUnusedAddressGap, int maxNumberOfIndexesToCheck) {
|
int maxUnusedAddressGap,
|
||||||
// TODO: implement fullRescan
|
int maxNumberOfIndexesToCheck,
|
||||||
throw UnimplementedError();
|
) async {
|
||||||
|
await db.deleteWalletBlockchainData(walletId);
|
||||||
|
await _generateAndSaveAddress(
|
||||||
|
(await mnemonicString)!,
|
||||||
|
(await mnemonicPassphrase)!,
|
||||||
|
);
|
||||||
|
await updateBalance();
|
||||||
|
await _refreshTransactions(isRescan: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> generateNewAddress() {
|
Future<bool> generateNewAddress() {
|
||||||
// TODO: implement generateNewAddress - might not be needed for ETH
|
// not used for ETH
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,10 +297,10 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
||||||
level: LogLevel.Info,
|
level: LogLevel.Info,
|
||||||
);
|
);
|
||||||
|
|
||||||
//First get mnemonic so we can initialize credentials
|
await _initCredentials(
|
||||||
String privateKey =
|
(await mnemonicString)!,
|
||||||
getPrivateKey((await mnemonicString)!, (await mnemonicPassphrase)!);
|
(await mnemonicPassphrase)!,
|
||||||
_credentials = web3.EthPrivateKey.fromHex(privateKey);
|
);
|
||||||
|
|
||||||
if (getCachedId() == null) {
|
if (getCachedId() == null) {
|
||||||
throw Exception(
|
throw Exception(
|
||||||
|
@ -367,8 +373,24 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
||||||
value: "",
|
value: "",
|
||||||
);
|
);
|
||||||
|
|
||||||
String privateKey = getPrivateKey(mnemonic, "");
|
await _generateAndSaveAddress(mnemonic, "");
|
||||||
|
|
||||||
|
Logging.instance.log("_generateNewWalletFinished", level: LogLevel.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _initCredentials(
|
||||||
|
String mnemonic,
|
||||||
|
String mnemonicPassphrase,
|
||||||
|
) async {
|
||||||
|
String privateKey = getPrivateKey(mnemonic, mnemonicPassphrase);
|
||||||
_credentials = web3.EthPrivateKey.fromHex(privateKey);
|
_credentials = web3.EthPrivateKey.fromHex(privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _generateAndSaveAddress(
|
||||||
|
String mnemonic,
|
||||||
|
String mnemonicPassphrase,
|
||||||
|
) async {
|
||||||
|
await _initCredentials(mnemonic, mnemonicPassphrase);
|
||||||
|
|
||||||
final address = Address(
|
final address = Address(
|
||||||
walletId: walletId,
|
walletId: walletId,
|
||||||
|
@ -381,8 +403,6 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
||||||
);
|
);
|
||||||
|
|
||||||
await db.putAddress(address);
|
await db.putAddress(address);
|
||||||
|
|
||||||
Logging.instance.log("_generateNewWalletFinished", level: LogLevel.Info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isConnected = false;
|
bool _isConnected = false;
|
||||||
|
@ -649,7 +669,7 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
||||||
if (!needsRefresh) {
|
if (!needsRefresh) {
|
||||||
var allOwnAddresses = await _fetchAllOwnAddresses();
|
var allOwnAddresses = await _fetchAllOwnAddresses();
|
||||||
final response = await EthereumAPI.getEthTransactions(
|
final response = await EthereumAPI.getEthTransactions(
|
||||||
allOwnAddresses.elementAt(0).value,
|
address: allOwnAddresses.elementAt(0).value,
|
||||||
);
|
);
|
||||||
if (response.value != null) {
|
if (response.value != null) {
|
||||||
final allTxs = response.value!;
|
final allTxs = response.value!;
|
||||||
|
@ -985,10 +1005,26 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
||||||
return isValidEthereumAddress(address);
|
return isValidEthereumAddress(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _refreshTransactions() async {
|
Future<void> _refreshTransactions({bool isRescan = false}) async {
|
||||||
String thisAddress = await currentReceivingAddress;
|
String thisAddress = await currentReceivingAddress;
|
||||||
|
|
||||||
final response = await EthereumAPI.getEthTransactions(thisAddress);
|
int firstBlock = 0;
|
||||||
|
|
||||||
|
if (!isRescan) {
|
||||||
|
firstBlock =
|
||||||
|
await db.getTransactions(walletId).heightProperty().max() ?? 0;
|
||||||
|
|
||||||
|
if (firstBlock > 10) {
|
||||||
|
// add some buffer
|
||||||
|
firstBlock -= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final response = await EthereumAPI.getEthTransactions(
|
||||||
|
address: thisAddress,
|
||||||
|
firstBlock: isRescan ? 0 : firstBlock,
|
||||||
|
includeTokens: true,
|
||||||
|
);
|
||||||
|
|
||||||
if (response.value == null) {
|
if (response.value == null) {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
|
@ -999,6 +1035,11 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response.value!.isEmpty) {
|
||||||
|
// no new transactions found
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final txsResponse =
|
final txsResponse =
|
||||||
await EthereumAPI.getEthTransactionNonces(response.value!);
|
await EthereumAPI.getEthTransactionNonces(response.value!);
|
||||||
|
|
||||||
|
@ -1017,8 +1058,10 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
||||||
txFailed = true;
|
txFailed = true;
|
||||||
}
|
}
|
||||||
isIncoming = false;
|
isIncoming = false;
|
||||||
} else {
|
} else if (checksumEthereumAddress(element.to) == thisAddress) {
|
||||||
isIncoming = true;
|
isIncoming = true;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Calculate fees (GasLimit * gasPrice)
|
//Calculate fees (GasLimit * gasPrice)
|
||||||
|
|
|
@ -1029,8 +1029,55 @@ class FiroWallet extends CoinServiceAPI
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final feeRateType = args?["feeRate"];
|
final feeRateType = args?["feeRate"];
|
||||||
|
final customSatsPerVByte = args?["satsPerVByte"] as int?;
|
||||||
final feeRateAmount = args?["feeRateAmount"];
|
final feeRateAmount = args?["feeRateAmount"];
|
||||||
if (feeRateType is FeeRateType || feeRateAmount is int) {
|
|
||||||
|
if (customSatsPerVByte != null) {
|
||||||
|
// check for send all
|
||||||
|
bool isSendAll = false;
|
||||||
|
if (amount == balance.spendable) {
|
||||||
|
isSendAll = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final result = await coinSelection(
|
||||||
|
amount.raw.toInt(),
|
||||||
|
-1,
|
||||||
|
address,
|
||||||
|
isSendAll,
|
||||||
|
satsPerVByte: customSatsPerVByte,
|
||||||
|
);
|
||||||
|
|
||||||
|
Logging.instance
|
||||||
|
.log("PREPARE SEND RESULT: $result", level: LogLevel.Info);
|
||||||
|
if (result is int) {
|
||||||
|
switch (result) {
|
||||||
|
case 1:
|
||||||
|
throw Exception("Insufficient balance!");
|
||||||
|
case 2:
|
||||||
|
throw Exception("Insufficient funds to pay for transaction fee!");
|
||||||
|
default:
|
||||||
|
throw Exception("Transaction failed with error code $result");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final hex = result["hex"];
|
||||||
|
if (hex is String) {
|
||||||
|
final fee = result["fee"] as int;
|
||||||
|
final vSize = result["vSize"] as int;
|
||||||
|
|
||||||
|
Logging.instance.log("txHex: $hex", level: LogLevel.Info);
|
||||||
|
Logging.instance.log("fee: $fee", level: LogLevel.Info);
|
||||||
|
Logging.instance.log("vsize: $vSize", level: LogLevel.Info);
|
||||||
|
// fee should never be less than vSize sanity check
|
||||||
|
if (fee < vSize) {
|
||||||
|
throw Exception(
|
||||||
|
"Error in fee calculation: Transaction fee cannot be less than vSize");
|
||||||
|
}
|
||||||
|
return result as Map<String, dynamic>;
|
||||||
|
} else {
|
||||||
|
throw Exception("sent hex is not a String!!!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||||
late final int rate;
|
late final int rate;
|
||||||
if (feeRateType is FeeRateType) {
|
if (feeRateType is FeeRateType) {
|
||||||
int fee = 0;
|
int fee = 0;
|
||||||
|
@ -1045,6 +1092,8 @@ class FiroWallet extends CoinServiceAPI
|
||||||
case FeeRateType.slow:
|
case FeeRateType.slow:
|
||||||
fee = feeObject.slow;
|
fee = feeObject.slow;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
throw ArgumentError("Invalid use of custom fee");
|
||||||
}
|
}
|
||||||
rate = fee;
|
rate = fee;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1296,6 +1345,7 @@ class FiroWallet extends CoinServiceAPI
|
||||||
int selectedTxFeeRate,
|
int selectedTxFeeRate,
|
||||||
String _recipientAddress,
|
String _recipientAddress,
|
||||||
bool isSendAll, {
|
bool isSendAll, {
|
||||||
|
int? satsPerVByte,
|
||||||
int additionalOutputs = 0,
|
int additionalOutputs = 0,
|
||||||
List<isar_models.UTXO>? utxos,
|
List<isar_models.UTXO>? utxos,
|
||||||
}) async {
|
}) async {
|
||||||
|
@ -1385,10 +1435,12 @@ class FiroWallet extends CoinServiceAPI
|
||||||
recipients: [_recipientAddress],
|
recipients: [_recipientAddress],
|
||||||
satoshiAmounts: [satoshisBeingUsed - 1],
|
satoshiAmounts: [satoshisBeingUsed - 1],
|
||||||
))["vSize"] as int;
|
))["vSize"] as int;
|
||||||
int feeForOneOutput = estimateTxFee(
|
int feeForOneOutput = satsPerVByte != null
|
||||||
vSize: vSizeForOneOutput,
|
? (satsPerVByte * vSizeForOneOutput)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForOneOutput,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
|
|
||||||
if (feeForOneOutput < vSizeForOneOutput + 1) {
|
if (feeForOneOutput < vSizeForOneOutput + 1) {
|
||||||
feeForOneOutput = vSizeForOneOutput + 1;
|
feeForOneOutput = vSizeForOneOutput + 1;
|
||||||
|
@ -1429,20 +1481,21 @@ class FiroWallet extends CoinServiceAPI
|
||||||
satoshisBeingUsed - satoshiAmountToSend - 1,
|
satoshisBeingUsed - satoshiAmountToSend - 1,
|
||||||
], // dust limit is the minimum amount a change output should be
|
], // dust limit is the minimum amount a change output should be
|
||||||
))["vSize"] as int;
|
))["vSize"] as int;
|
||||||
//todo: check if print needed
|
|
||||||
debugPrint("vSizeForOneOutput $vSizeForOneOutput");
|
|
||||||
debugPrint("vSizeForTwoOutPuts $vSizeForTwoOutPuts");
|
|
||||||
|
|
||||||
// Assume 1 output, only for recipient and no change
|
// Assume 1 output, only for recipient and no change
|
||||||
var feeForOneOutput = estimateTxFee(
|
var feeForOneOutput = satsPerVByte != null
|
||||||
vSize: vSizeForOneOutput,
|
? (satsPerVByte * vSizeForOneOutput)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForOneOutput,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
// Assume 2 outputs, one for recipient and one for change
|
// Assume 2 outputs, one for recipient and one for change
|
||||||
var feeForTwoOutputs = estimateTxFee(
|
var feeForTwoOutputs = satsPerVByte != null
|
||||||
vSize: vSizeForTwoOutPuts,
|
? (satsPerVByte * vSizeForTwoOutPuts)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForTwoOutPuts,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
|
|
||||||
Logging.instance
|
Logging.instance
|
||||||
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
||||||
|
@ -1641,9 +1694,15 @@ class FiroWallet extends CoinServiceAPI
|
||||||
level: LogLevel.Warning);
|
level: LogLevel.Warning);
|
||||||
// try adding more outputs
|
// try adding more outputs
|
||||||
if (spendableOutputs.length > inputsBeingConsumed) {
|
if (spendableOutputs.length > inputsBeingConsumed) {
|
||||||
return coinSelection(satoshiAmountToSend, selectedTxFeeRate,
|
return coinSelection(
|
||||||
_recipientAddress, isSendAll,
|
satoshiAmountToSend,
|
||||||
additionalOutputs: additionalOutputs + 1, utxos: utxos);
|
selectedTxFeeRate,
|
||||||
|
_recipientAddress,
|
||||||
|
isSendAll,
|
||||||
|
additionalOutputs: additionalOutputs + 1,
|
||||||
|
satsPerVByte: satsPerVByte,
|
||||||
|
utxos: utxos,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,8 +232,8 @@ class LitecoinWallet extends CoinServiceAPI
|
||||||
@override
|
@override
|
||||||
Future<int> get maxFee async {
|
Future<int> get maxFee async {
|
||||||
final fee = (await fees).fast as String;
|
final fee = (await fees).fast as String;
|
||||||
final satsFee =
|
final satsFee = Decimal.parse(fee) *
|
||||||
Decimal.parse(fee) * Decimal.fromInt(Constants.satsPerCoin(coin).toInt());
|
Decimal.fromInt(Constants.satsPerCoin(coin).toInt());
|
||||||
return satsFee.floor().toBigInt().toInt();
|
return satsFee.floor().toBigInt().toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1058,9 +1058,60 @@ class LitecoinWallet extends CoinServiceAPI
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final feeRateType = args?["feeRate"];
|
final feeRateType = args?["feeRate"];
|
||||||
|
final customSatsPerVByte = args?["satsPerVByte"] as int?;
|
||||||
final feeRateAmount = args?["feeRateAmount"];
|
final feeRateAmount = args?["feeRateAmount"];
|
||||||
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
||||||
if (feeRateType is FeeRateType || feeRateAmount is int) {
|
|
||||||
|
if (customSatsPerVByte != null) {
|
||||||
|
// check for send all
|
||||||
|
bool isSendAll = false;
|
||||||
|
if (amount == balance.spendable) {
|
||||||
|
isSendAll = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final bool coinControl = utxos != null;
|
||||||
|
|
||||||
|
final result = await coinSelection(
|
||||||
|
satoshiAmountToSend: amount.raw.toInt(),
|
||||||
|
selectedTxFeeRate: -1,
|
||||||
|
satsPerVByte: customSatsPerVByte,
|
||||||
|
recipientAddress: address,
|
||||||
|
isSendAll: isSendAll,
|
||||||
|
utxos: utxos?.toList(),
|
||||||
|
coinControl: coinControl,
|
||||||
|
);
|
||||||
|
|
||||||
|
Logging.instance
|
||||||
|
.log("PREPARE SEND RESULT: $result", level: LogLevel.Info);
|
||||||
|
if (result is int) {
|
||||||
|
switch (result) {
|
||||||
|
case 1:
|
||||||
|
throw Exception("Insufficient balance!");
|
||||||
|
case 2:
|
||||||
|
throw Exception("Insufficient funds to pay for transaction fee!");
|
||||||
|
default:
|
||||||
|
throw Exception("Transaction failed with error code $result");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final hex = result["hex"];
|
||||||
|
if (hex is String) {
|
||||||
|
final fee = result["fee"] as int;
|
||||||
|
final vSize = result["vSize"] as int;
|
||||||
|
|
||||||
|
Logging.instance.log("txHex: $hex", level: LogLevel.Info);
|
||||||
|
Logging.instance.log("fee: $fee", level: LogLevel.Info);
|
||||||
|
Logging.instance.log("vsize: $vSize", level: LogLevel.Info);
|
||||||
|
// fee should never be less than vSize sanity check
|
||||||
|
if (fee < vSize) {
|
||||||
|
throw Exception(
|
||||||
|
"Error in fee calculation: Transaction fee cannot be less than vSize");
|
||||||
|
}
|
||||||
|
return result as Map<String, dynamic>;
|
||||||
|
} else {
|
||||||
|
throw Exception("sent hex is not a String!!!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||||
late final int rate;
|
late final int rate;
|
||||||
if (feeRateType is FeeRateType) {
|
if (feeRateType is FeeRateType) {
|
||||||
int fee = 0;
|
int fee = 0;
|
||||||
|
@ -1075,6 +1126,8 @@ class LitecoinWallet extends CoinServiceAPI
|
||||||
case FeeRateType.slow:
|
case FeeRateType.slow:
|
||||||
fee = feeObject.slow;
|
fee = feeObject.slow;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
throw ArgumentError("Invalid use of custom fee");
|
||||||
}
|
}
|
||||||
rate = fee;
|
rate = fee;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2298,6 +2351,7 @@ class LitecoinWallet extends CoinServiceAPI
|
||||||
required String recipientAddress,
|
required String recipientAddress,
|
||||||
required bool coinControl,
|
required bool coinControl,
|
||||||
required bool isSendAll,
|
required bool isSendAll,
|
||||||
|
int? satsPerVByte,
|
||||||
int additionalOutputs = 0,
|
int additionalOutputs = 0,
|
||||||
List<isar_models.UTXO>? utxos,
|
List<isar_models.UTXO>? utxos,
|
||||||
}) async {
|
}) async {
|
||||||
|
@ -2403,18 +2457,22 @@ class LitecoinWallet extends CoinServiceAPI
|
||||||
recipients: [recipientAddress],
|
recipients: [recipientAddress],
|
||||||
satoshiAmounts: [satoshisBeingUsed - 1],
|
satoshiAmounts: [satoshisBeingUsed - 1],
|
||||||
))["vSize"] as int;
|
))["vSize"] as int;
|
||||||
int feeForOneOutput = estimateTxFee(
|
int feeForOneOutput = satsPerVByte != null
|
||||||
vSize: vSizeForOneOutput,
|
? (satsPerVByte * vSizeForOneOutput)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForOneOutput,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
|
|
||||||
final int roughEstimate = roughFeeEstimate(
|
if (satsPerVByte == null) {
|
||||||
spendableOutputs.length,
|
final int roughEstimate = roughFeeEstimate(
|
||||||
1,
|
spendableOutputs.length,
|
||||||
selectedTxFeeRate,
|
1,
|
||||||
).raw.toInt();
|
selectedTxFeeRate,
|
||||||
if (feeForOneOutput < roughEstimate) {
|
).raw.toInt();
|
||||||
feeForOneOutput = roughEstimate;
|
if (feeForOneOutput < roughEstimate) {
|
||||||
|
feeForOneOutput = roughEstimate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final int amount = satoshiAmountToSend - feeForOneOutput;
|
final int amount = satoshiAmountToSend - feeForOneOutput;
|
||||||
|
@ -2455,15 +2513,19 @@ class LitecoinWallet extends CoinServiceAPI
|
||||||
))["vSize"] as int;
|
))["vSize"] as int;
|
||||||
|
|
||||||
// Assume 1 output, only for recipient and no change
|
// Assume 1 output, only for recipient and no change
|
||||||
final feeForOneOutput = estimateTxFee(
|
final feeForOneOutput = satsPerVByte != null
|
||||||
vSize: vSizeForOneOutput,
|
? (satsPerVByte * vSizeForOneOutput)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForOneOutput,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
// Assume 2 outputs, one for recipient and one for change
|
// Assume 2 outputs, one for recipient and one for change
|
||||||
final feeForTwoOutputs = estimateTxFee(
|
final feeForTwoOutputs = satsPerVByte != null
|
||||||
vSize: vSizeForTwoOutPuts,
|
? (satsPerVByte * vSizeForTwoOutPuts)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForTwoOutPuts,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
|
|
||||||
Logging.instance
|
Logging.instance
|
||||||
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
||||||
|
@ -2659,6 +2721,7 @@ class LitecoinWallet extends CoinServiceAPI
|
||||||
return coinSelection(
|
return coinSelection(
|
||||||
satoshiAmountToSend: satoshiAmountToSend,
|
satoshiAmountToSend: satoshiAmountToSend,
|
||||||
selectedTxFeeRate: selectedTxFeeRate,
|
selectedTxFeeRate: selectedTxFeeRate,
|
||||||
|
satsPerVByte: satsPerVByte,
|
||||||
recipientAddress: recipientAddress,
|
recipientAddress: recipientAddress,
|
||||||
isSendAll: isSendAll,
|
isSendAll: isSendAll,
|
||||||
additionalOutputs: additionalOutputs + 1,
|
additionalOutputs: additionalOutputs + 1,
|
||||||
|
|
|
@ -458,6 +458,8 @@ class MoneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
||||||
case FeeRateType.slow:
|
case FeeRateType.slow:
|
||||||
feePriority = MoneroTransactionPriority.slow;
|
feePriority = MoneroTransactionPriority.slow;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
throw ArgumentError("Invalid use of custom fee");
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<PendingTransaction>? awaitPendingTransaction;
|
Future<PendingTransaction>? awaitPendingTransaction;
|
||||||
|
@ -864,6 +866,10 @@ class MoneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
||||||
//
|
//
|
||||||
String address = walletBase!.getTransactionAddress(chain, index);
|
String address = walletBase!.getTransactionAddress(chain, index);
|
||||||
|
|
||||||
|
if (address.contains("111")) {
|
||||||
|
return await _generateAddressForChain(chain, index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
return isar_models.Address(
|
return isar_models.Address(
|
||||||
walletId: walletId,
|
walletId: walletId,
|
||||||
derivationIndex: index,
|
derivationIndex: index,
|
||||||
|
|
|
@ -224,8 +224,8 @@ class NamecoinWallet extends CoinServiceAPI
|
||||||
@override
|
@override
|
||||||
Future<int> get maxFee async {
|
Future<int> get maxFee async {
|
||||||
final fee = (await fees).fast as String;
|
final fee = (await fees).fast as String;
|
||||||
final satsFee =
|
final satsFee = Decimal.parse(fee) *
|
||||||
Decimal.parse(fee) * Decimal.fromInt(Constants.satsPerCoin(coin).toInt());
|
Decimal.fromInt(Constants.satsPerCoin(coin).toInt());
|
||||||
return satsFee.floor().toBigInt().toInt();
|
return satsFee.floor().toBigInt().toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1048,9 +1048,60 @@ class NamecoinWallet extends CoinServiceAPI
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final feeRateType = args?["feeRate"];
|
final feeRateType = args?["feeRate"];
|
||||||
|
final customSatsPerVByte = args?["satsPerVByte"] as int?;
|
||||||
final feeRateAmount = args?["feeRateAmount"];
|
final feeRateAmount = args?["feeRateAmount"];
|
||||||
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
||||||
if (feeRateType is FeeRateType || feeRateAmount is int) {
|
|
||||||
|
if (customSatsPerVByte != null) {
|
||||||
|
// check for send all
|
||||||
|
bool isSendAll = false;
|
||||||
|
if (amount == balance.spendable) {
|
||||||
|
isSendAll = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final bool coinControl = utxos != null;
|
||||||
|
|
||||||
|
final result = await coinSelection(
|
||||||
|
satoshiAmountToSend: amount.raw.toInt(),
|
||||||
|
selectedTxFeeRate: -1,
|
||||||
|
satsPerVByte: customSatsPerVByte,
|
||||||
|
recipientAddress: address,
|
||||||
|
isSendAll: isSendAll,
|
||||||
|
utxos: utxos?.toList(),
|
||||||
|
coinControl: coinControl,
|
||||||
|
);
|
||||||
|
|
||||||
|
Logging.instance
|
||||||
|
.log("PREPARE SEND RESULT: $result", level: LogLevel.Info);
|
||||||
|
if (result is int) {
|
||||||
|
switch (result) {
|
||||||
|
case 1:
|
||||||
|
throw Exception("Insufficient balance!");
|
||||||
|
case 2:
|
||||||
|
throw Exception("Insufficient funds to pay for transaction fee!");
|
||||||
|
default:
|
||||||
|
throw Exception("Transaction failed with error code $result");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final hex = result["hex"];
|
||||||
|
if (hex is String) {
|
||||||
|
final fee = result["fee"] as int;
|
||||||
|
final vSize = result["vSize"] as int;
|
||||||
|
|
||||||
|
Logging.instance.log("txHex: $hex", level: LogLevel.Info);
|
||||||
|
Logging.instance.log("fee: $fee", level: LogLevel.Info);
|
||||||
|
Logging.instance.log("vsize: $vSize", level: LogLevel.Info);
|
||||||
|
// fee should never be less than vSize sanity check
|
||||||
|
if (fee < vSize) {
|
||||||
|
throw Exception(
|
||||||
|
"Error in fee calculation: Transaction fee cannot be less than vSize");
|
||||||
|
}
|
||||||
|
return result as Map<String, dynamic>;
|
||||||
|
} else {
|
||||||
|
throw Exception("sent hex is not a String!!!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||||
late final int rate;
|
late final int rate;
|
||||||
if (feeRateType is FeeRateType) {
|
if (feeRateType is FeeRateType) {
|
||||||
int fee = 0;
|
int fee = 0;
|
||||||
|
@ -1065,6 +1116,8 @@ class NamecoinWallet extends CoinServiceAPI
|
||||||
case FeeRateType.slow:
|
case FeeRateType.slow:
|
||||||
fee = feeObject.slow;
|
fee = feeObject.slow;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
throw ArgumentError("Invalid use of custom fee");
|
||||||
}
|
}
|
||||||
rate = fee;
|
rate = fee;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2274,6 +2327,7 @@ class NamecoinWallet extends CoinServiceAPI
|
||||||
required String recipientAddress,
|
required String recipientAddress,
|
||||||
required bool coinControl,
|
required bool coinControl,
|
||||||
required bool isSendAll,
|
required bool isSendAll,
|
||||||
|
int? satsPerVByte,
|
||||||
int additionalOutputs = 0,
|
int additionalOutputs = 0,
|
||||||
List<isar_models.UTXO>? utxos,
|
List<isar_models.UTXO>? utxos,
|
||||||
}) async {
|
}) async {
|
||||||
|
@ -2379,18 +2433,22 @@ class NamecoinWallet extends CoinServiceAPI
|
||||||
recipients: [recipientAddress],
|
recipients: [recipientAddress],
|
||||||
satoshiAmounts: [satoshisBeingUsed - 1],
|
satoshiAmounts: [satoshisBeingUsed - 1],
|
||||||
))["vSize"] as int;
|
))["vSize"] as int;
|
||||||
int feeForOneOutput = estimateTxFee(
|
int feeForOneOutput = satsPerVByte != null
|
||||||
vSize: vSizeForOneOutput,
|
? (satsPerVByte * vSizeForOneOutput)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForOneOutput,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
|
|
||||||
final int roughEstimate = roughFeeEstimate(
|
if (satsPerVByte == null) {
|
||||||
spendableOutputs.length,
|
final int roughEstimate = roughFeeEstimate(
|
||||||
1,
|
spendableOutputs.length,
|
||||||
selectedTxFeeRate,
|
1,
|
||||||
).raw.toInt();
|
selectedTxFeeRate,
|
||||||
if (feeForOneOutput < roughEstimate) {
|
).raw.toInt();
|
||||||
feeForOneOutput = roughEstimate;
|
if (feeForOneOutput < roughEstimate) {
|
||||||
|
feeForOneOutput = roughEstimate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final int amount = satoshiAmountToSend - feeForOneOutput;
|
final int amount = satoshiAmountToSend - feeForOneOutput;
|
||||||
|
@ -2431,15 +2489,19 @@ class NamecoinWallet extends CoinServiceAPI
|
||||||
))["vSize"] as int;
|
))["vSize"] as int;
|
||||||
|
|
||||||
// Assume 1 output, only for recipient and no change
|
// Assume 1 output, only for recipient and no change
|
||||||
final feeForOneOutput = estimateTxFee(
|
final feeForOneOutput = satsPerVByte != null
|
||||||
vSize: vSizeForOneOutput,
|
? (satsPerVByte * vSizeForOneOutput)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForOneOutput,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
// Assume 2 outputs, one for recipient and one for change
|
// Assume 2 outputs, one for recipient and one for change
|
||||||
final feeForTwoOutputs = estimateTxFee(
|
final feeForTwoOutputs = satsPerVByte != null
|
||||||
vSize: vSizeForTwoOutPuts,
|
? (satsPerVByte * vSizeForTwoOutPuts)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForTwoOutPuts,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
|
|
||||||
Logging.instance
|
Logging.instance
|
||||||
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
||||||
|
@ -2635,6 +2697,7 @@ class NamecoinWallet extends CoinServiceAPI
|
||||||
return coinSelection(
|
return coinSelection(
|
||||||
satoshiAmountToSend: satoshiAmountToSend,
|
satoshiAmountToSend: satoshiAmountToSend,
|
||||||
selectedTxFeeRate: selectedTxFeeRate,
|
selectedTxFeeRate: selectedTxFeeRate,
|
||||||
|
satsPerVByte: satsPerVByte,
|
||||||
recipientAddress: recipientAddress,
|
recipientAddress: recipientAddress,
|
||||||
isSendAll: isSendAll,
|
isSendAll: isSendAll,
|
||||||
additionalOutputs: additionalOutputs + 1,
|
additionalOutputs: additionalOutputs + 1,
|
||||||
|
|
|
@ -219,8 +219,8 @@ class ParticlWallet extends CoinServiceAPI
|
||||||
@override
|
@override
|
||||||
Future<int> get maxFee async {
|
Future<int> get maxFee async {
|
||||||
final fee = (await fees).fast as String;
|
final fee = (await fees).fast as String;
|
||||||
final satsFee =
|
final satsFee = Decimal.parse(fee) *
|
||||||
Decimal.parse(fee) * Decimal.fromInt(Constants.satsPerCoin(coin).toInt());
|
Decimal.fromInt(Constants.satsPerCoin(coin).toInt());
|
||||||
return satsFee.floor().toBigInt().toInt();
|
return satsFee.floor().toBigInt().toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -975,9 +975,60 @@ class ParticlWallet extends CoinServiceAPI
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final feeRateType = args?["feeRate"];
|
final feeRateType = args?["feeRate"];
|
||||||
|
final customSatsPerVByte = args?["satsPerVByte"] as int?;
|
||||||
final feeRateAmount = args?["feeRateAmount"];
|
final feeRateAmount = args?["feeRateAmount"];
|
||||||
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
||||||
if (feeRateType is FeeRateType || feeRateAmount is int) {
|
|
||||||
|
if (customSatsPerVByte != null) {
|
||||||
|
// check for send all
|
||||||
|
bool isSendAll = false;
|
||||||
|
if (amount == balance.spendable) {
|
||||||
|
isSendAll = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final bool coinControl = utxos != null;
|
||||||
|
|
||||||
|
final result = await coinSelection(
|
||||||
|
satoshiAmountToSend: amount.raw.toInt(),
|
||||||
|
selectedTxFeeRate: -1,
|
||||||
|
satsPerVByte: customSatsPerVByte,
|
||||||
|
recipientAddress: address,
|
||||||
|
isSendAll: isSendAll,
|
||||||
|
utxos: utxos?.toList(),
|
||||||
|
coinControl: coinControl,
|
||||||
|
);
|
||||||
|
|
||||||
|
Logging.instance
|
||||||
|
.log("PREPARE SEND RESULT: $result", level: LogLevel.Info);
|
||||||
|
if (result is int) {
|
||||||
|
switch (result) {
|
||||||
|
case 1:
|
||||||
|
throw Exception("Insufficient balance!");
|
||||||
|
case 2:
|
||||||
|
throw Exception("Insufficient funds to pay for transaction fee!");
|
||||||
|
default:
|
||||||
|
throw Exception("Transaction failed with error code $result");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final hex = result["hex"];
|
||||||
|
if (hex is String) {
|
||||||
|
final fee = result["fee"] as int;
|
||||||
|
final vSize = result["vSize"] as int;
|
||||||
|
|
||||||
|
Logging.instance.log("txHex: $hex", level: LogLevel.Info);
|
||||||
|
Logging.instance.log("fee: $fee", level: LogLevel.Info);
|
||||||
|
Logging.instance.log("vsize: $vSize", level: LogLevel.Info);
|
||||||
|
// fee should never be less than vSize sanity check
|
||||||
|
if (fee < vSize) {
|
||||||
|
throw Exception(
|
||||||
|
"Error in fee calculation: Transaction fee cannot be less than vSize");
|
||||||
|
}
|
||||||
|
return result as Map<String, dynamic>;
|
||||||
|
} else {
|
||||||
|
throw Exception("sent hex is not a String!!!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||||
late final int rate;
|
late final int rate;
|
||||||
if (feeRateType is FeeRateType) {
|
if (feeRateType is FeeRateType) {
|
||||||
int fee = 0;
|
int fee = 0;
|
||||||
|
@ -992,6 +1043,8 @@ class ParticlWallet extends CoinServiceAPI
|
||||||
case FeeRateType.slow:
|
case FeeRateType.slow:
|
||||||
fee = feeObject.slow;
|
fee = feeObject.slow;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
throw ArgumentError("Invalid use of custom fee");
|
||||||
}
|
}
|
||||||
rate = fee;
|
rate = fee;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2441,6 +2494,7 @@ class ParticlWallet extends CoinServiceAPI
|
||||||
required String recipientAddress,
|
required String recipientAddress,
|
||||||
required bool coinControl,
|
required bool coinControl,
|
||||||
required bool isSendAll,
|
required bool isSendAll,
|
||||||
|
int? satsPerVByte,
|
||||||
int additionalOutputs = 0,
|
int additionalOutputs = 0,
|
||||||
List<isar_models.UTXO>? utxos,
|
List<isar_models.UTXO>? utxos,
|
||||||
}) async {
|
}) async {
|
||||||
|
@ -2546,18 +2600,22 @@ class ParticlWallet extends CoinServiceAPI
|
||||||
recipients: [recipientAddress],
|
recipients: [recipientAddress],
|
||||||
satoshiAmounts: [satoshisBeingUsed - 1],
|
satoshiAmounts: [satoshisBeingUsed - 1],
|
||||||
))["vSize"] as int;
|
))["vSize"] as int;
|
||||||
int feeForOneOutput = estimateTxFee(
|
int feeForOneOutput = satsPerVByte != null
|
||||||
vSize: vSizeForOneOutput,
|
? (satsPerVByte * vSizeForOneOutput)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForOneOutput,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
|
|
||||||
final int roughEstimate = roughFeeEstimate(
|
if (satsPerVByte == null) {
|
||||||
spendableOutputs.length,
|
final int roughEstimate = roughFeeEstimate(
|
||||||
1,
|
spendableOutputs.length,
|
||||||
selectedTxFeeRate,
|
1,
|
||||||
).raw.toInt();
|
selectedTxFeeRate,
|
||||||
if (feeForOneOutput < roughEstimate) {
|
).raw.toInt();
|
||||||
feeForOneOutput = roughEstimate;
|
if (feeForOneOutput < roughEstimate) {
|
||||||
|
feeForOneOutput = roughEstimate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final int amount = satoshiAmountToSend - feeForOneOutput;
|
final int amount = satoshiAmountToSend - feeForOneOutput;
|
||||||
|
@ -2598,15 +2656,19 @@ class ParticlWallet extends CoinServiceAPI
|
||||||
))["vSize"] as int;
|
))["vSize"] as int;
|
||||||
|
|
||||||
// Assume 1 output, only for recipient and no change
|
// Assume 1 output, only for recipient and no change
|
||||||
final feeForOneOutput = estimateTxFee(
|
final feeForOneOutput = satsPerVByte != null
|
||||||
vSize: vSizeForOneOutput,
|
? (satsPerVByte * vSizeForOneOutput)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForOneOutput,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
// Assume 2 outputs, one for recipient and one for change
|
// Assume 2 outputs, one for recipient and one for change
|
||||||
final feeForTwoOutputs = estimateTxFee(
|
final feeForTwoOutputs = satsPerVByte != null
|
||||||
vSize: vSizeForTwoOutPuts,
|
? (satsPerVByte * vSizeForTwoOutPuts)
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
: estimateTxFee(
|
||||||
);
|
vSize: vSizeForTwoOutPuts,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
|
|
||||||
Logging.instance
|
Logging.instance
|
||||||
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
||||||
|
@ -2803,6 +2865,7 @@ class ParticlWallet extends CoinServiceAPI
|
||||||
satoshiAmountToSend: satoshiAmountToSend,
|
satoshiAmountToSend: satoshiAmountToSend,
|
||||||
selectedTxFeeRate: selectedTxFeeRate,
|
selectedTxFeeRate: selectedTxFeeRate,
|
||||||
recipientAddress: recipientAddress,
|
recipientAddress: recipientAddress,
|
||||||
|
satsPerVByte: satsPerVByte,
|
||||||
isSendAll: isSendAll,
|
isSendAll: isSendAll,
|
||||||
additionalOutputs: additionalOutputs + 1,
|
additionalOutputs: additionalOutputs + 1,
|
||||||
utxos: utxos,
|
utxos: utxos,
|
||||||
|
|
|
@ -486,6 +486,8 @@ class WowneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
||||||
case FeeRateType.slow:
|
case FeeRateType.slow:
|
||||||
feePriority = MoneroTransactionPriority.slow;
|
feePriority = MoneroTransactionPriority.slow;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
throw ArgumentError("Invalid use of custom fee");
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<PendingTransaction>? awaitPendingTransaction;
|
Future<PendingTransaction>? awaitPendingTransaction;
|
||||||
|
|
|
@ -47,12 +47,15 @@ class EthereumResponse<T> {
|
||||||
abstract class EthereumAPI {
|
abstract class EthereumAPI {
|
||||||
static String get stackBaseServer => DefaultNodes.ethereum.host;
|
static String get stackBaseServer => DefaultNodes.ethereum.host;
|
||||||
|
|
||||||
static Future<EthereumResponse<List<EthTxDTO>>> getEthTransactions(
|
static Future<EthereumResponse<List<EthTxDTO>>> getEthTransactions({
|
||||||
String address) async {
|
required String address,
|
||||||
|
int firstBlock = 0,
|
||||||
|
bool includeTokens = false,
|
||||||
|
}) async {
|
||||||
try {
|
try {
|
||||||
final response = await get(
|
final response = await get(
|
||||||
Uri.parse(
|
Uri.parse(
|
||||||
"$stackBaseServer/export?addrs=$address",
|
"$stackBaseServer/export?addrs=$address&firstBlock=$firstBlock",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -65,7 +68,7 @@ abstract class EthereumAPI {
|
||||||
for (final map in list!) {
|
for (final map in list!) {
|
||||||
final txn = EthTxDTO.fromMap(Map<String, dynamic>.from(map as Map));
|
final txn = EthTxDTO.fromMap(Map<String, dynamic>.from(map as Map));
|
||||||
|
|
||||||
if (txn.hasToken == 0) {
|
if (txn.hasToken == 0 || includeTokens) {
|
||||||
txns.add(txn);
|
txns.add(txn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,9 +77,11 @@ abstract class EthereumAPI {
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
throw EthApiException(
|
// nice that the api returns an empty body instead of being
|
||||||
"getEthTransactions($address) response is empty but status code is "
|
// consistent and returning a json object with no transactions
|
||||||
"${response.statusCode}",
|
return EthereumResponse(
|
||||||
|
[],
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -194,9 +199,11 @@ abstract class EthereumAPI {
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
throw EthApiException(
|
// nice that the api returns an empty body instead of being
|
||||||
"getEthTransactionNonces($txns) response is empty but status code is "
|
// consistent and returning a json object with no transactions
|
||||||
"${response.statusCode}",
|
return EthereumResponse(
|
||||||
|
[],
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -250,13 +257,13 @@ abstract class EthereumAPI {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
throw EthApiException(
|
throw EthApiException(
|
||||||
"getEthTransaction($txids) response is empty but status code is "
|
"getEthTokenTransactionsByTxids($txids) response is empty but status code is "
|
||||||
"${response.statusCode}",
|
"${response.statusCode}",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw EthApiException(
|
throw EthApiException(
|
||||||
"getEthTransaction($txids) failed with status code: "
|
"getEthTokenTransactionsByTxids($txids) failed with status code: "
|
||||||
"${response.statusCode}",
|
"${response.statusCode}",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -267,7 +274,7 @@ abstract class EthereumAPI {
|
||||||
);
|
);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"getEthTransaction($txids): $e\n$s",
|
"getEthTokenTransactionsByTxids($txids): $e\n$s",
|
||||||
level: LogLevel.Error,
|
level: LogLevel.Error,
|
||||||
);
|
);
|
||||||
return EthereumResponse(
|
return EthereumResponse(
|
||||||
|
@ -305,9 +312,11 @@ abstract class EthereumAPI {
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
throw EthApiException(
|
// nice that the api returns an empty body instead of being
|
||||||
"getTokenTransactions($address, $tokenContractAddress) response is empty but status code is "
|
// consistent and returning a json object with no transactions
|
||||||
"${response.statusCode}",
|
return EthereumResponse(
|
||||||
|
[],
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -449,6 +449,15 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.value == null) {
|
if (response.value == null) {
|
||||||
|
if (response.exception != null &&
|
||||||
|
response.exception!.message
|
||||||
|
.contains("response is empty but status code is 200")) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"No ${tokenContract.name} transfers found for $addressString",
|
||||||
|
level: LogLevel.Info,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
throw response.exception ??
|
throw response.exception ??
|
||||||
Exception("Failed to fetch token transaction data");
|
Exception("Failed to fetch token transaction data");
|
||||||
}
|
}
|
||||||
|
@ -507,9 +516,13 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
|
||||||
} else if (toAddress == addressString) {
|
} else if (toAddress == addressString) {
|
||||||
isIncoming = true;
|
isIncoming = true;
|
||||||
} else {
|
} else {
|
||||||
throw Exception("Unknown token transaction found for "
|
// ignore for now I guess since anything here is not reflected in
|
||||||
"${ethWallet.walletName} ${ethWallet.walletId}: "
|
// balance anyways
|
||||||
"${tuple.item1.toString()}");
|
continue;
|
||||||
|
|
||||||
|
// throw Exception("Unknown token transaction found for "
|
||||||
|
// "${ethWallet.walletName} ${ethWallet.walletId}: "
|
||||||
|
// "${tuple.item1.toString()}");
|
||||||
}
|
}
|
||||||
|
|
||||||
final txn = Transaction(
|
final txn = Transaction(
|
||||||
|
|
|
@ -135,6 +135,9 @@ class ExchangeDataLoadingService {
|
||||||
Future<void> loadAll() async {
|
Future<void> loadAll() async {
|
||||||
if (!_locked) {
|
if (!_locked) {
|
||||||
_locked = true;
|
_locked = true;
|
||||||
|
if (_isar == null) {
|
||||||
|
await initDB();
|
||||||
|
}
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"ExchangeDataLoadingService.loadAll starting...",
|
"ExchangeDataLoadingService.loadAll starting...",
|
||||||
level: LogLevel.Info,
|
level: LogLevel.Info,
|
||||||
|
|
|
@ -21,7 +21,7 @@ class LocaleService extends ChangeNotifier {
|
||||||
Future<void> loadLocale({bool notify = true}) async {
|
Future<void> loadLocale({bool notify = true}) async {
|
||||||
_locale = Platform.isWindows
|
_locale = Platform.isWindows
|
||||||
? "en_US"
|
? "en_US"
|
||||||
: await Devicelocale.currentLocale ?? "en_US";
|
: (await Devicelocale.currentAsLocale)?.toString() ?? "en_US";
|
||||||
if (notify) {
|
if (notify) {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ class NotificationApi {
|
||||||
priority: Priority.high,
|
priority: Priority.high,
|
||||||
ticker: 'ticker'),
|
ticker: 'ticker'),
|
||||||
iOS: IOSNotificationDetails(),
|
iOS: IOSNotificationDetails(),
|
||||||
|
macOS: MacOSNotificationDetails(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,8 +35,13 @@ class NotificationApi {
|
||||||
const iOS = IOSInitializationSettings();
|
const iOS = IOSInitializationSettings();
|
||||||
const linux = LinuxInitializationSettings(
|
const linux = LinuxInitializationSettings(
|
||||||
defaultActionName: "temporary_stack_wallet");
|
defaultActionName: "temporary_stack_wallet");
|
||||||
const settings =
|
const macOS = MacOSInitializationSettings();
|
||||||
InitializationSettings(android: android, iOS: iOS, linux: linux);
|
const settings = InitializationSettings(
|
||||||
|
android: android,
|
||||||
|
iOS: iOS,
|
||||||
|
linux: linux,
|
||||||
|
macOS: macOS,
|
||||||
|
);
|
||||||
await _notifications.initialize(
|
await _notifications.initialize(
|
||||||
settings,
|
settings,
|
||||||
onSelectNotification: (payload) async {
|
onSelectNotification: (payload) async {
|
||||||
|
|
|
@ -80,7 +80,7 @@ class PriceAPI {
|
||||||
{required String baseCurrency}) async {
|
{required String baseCurrency}) async {
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
if (_lastUsedBaseCurrency != baseCurrency ||
|
if (_lastUsedBaseCurrency != baseCurrency ||
|
||||||
now.difference(_lastCalled).inSeconds > 0) {
|
now.difference(_lastCalled) > refreshIntervalDuration) {
|
||||||
_lastCalled = now;
|
_lastCalled = now;
|
||||||
_lastUsedBaseCurrency = baseCurrency;
|
_lastUsedBaseCurrency = baseCurrency;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -84,17 +84,12 @@ class Wallets extends ChangeNotifier {
|
||||||
}
|
}
|
||||||
final List<Tuple2<Coin, List<ChangeNotifierProvider<Manager>>>> result = [];
|
final List<Tuple2<Coin, List<ChangeNotifierProvider<Manager>>>> result = [];
|
||||||
|
|
||||||
for (final coin in map.keys) {
|
for (final coin in Coin.values) {
|
||||||
result.add(Tuple2(coin, map[coin]!));
|
if (map[coin] != null) {
|
||||||
|
result.add(Tuple2(coin, map[coin]!));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// result.sort((a, b) => a.item1.prettyName.compareTo(b.item1.prettyName));
|
|
||||||
result.sort((a, b) => a.item1.prettyName == "Bitcoin"
|
|
||||||
? -1
|
|
||||||
: b.item1.prettyName == "Monero"
|
|
||||||
? 1
|
|
||||||
: a.item1.prettyName.compareTo(b.item1.prettyName));
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,3 +22,14 @@ final coinCardProvider = Provider.family<String?, Coin>((ref, coin) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final coinCardFavoritesProvider = Provider.family<String?, Coin>((ref, coin) {
|
||||||
|
final assets = ref.watch(themeAssetsProvider);
|
||||||
|
|
||||||
|
if (assets is ThemeAssetsV3) {
|
||||||
|
return assets.coinCardFavoritesImages?[coin.mainNetVersion] ??
|
||||||
|
assets.coinCardImages?[coin.mainNetVersion];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -30,8 +30,6 @@ final coinIconProvider = Provider.family<String, Coin>((ref, coin) {
|
||||||
case Coin.dogecoin:
|
case Coin.dogecoin:
|
||||||
case Coin.dogecoinTestNet:
|
case Coin.dogecoinTestNet:
|
||||||
return assets.dogecoin;
|
return assets.dogecoin;
|
||||||
case Coin.eCash:
|
|
||||||
return assets.bitcoin;
|
|
||||||
case Coin.epicCash:
|
case Coin.epicCash:
|
||||||
return assets.epicCash;
|
return assets.epicCash;
|
||||||
case Coin.firo:
|
case Coin.firo:
|
||||||
|
@ -48,7 +46,7 @@ final coinIconProvider = Provider.family<String, Coin>((ref, coin) {
|
||||||
case Coin.ethereum:
|
case Coin.ethereum:
|
||||||
return assets.ethereum;
|
return assets.ethereum;
|
||||||
default:
|
default:
|
||||||
return assets.bitcoin;
|
return assets.stackIcon;
|
||||||
}
|
}
|
||||||
} else if (assets is ThemeAssetsV2) {
|
} else if (assets is ThemeAssetsV2) {
|
||||||
return (assets).coinIcons[coin.mainNetVersion]!;
|
return (assets).coinIcons[coin.mainNetVersion]!;
|
||||||
|
|
|
@ -40,7 +40,7 @@ class ThemeService {
|
||||||
void init(MainDB db) => _db ??= db;
|
void init(MainDB db) => _db ??= db;
|
||||||
|
|
||||||
Future<void> install({required Uint8List themeArchiveData}) async {
|
Future<void> install({required Uint8List themeArchiveData}) async {
|
||||||
final themesDir = await StackFileSystem.applicationThemesDirectory();
|
final themesDir = StackFileSystem.themesDir!;
|
||||||
|
|
||||||
final archive = ZipDecoder().decodeBytes(themeArchiveData);
|
final archive = ZipDecoder().decodeBytes(themeArchiveData);
|
||||||
|
|
||||||
|
@ -55,7 +55,6 @@ class ThemeService {
|
||||||
|
|
||||||
final theme = StackTheme.fromJson(
|
final theme = StackTheme.fromJson(
|
||||||
json: Map<String, dynamic>.from(json),
|
json: Map<String, dynamic>.from(json),
|
||||||
applicationThemesDirectoryPath: themesDir.path,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -88,7 +87,7 @@ class ThemeService {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> remove({required String themeId}) async {
|
Future<void> remove({required String themeId}) async {
|
||||||
final themesDir = await StackFileSystem.applicationThemesDirectory();
|
final themesDir = StackFileSystem.themesDir!;
|
||||||
final isarId = await db.isar.stackThemes
|
final isarId = await db.isar.stackThemes
|
||||||
.where()
|
.where()
|
||||||
.themeIdEqualTo(themeId)
|
.themeIdEqualTo(themeId)
|
||||||
|
@ -98,7 +97,10 @@ class ThemeService {
|
||||||
await db.isar.writeTxn(() async {
|
await db.isar.writeTxn(() async {
|
||||||
await db.isar.stackThemes.delete(isarId);
|
await db.isar.stackThemes.delete(isarId);
|
||||||
});
|
});
|
||||||
await Directory("${themesDir.path}/$themeId").delete(recursive: true);
|
final dir = Directory("${themesDir.path}/$themeId");
|
||||||
|
if (dir.existsSync()) {
|
||||||
|
await dir.delete(recursive: true);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"Failed to delete theme $themeId",
|
"Failed to delete theme $themeId",
|
||||||
|
@ -154,20 +156,22 @@ class ThemeService {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// check installed version
|
// check installed version
|
||||||
final theme = ThemeService.instance.getTheme(themeId: "dark");
|
// final theme = ThemeService.instance.getTheme(themeId: "dark");
|
||||||
if ((theme?.version ?? 1) < _currentDefaultThemeVersion) {
|
// Force update theme to add missing icons for now
|
||||||
Logging.instance.log(
|
// TODO: uncomment if statement in future when themes are version 4 or above
|
||||||
"Updating default dark theme...",
|
// if ((theme?.version ?? 1) < _currentDefaultThemeVersion) {
|
||||||
level: LogLevel.Info,
|
Logging.instance.log(
|
||||||
);
|
"Updating default dark theme...",
|
||||||
final darkZip = await rootBundle.load("assets/default_themes/dark.zip");
|
level: LogLevel.Info,
|
||||||
await ThemeService.instance
|
);
|
||||||
.install(themeArchiveData: darkZip.buffer.asUint8List());
|
final darkZip = await rootBundle.load("assets/default_themes/dark.zip");
|
||||||
Logging.instance.log(
|
await ThemeService.instance
|
||||||
"Updating default dark theme... finished",
|
.install(themeArchiveData: darkZip.buffer.asUint8List());
|
||||||
level: LogLevel.Info,
|
Logging.instance.log(
|
||||||
);
|
"Updating default dark theme... finished",
|
||||||
}
|
level: LogLevel.Info,
|
||||||
|
);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +186,7 @@ class ThemeService {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final themesDir = await StackFileSystem.applicationThemesDirectory();
|
final themesDir = StackFileSystem.themesDir!;
|
||||||
final jsonFileExists =
|
final jsonFileExists =
|
||||||
await File("${themesDir.path}/$themeId/theme.json").exists();
|
await File("${themesDir.path}/$themeId/theme.json").exists();
|
||||||
final assetsDirExists =
|
final assetsDirExists =
|
||||||
|
|
|
@ -10,11 +10,11 @@
|
||||||
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:bitbox/bitbox.dart' as bitbox;
|
||||||
import 'package:bitcoindart/bitcoindart.dart';
|
import 'package:bitcoindart/bitcoindart.dart';
|
||||||
import 'package:crypto/crypto.dart';
|
import 'package:crypto/crypto.dart';
|
||||||
import 'package:flutter_libepiccash/epic_cash.dart';
|
import 'package:flutter_libepiccash/epic_cash.dart';
|
||||||
import 'package:nanodart/nanodart.dart';
|
import 'package:nanodart/nanodart.dart';
|
||||||
import 'package:stackwallet/services/coins/bitcoincash/bitcoincash_wallet.dart';
|
|
||||||
import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
|
import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
|
||||||
import 'package:stackwallet/services/coins/ecash/ecash_wallet.dart';
|
import 'package:stackwallet/services/coins/ecash/ecash_wallet.dart';
|
||||||
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
|
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
|
||||||
|
@ -64,7 +64,27 @@ class AddressUtils {
|
||||||
case Coin.litecoin:
|
case Coin.litecoin:
|
||||||
return Address.validateAddress(address, litecoin);
|
return Address.validateAddress(address, litecoin);
|
||||||
case Coin.bitcoincash:
|
case Coin.bitcoincash:
|
||||||
return Address.validateAddress(address, bitcoincash);
|
try {
|
||||||
|
// 0 for bitcoincash: address scheme, 1 for legacy address
|
||||||
|
final format = bitbox.Address.detectFormat(address);
|
||||||
|
|
||||||
|
if (coin == Coin.bitcoincashTestnet) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format == bitbox.Address.formatCashAddr) {
|
||||||
|
String addr = address;
|
||||||
|
if (addr.contains(":")) {
|
||||||
|
addr = addr.split(":").last;
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr.startsWith("q");
|
||||||
|
} else {
|
||||||
|
return address.startsWith("1");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
case Coin.dogecoin:
|
case Coin.dogecoin:
|
||||||
return Address.validateAddress(address, dogecoin);
|
return Address.validateAddress(address, dogecoin);
|
||||||
case Coin.epicCash:
|
case Coin.epicCash:
|
||||||
|
@ -94,7 +114,27 @@ class AddressUtils {
|
||||||
case Coin.litecoinTestNet:
|
case Coin.litecoinTestNet:
|
||||||
return Address.validateAddress(address, litecointestnet);
|
return Address.validateAddress(address, litecointestnet);
|
||||||
case Coin.bitcoincashTestnet:
|
case Coin.bitcoincashTestnet:
|
||||||
return Address.validateAddress(address, bitcoincashtestnet);
|
try {
|
||||||
|
// 0 for bitcoincash: address scheme, 1 for legacy address
|
||||||
|
final format = bitbox.Address.detectFormat(address);
|
||||||
|
|
||||||
|
if (coin == Coin.bitcoincashTestnet) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format == bitbox.Address.formatCashAddr) {
|
||||||
|
String addr = address;
|
||||||
|
if (addr.contains(":")) {
|
||||||
|
addr = addr.split(":").last;
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr.startsWith("q");
|
||||||
|
} else {
|
||||||
|
return address.startsWith("1");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
case Coin.firoTestNet:
|
case Coin.firoTestNet:
|
||||||
return Address.validateAddress(address, firoTestNetwork);
|
return Address.validateAddress(address, firoTestNetwork);
|
||||||
case Coin.dogecoinTestNet:
|
case Coin.dogecoinTestNet:
|
||||||
|
@ -126,7 +166,17 @@ class AddressUtils {
|
||||||
String address,
|
String address,
|
||||||
Map<String, String> params,
|
Map<String, String> params,
|
||||||
) {
|
) {
|
||||||
String uriString = "${coin.uriScheme}:$address";
|
// TODO: other sanitation as well ?
|
||||||
|
String sanitizedAddress = address;
|
||||||
|
if (coin == Coin.bitcoincash ||
|
||||||
|
coin == Coin.bitcoincashTestnet ||
|
||||||
|
coin == Coin.eCash) {
|
||||||
|
final prefix = "${coin.uriScheme}:";
|
||||||
|
if (address.startsWith(prefix)) {
|
||||||
|
sanitizedAddress = address.replaceFirst(prefix, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String uriString = "${coin.uriScheme}:$sanitizedAddress";
|
||||||
if (params.isNotEmpty) {
|
if (params.isNotEmpty) {
|
||||||
uriString += Uri(queryParameters: params).toString();
|
uriString += Uri(queryParameters: params).toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,7 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:decimal/decimal.dart';
|
import 'package:decimal/decimal.dart';
|
||||||
import 'package:intl/number_symbols.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
import 'package:intl/number_symbols_data.dart';
|
|
||||||
|
|
||||||
class Amount {
|
class Amount {
|
||||||
Amount({
|
Amount({
|
||||||
|
@ -32,6 +31,38 @@ class Amount {
|
||||||
: assert(fractionDigits >= 0),
|
: assert(fractionDigits >= 0),
|
||||||
_value = amount.shift(fractionDigits).toBigInt();
|
_value = amount.shift(fractionDigits).toBigInt();
|
||||||
|
|
||||||
|
static Amount? tryParseFiatString(
|
||||||
|
String value, {
|
||||||
|
required String locale,
|
||||||
|
}) {
|
||||||
|
final parts = value.split(" ");
|
||||||
|
|
||||||
|
if (parts.first.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String str = parts.first;
|
||||||
|
if (str.startsWith(RegExp(r'[+-]'))) {
|
||||||
|
str = str.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get number symbols for decimal place and group separator
|
||||||
|
final numberSymbols = Util.getSymbolsFor(locale: locale);
|
||||||
|
|
||||||
|
final groupSeparator = numberSymbols?.GROUP_SEP ?? ",";
|
||||||
|
final decimalSeparator = numberSymbols?.DECIMAL_SEP ?? ".";
|
||||||
|
|
||||||
|
str = str.replaceAll(groupSeparator, "");
|
||||||
|
|
||||||
|
final decimalString = str.replaceFirst(decimalSeparator, ".");
|
||||||
|
|
||||||
|
return Decimal.tryParse(decimalString)?.toAmount(fractionDigits: 2);
|
||||||
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// ======= Instance properties ===============================================
|
// ======= Instance properties ===============================================
|
||||||
|
|
||||||
|
@ -67,15 +98,22 @@ class Amount {
|
||||||
}) {
|
}) {
|
||||||
final wholeNumber = decimal.truncate();
|
final wholeNumber = decimal.truncate();
|
||||||
|
|
||||||
final String separator =
|
// get number symbols for decimal place and group separator
|
||||||
(numberFormatSymbols[locale] as NumberSymbols?)?.DECIMAL_SEP ??
|
final numberSymbols = Util.getSymbolsFor(locale: locale);
|
||||||
(numberFormatSymbols[locale.substring(0, 2)] as NumberSymbols?)
|
|
||||||
?.DECIMAL_SEP ??
|
final String separator = numberSymbols?.DECIMAL_SEP ?? ".";
|
||||||
".";
|
|
||||||
|
|
||||||
final fraction = decimal - wholeNumber;
|
final fraction = decimal - wholeNumber;
|
||||||
|
|
||||||
return "${wholeNumber.toStringAsFixed(0)}$separator${fraction.toStringAsFixed(2).substring(2)}";
|
String wholeNumberString = wholeNumber.toStringAsFixed(0);
|
||||||
|
// insert group separator
|
||||||
|
final regex = RegExp(r'\B(?=(\d{3})+(?!\d))');
|
||||||
|
wholeNumberString = wholeNumberString.replaceAllMapped(
|
||||||
|
regex,
|
||||||
|
(m) => "${m.group(0)}${numberSymbols?.GROUP_SEP ?? ","}",
|
||||||
|
);
|
||||||
|
|
||||||
|
return "$wholeNumberString$separator${fraction.toStringAsFixed(2).substring(2)}";
|
||||||
}
|
}
|
||||||
// String localizedStringAsFixed({
|
// String localizedStringAsFixed({
|
||||||
// required String locale,
|
// required String locale,
|
||||||
|
|
|
@ -22,11 +22,13 @@ final pMaxDecimals = Provider.family<int, Coin>(
|
||||||
);
|
);
|
||||||
|
|
||||||
final pAmountFormatter = Provider.family<AmountFormatter, Coin>((ref, coin) {
|
final pAmountFormatter = Provider.family<AmountFormatter, Coin>((ref, coin) {
|
||||||
|
final locale = ref.watch(
|
||||||
|
localeServiceChangeNotifierProvider.select((value) => value.locale),
|
||||||
|
);
|
||||||
|
|
||||||
return AmountFormatter(
|
return AmountFormatter(
|
||||||
unit: ref.watch(pAmountUnit(coin)),
|
unit: ref.watch(pAmountUnit(coin)),
|
||||||
locale: ref.watch(
|
locale: locale,
|
||||||
localeServiceChangeNotifierProvider.select((value) => value.locale),
|
|
||||||
),
|
|
||||||
coin: coin,
|
coin: coin,
|
||||||
maxDecimals: ref.watch(pMaxDecimals(coin)),
|
maxDecimals: ref.watch(pMaxDecimals(coin)),
|
||||||
);
|
);
|
||||||
|
@ -63,4 +65,16 @@ class AmountFormatter {
|
||||||
tokenContract: ethContract,
|
tokenContract: ethContract,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Amount? tryParse(
|
||||||
|
String string, {
|
||||||
|
EthContract? ethContract,
|
||||||
|
}) {
|
||||||
|
return unit.tryParse(
|
||||||
|
string,
|
||||||
|
locale: locale,
|
||||||
|
coin: coin,
|
||||||
|
tokenContract: ethContract,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
95
lib/utilities/amount/amount_input_formatter.dart
Normal file
95
lib/utilities/amount/amount_input_formatter.dart
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:stackwallet/utilities/amount/amount_unit.dart';
|
||||||
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
|
||||||
|
class AmountInputFormatter extends TextInputFormatter {
|
||||||
|
final int decimals;
|
||||||
|
final String locale;
|
||||||
|
final AmountUnit? unit;
|
||||||
|
|
||||||
|
AmountInputFormatter({
|
||||||
|
required this.decimals,
|
||||||
|
required this.locale,
|
||||||
|
this.unit,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
TextEditingValue formatEditUpdate(
|
||||||
|
TextEditingValue oldValue, TextEditingValue newValue) {
|
||||||
|
// get number symbols for decimal place and group separator
|
||||||
|
final numberSymbols = Util.getSymbolsFor(locale: locale);
|
||||||
|
|
||||||
|
final decimalSeparator = numberSymbols?.DECIMAL_SEP ?? ".";
|
||||||
|
final groupSeparator = numberSymbols?.GROUP_SEP ?? ",";
|
||||||
|
|
||||||
|
String newText = newValue.text.replaceAll(groupSeparator, "");
|
||||||
|
|
||||||
|
final selectionIndexFromTheRight =
|
||||||
|
newValue.text.length - newValue.selection.end;
|
||||||
|
|
||||||
|
String? fraction;
|
||||||
|
if (newText.contains(decimalSeparator)) {
|
||||||
|
final parts = newText.split(decimalSeparator);
|
||||||
|
|
||||||
|
if (parts.length > 2) {
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final fractionDigits =
|
||||||
|
unit == null ? decimals : max(decimals - unit!.shift, 0);
|
||||||
|
|
||||||
|
if (newText.startsWith(decimalSeparator)) {
|
||||||
|
if (newText.length - 1 > fractionDigits) {
|
||||||
|
newText = newText.substring(0, fractionDigits + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TextEditingValue(
|
||||||
|
text: newText,
|
||||||
|
selection: TextSelection.collapsed(
|
||||||
|
offset: newText.length - selectionIndexFromTheRight,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
newText = parts.first;
|
||||||
|
if (parts.length == 2) {
|
||||||
|
fraction = parts.last;
|
||||||
|
} else {
|
||||||
|
fraction = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fraction.length > fractionDigits) {
|
||||||
|
fraction = fraction.substring(0, fractionDigits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String newString;
|
||||||
|
final val = BigInt.tryParse(newText);
|
||||||
|
if (val == null || val < BigInt.one) {
|
||||||
|
newString = newText;
|
||||||
|
} else {
|
||||||
|
// insert group separator
|
||||||
|
final regex = RegExp(r'\B(?=(\d{3})+(?!\d))');
|
||||||
|
newString = newText.replaceAllMapped(
|
||||||
|
regex,
|
||||||
|
(m) => "${m.group(0)}${numberSymbols?.GROUP_SEP ?? ","}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fraction != null) {
|
||||||
|
newString += decimalSeparator;
|
||||||
|
if (fraction.isNotEmpty) {
|
||||||
|
newString += fraction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TextEditingValue(
|
||||||
|
text: newString,
|
||||||
|
selection: TextSelection.collapsed(
|
||||||
|
offset: newString.length - selectionIndexFromTheRight,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,11 +11,10 @@
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:decimal/decimal.dart';
|
import 'package:decimal/decimal.dart';
|
||||||
import 'package:intl/number_symbols.dart';
|
|
||||||
import 'package:intl/number_symbols_data.dart';
|
|
||||||
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
|
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
|
||||||
// preserve index order as index is used to store value in preferences
|
// preserve index order as index is used to store value in preferences
|
||||||
enum AmountUnit {
|
enum AmountUnit {
|
||||||
|
@ -164,6 +163,53 @@ extension AmountUnitExt on AmountUnit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Amount? tryParse(
|
||||||
|
String value, {
|
||||||
|
required String locale,
|
||||||
|
required Coin coin,
|
||||||
|
EthContract? tokenContract,
|
||||||
|
bool overrideWithDecimalPlacesFromString = false,
|
||||||
|
}) {
|
||||||
|
final precisionLost = value.startsWith("~");
|
||||||
|
|
||||||
|
final parts = (precisionLost ? value.substring(1) : value).split(" ");
|
||||||
|
|
||||||
|
if (parts.first.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String str = parts.first;
|
||||||
|
if (str.startsWith(RegExp(r'[+-]'))) {
|
||||||
|
str = str.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get number symbols for decimal place and group separator
|
||||||
|
final numberSymbols = Util.getSymbolsFor(locale: locale);
|
||||||
|
|
||||||
|
final groupSeparator = numberSymbols?.GROUP_SEP ?? ",";
|
||||||
|
final decimalSeparator = numberSymbols?.DECIMAL_SEP ?? ".";
|
||||||
|
|
||||||
|
str = str.replaceAll(groupSeparator, "");
|
||||||
|
|
||||||
|
final decimalString = str.replaceFirst(decimalSeparator, ".");
|
||||||
|
final Decimal? decimal = Decimal.tryParse(decimalString);
|
||||||
|
|
||||||
|
if (decimal == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final decimalPlaces = overrideWithDecimalPlacesFromString
|
||||||
|
? decimal.scale
|
||||||
|
: tokenContract?.decimals ?? coin.decimals;
|
||||||
|
final realShift = math.min(shift, decimalPlaces);
|
||||||
|
|
||||||
|
return decimal.shift(0 - realShift).toAmount(fractionDigits: decimalPlaces);
|
||||||
|
}
|
||||||
|
|
||||||
String displayAmount({
|
String displayAmount({
|
||||||
required Amount amount,
|
required Amount amount,
|
||||||
required String locale,
|
required String locale,
|
||||||
|
@ -191,6 +237,16 @@ extension AmountUnitExt on AmountUnit {
|
||||||
// start building the return value with just the whole value
|
// start building the return value with just the whole value
|
||||||
String returnValue = wholeNumber.toString();
|
String returnValue = wholeNumber.toString();
|
||||||
|
|
||||||
|
// get number symbols for decimal place and group separator
|
||||||
|
final numberSymbols = Util.getSymbolsFor(locale: locale);
|
||||||
|
|
||||||
|
// insert group separator
|
||||||
|
final regex = RegExp(r'\B(?=(\d{3})+(?!\d))');
|
||||||
|
returnValue = returnValue.replaceAllMapped(
|
||||||
|
regex,
|
||||||
|
(m) => "${m.group(0)}${numberSymbols?.GROUP_SEP ?? ","}",
|
||||||
|
);
|
||||||
|
|
||||||
// if true and withUnitName is true, we will show "~" prepended on amount
|
// if true and withUnitName is true, we will show "~" prepended on amount
|
||||||
bool didLosePrecision = false;
|
bool didLosePrecision = false;
|
||||||
|
|
||||||
|
@ -239,11 +295,7 @@ extension AmountUnitExt on AmountUnit {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get decimal separator based on locale
|
// get decimal separator based on locale
|
||||||
final String separator =
|
final String separator = numberSymbols?.DECIMAL_SEP ?? ".";
|
||||||
(numberFormatSymbols[locale] as NumberSymbols?)?.DECIMAL_SEP ??
|
|
||||||
(numberFormatSymbols[locale.substring(0, 2)] as NumberSymbols?)
|
|
||||||
?.DECIMAL_SEP ??
|
|
||||||
".";
|
|
||||||
|
|
||||||
// append separator and fractional amount
|
// append separator and fractional amount
|
||||||
returnValue += "$separator$remainder";
|
returnValue += "$separator$remainder";
|
||||||
|
|
|
@ -65,8 +65,8 @@ class _EXCHANGE {
|
||||||
class _BUY {
|
class _BUY {
|
||||||
const _BUY();
|
const _BUY();
|
||||||
|
|
||||||
String simplexLogo(BuildContext context) {
|
String simplexLogo(Brightness themeBrightness) {
|
||||||
switch (MediaQuery.of(context).platformBrightness) {
|
switch (themeBrightness) {
|
||||||
case Brightness.dark:
|
case Brightness.dark:
|
||||||
return "assets/svg/buy/Simplex-Nuvei-Logo-light.svg";
|
return "assets/svg/buy/Simplex-Nuvei-Logo-light.svg";
|
||||||
|
|
||||||
|
|
|
@ -178,10 +178,8 @@ class DbVersionMigrator with WalletDB {
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
// clear possible broken firo cache
|
// clear possible broken firo cache
|
||||||
await DB.instance.deleteBoxFromDisk(
|
await DB.instance.clearSharedTransactionCache(coin: Coin.firo);
|
||||||
boxName: DB.instance.boxNameSetCache(coin: Coin.firo));
|
|
||||||
await DB.instance.deleteBoxFromDisk(
|
|
||||||
boxName: DB.instance.boxNameUsedSerialsCache(coin: Coin.firo));
|
|
||||||
|
|
||||||
// update version
|
// update version
|
||||||
await DB.instance.put<dynamic>(
|
await DB.instance.put<dynamic>(
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of Stack Wallet.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2023 Cypher Stack
|
|
||||||
* All Rights Reserved.
|
|
||||||
* The code is distributed under GPLv3 license, see LICENSE file for details.
|
|
||||||
* Generated by Cypher Stack on 2023-05-26
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import 'package:stackwallet/db/hive/db.dart';
|
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
|
||||||
|
|
||||||
Future<bool> deleteEverything() async {
|
|
||||||
try {
|
|
||||||
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameAddressBook);
|
|
||||||
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameDebugInfo);
|
|
||||||
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameNodeModels);
|
|
||||||
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNamePrimaryNodes);
|
|
||||||
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameAllWalletsData);
|
|
||||||
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameNotifications);
|
|
||||||
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameWatchedTransactions);
|
|
||||||
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameWatchedTrades);
|
|
||||||
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameTrades);
|
|
||||||
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameTradesV2);
|
|
||||||
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameTradeNotes);
|
|
||||||
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameTradeLookup);
|
|
||||||
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameFavoriteWallets);
|
|
||||||
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNamePrefs);
|
|
||||||
await DB.instance
|
|
||||||
.deleteBoxFromDisk(boxName: DB.boxNameWalletsToDeleteOnStart);
|
|
||||||
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNamePriceCache);
|
|
||||||
await DB.instance.deleteBoxFromDisk(boxName: DB.boxNameDBInfo);
|
|
||||||
await DB.instance.deleteBoxFromDisk(boxName: "theme");
|
|
||||||
return true;
|
|
||||||
} catch (e, s) {
|
|
||||||
Logging.instance.log("$e $s", level: LogLevel.Error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -10,9 +10,9 @@
|
||||||
|
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:stack_wallet_backup/secure_storage.dart';
|
import 'package:stack_wallet_backup/secure_storage.dart';
|
||||||
import 'package:stackwallet/db/hive/db.dart';
|
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
|
|
||||||
|
const String kBoxNameDesktopData = "desktopData";
|
||||||
const String _kKeyBlobKey = "swbKeyBlobKeyStringID";
|
const String _kKeyBlobKey = "swbKeyBlobKeyStringID";
|
||||||
const String _kKeyBlobVersionKey = "swbKeyBlobVersionKeyStringID";
|
const String _kKeyBlobVersionKey = "swbKeyBlobVersionKeyStringID";
|
||||||
|
|
||||||
|
@ -62,14 +62,8 @@ class DPS {
|
||||||
kLatestBlobVersion,
|
kLatestBlobVersion,
|
||||||
);
|
);
|
||||||
|
|
||||||
final box = await Hive.openBox<String>(DB.boxNameDesktopData);
|
await _put(key: _kKeyBlobKey, value: await _handler!.getKeyBlob());
|
||||||
await DB.instance.put<String>(
|
|
||||||
boxName: DB.boxNameDesktopData,
|
|
||||||
key: _kKeyBlobKey,
|
|
||||||
value: await _handler!.getKeyBlob(),
|
|
||||||
);
|
|
||||||
await _updateStoredKeyBlobVersion(kLatestBlobVersion);
|
await _updateStoredKeyBlobVersion(kLatestBlobVersion);
|
||||||
await box.close();
|
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"${_getMessageFromException(e)}\n$s",
|
"${_getMessageFromException(e)}\n$s",
|
||||||
|
@ -85,19 +79,13 @@ class DPS {
|
||||||
"DPS: attempted to re initialize with existing passphrase");
|
"DPS: attempted to re initialize with existing passphrase");
|
||||||
}
|
}
|
||||||
|
|
||||||
final box = await Hive.openBox<String>(DB.boxNameDesktopData);
|
|
||||||
final keyBlob = DB.instance.get<String>(
|
|
||||||
boxName: DB.boxNameDesktopData,
|
|
||||||
key: _kKeyBlobKey,
|
|
||||||
);
|
|
||||||
await box.close();
|
|
||||||
|
|
||||||
if (keyBlob == null) {
|
|
||||||
throw Exception(
|
|
||||||
"DPS: failed to find keyBlob while attempting to initialize with existing passphrase");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
final keyBlob = await _get(key: _kKeyBlobKey);
|
||||||
|
|
||||||
|
if (keyBlob == null) {
|
||||||
|
throw Exception(
|
||||||
|
"DPS: failed to find keyBlob while attempting to initialize with existing passphrase");
|
||||||
|
}
|
||||||
final blobVersion = await _getStoredKeyBlobVersion();
|
final blobVersion = await _getStoredKeyBlobVersion();
|
||||||
_handler = await StorageCryptoHandler.fromExisting(
|
_handler = await StorageCryptoHandler.fromExisting(
|
||||||
passphrase,
|
passphrase,
|
||||||
|
@ -107,14 +95,8 @@ class DPS {
|
||||||
if (blobVersion < kLatestBlobVersion) {
|
if (blobVersion < kLatestBlobVersion) {
|
||||||
// update blob
|
// update blob
|
||||||
await _handler!.resetPassphrase(passphrase, kLatestBlobVersion);
|
await _handler!.resetPassphrase(passphrase, kLatestBlobVersion);
|
||||||
final box = await Hive.openBox<String>(DB.boxNameDesktopData);
|
await _put(key: _kKeyBlobKey, value: await _handler!.getKeyBlob());
|
||||||
await DB.instance.put<String>(
|
|
||||||
boxName: DB.boxNameDesktopData,
|
|
||||||
key: _kKeyBlobKey,
|
|
||||||
value: await _handler!.getKeyBlob(),
|
|
||||||
);
|
|
||||||
await _updateStoredKeyBlobVersion(kLatestBlobVersion);
|
await _updateStoredKeyBlobVersion(kLatestBlobVersion);
|
||||||
await box.close();
|
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
|
@ -126,19 +108,13 @@ class DPS {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> verifyPassphrase(String passphrase) async {
|
Future<bool> verifyPassphrase(String passphrase) async {
|
||||||
final box = await Hive.openBox<String>(DB.boxNameDesktopData);
|
|
||||||
final keyBlob = DB.instance.get<String>(
|
|
||||||
boxName: DB.boxNameDesktopData,
|
|
||||||
key: _kKeyBlobKey,
|
|
||||||
);
|
|
||||||
await box.close();
|
|
||||||
|
|
||||||
if (keyBlob == null) {
|
|
||||||
// no passphrase key blob found so any passphrase is technically bad
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
final keyBlob = await _get(key: _kKeyBlobKey);
|
||||||
|
|
||||||
|
if (keyBlob == null) {
|
||||||
|
// no passphrase key blob found so any passphrase is technically bad
|
||||||
|
return false;
|
||||||
|
}
|
||||||
final blobVersion = await _getStoredKeyBlobVersion();
|
final blobVersion = await _getStoredKeyBlobVersion();
|
||||||
await StorageCryptoHandler.fromExisting(passphrase, keyBlob, blobVersion);
|
await StorageCryptoHandler.fromExisting(passphrase, keyBlob, blobVersion);
|
||||||
// existing passphrase matches key blob
|
// existing passphrase matches key blob
|
||||||
|
@ -157,35 +133,25 @@ class DPS {
|
||||||
String passphraseOld,
|
String passphraseOld,
|
||||||
String passphraseNew,
|
String passphraseNew,
|
||||||
) async {
|
) async {
|
||||||
final box = await Hive.openBox<String>(DB.boxNameDesktopData);
|
|
||||||
final keyBlob = DB.instance.get<String>(
|
|
||||||
boxName: DB.boxNameDesktopData,
|
|
||||||
key: _kKeyBlobKey,
|
|
||||||
);
|
|
||||||
await box.close();
|
|
||||||
|
|
||||||
if (keyBlob == null) {
|
|
||||||
// no passphrase key blob found so any passphrase is technically bad
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(await verifyPassphrase(passphraseOld))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final blobVersion = await _getStoredKeyBlobVersion();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await _handler!.resetPassphrase(passphraseNew, blobVersion);
|
final keyBlob = await _get(key: _kKeyBlobKey);
|
||||||
|
|
||||||
final box = await Hive.openBox<String>(DB.boxNameDesktopData);
|
if (keyBlob == null) {
|
||||||
await DB.instance.put<String>(
|
// no passphrase key blob found so any passphrase is technically bad
|
||||||
boxName: DB.boxNameDesktopData,
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await verifyPassphrase(passphraseOld))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final blobVersion = await _getStoredKeyBlobVersion();
|
||||||
|
await _handler!.resetPassphrase(passphraseNew, blobVersion);
|
||||||
|
await _put(
|
||||||
key: _kKeyBlobKey,
|
key: _kKeyBlobKey,
|
||||||
value: await _handler!.getKeyBlob(),
|
value: await _handler!.getKeyBlob(),
|
||||||
);
|
);
|
||||||
await _updateStoredKeyBlobVersion(blobVersion);
|
await _updateStoredKeyBlobVersion(blobVersion);
|
||||||
await box.close();
|
|
||||||
|
|
||||||
// successfully updated passphrase
|
// successfully updated passphrase
|
||||||
return true;
|
return true;
|
||||||
|
@ -199,28 +165,54 @@ class DPS {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> hasPassword() async {
|
Future<bool> hasPassword() async {
|
||||||
final keyBlob = DB.instance.get<String>(
|
final keyBlob = await _get(key: _kKeyBlobKey);
|
||||||
boxName: DB.boxNameDesktopData,
|
|
||||||
key: _kKeyBlobKey,
|
|
||||||
);
|
|
||||||
return keyBlob != null;
|
return keyBlob != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> _getStoredKeyBlobVersion() async {
|
Future<int> _getStoredKeyBlobVersion() async {
|
||||||
final box = await Hive.openBox<String>(DB.boxNameDesktopData);
|
final keyBlobVersionString = await _get(key: _kKeyBlobVersionKey);
|
||||||
final keyBlobVersionString = DB.instance.get<String>(
|
|
||||||
boxName: DB.boxNameDesktopData,
|
|
||||||
key: _kKeyBlobVersionKey,
|
|
||||||
);
|
|
||||||
await box.close();
|
|
||||||
return int.tryParse(keyBlobVersionString ?? "1") ?? 1;
|
return int.tryParse(keyBlobVersionString ?? "1") ?? 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _updateStoredKeyBlobVersion(int version) async {
|
Future<void> _updateStoredKeyBlobVersion(int version) async {
|
||||||
await DB.instance.put<String>(
|
await _put(key: _kKeyBlobVersionKey, value: version.toString());
|
||||||
boxName: DB.boxNameDesktopData,
|
}
|
||||||
key: _kKeyBlobVersionKey,
|
|
||||||
value: version.toString(),
|
Future<void> _put({required String key, required String value}) async {
|
||||||
);
|
Box<String>? box;
|
||||||
|
try {
|
||||||
|
box = await Hive.openBox<String>(kBoxNameDesktopData);
|
||||||
|
await box.put(key, value);
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"DPS failed put($key): $e\n$s",
|
||||||
|
level: LogLevel.Fatal,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
await box?.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> _get({required String key}) async {
|
||||||
|
String? value;
|
||||||
|
Box<String>? box;
|
||||||
|
try {
|
||||||
|
box = await Hive.openBox<String>(kBoxNameDesktopData);
|
||||||
|
value = box.get(key);
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"DPS failed get($key): $e\n$s",
|
||||||
|
level: LogLevel.Fatal,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
await box?.close();
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dangerous. Used in one place and should not be called anywhere else.
|
||||||
|
@Deprecated("Don't use this if at all possible")
|
||||||
|
Future<void> deleteBox() async {
|
||||||
|
await Hive.deleteBoxFromDisk(kBoxNameDesktopData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
enum FeeRateType { fast, average, slow }
|
enum FeeRateType { fast, average, slow, custom }
|
||||||
|
|
||||||
extension FeeRateTypeExt on FeeRateType {
|
extension FeeRateTypeExt on FeeRateType {
|
||||||
String get prettyName {
|
String get prettyName {
|
||||||
|
@ -19,6 +19,8 @@ extension FeeRateTypeExt on FeeRateType {
|
||||||
return "Average";
|
return "Average";
|
||||||
case FeeRateType.slow:
|
case FeeRateType.slow:
|
||||||
return "Slow";
|
return "Slow";
|
||||||
|
case FeeRateType.custom:
|
||||||
|
return "Custom";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,35 +10,6 @@
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
// todo: delete this map (example)
|
|
||||||
final map = {
|
|
||||||
"name": "Dark",
|
|
||||||
"coinColors": {
|
|
||||||
"bitcoin": "0xFF267352",
|
|
||||||
},
|
|
||||||
"assets": {
|
|
||||||
"circleLock": "svg/somerandomnamecreatedbythemecreator.svg",
|
|
||||||
},
|
|
||||||
"colors": {
|
|
||||||
"background": "0xFF848383",
|
|
||||||
},
|
|
||||||
"gradientBackground": {
|
|
||||||
"gradientType": "linear",
|
|
||||||
"begin": {
|
|
||||||
"x": 0.0,
|
|
||||||
"y": 1.0,
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"x": -1.0,
|
|
||||||
"y": 1.0,
|
|
||||||
},
|
|
||||||
"colors": [
|
|
||||||
"0xFF638227",
|
|
||||||
"0xFF632827",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
extension BoxShadowExt on BoxShadow {
|
extension BoxShadowExt on BoxShadow {
|
||||||
static BoxShadow fromJson(Map<String, dynamic> json) => BoxShadow(
|
static BoxShadow fromJson(Map<String, dynamic> json) => BoxShadow(
|
||||||
color: Color(int.parse(json["color"] as String)),
|
color: Color(int.parse(json["color"] as String)),
|
||||||
|
|
|
@ -27,8 +27,8 @@ abstract class StackFileSystem {
|
||||||
} else if (Platform.isWindows) {
|
} else if (Platform.isWindows) {
|
||||||
appDirectory = await getApplicationSupportDirectory();
|
appDirectory = await getApplicationSupportDirectory();
|
||||||
} else if (Platform.isMacOS) {
|
} else if (Platform.isMacOS) {
|
||||||
// currently run in ipad mode??
|
appDirectory = await getLibraryDirectory();
|
||||||
throw Exception("Unsupported platform");
|
appDirectory = Directory("${appDirectory.path}/stackwallet");
|
||||||
} else if (Platform.isIOS) {
|
} else if (Platform.isIOS) {
|
||||||
// todo: check if we need different behaviour here
|
// todo: check if we need different behaviour here
|
||||||
if (Util.isDesktop) {
|
if (Util.isDesktop) {
|
||||||
|
@ -73,16 +73,15 @@ abstract class StackFileSystem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Directory> applicationThemesDirectory() async {
|
static Future<void> initThemesDir() async {
|
||||||
final root = await applicationRootDirectory();
|
final root = await applicationRootDirectory();
|
||||||
// if (Util.isDesktop) {
|
|
||||||
final dir = Directory("${root.path}/themes");
|
final dir = Directory("${root.path}/themes");
|
||||||
if (!dir.existsSync()) {
|
if (!dir.existsSync()) {
|
||||||
await dir.create();
|
await dir.create();
|
||||||
}
|
}
|
||||||
return dir;
|
themesDir = dir;
|
||||||
// } else {
|
|
||||||
// return root;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Directory? themesDir;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,21 @@ import 'dart:io';
|
||||||
|
|
||||||
import 'package:device_info_plus/device_info_plus.dart';
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/number_symbols.dart';
|
||||||
|
import 'package:intl/number_symbols_data.dart';
|
||||||
|
|
||||||
abstract class Util {
|
abstract class Util {
|
||||||
static Directory? libraryPath;
|
static Directory? libraryPath;
|
||||||
static double? screenWidth;
|
static double? screenWidth;
|
||||||
|
|
||||||
|
static NumberSymbols? getSymbolsFor({required String locale}) {
|
||||||
|
return numberFormatSymbols[locale] as NumberSymbols? ??
|
||||||
|
numberFormatSymbols[locale.replaceAll("-", "_")] as NumberSymbols? ??
|
||||||
|
numberFormatSymbols[locale.substring(3).toLowerCase()]
|
||||||
|
as NumberSymbols? ??
|
||||||
|
numberFormatSymbols[locale.substring(0, 2)] as NumberSymbols?;
|
||||||
|
}
|
||||||
|
|
||||||
static bool get isDesktop {
|
static bool get isDesktop {
|
||||||
// special check for running on linux based phones
|
// special check for running on linux based phones
|
||||||
if (Platform.isLinux && screenWidth != null && screenWidth! < 800) {
|
if (Platform.isLinux && screenWidth != null && screenWidth! < 800) {
|
||||||
|
|
|
@ -70,9 +70,10 @@ class _AddressBookCardState extends ConsumerState<AddressBookCard> {
|
||||||
final contact = _contact!;
|
final contact = _contact!;
|
||||||
|
|
||||||
final List<Coin> coins = [];
|
final List<Coin> coins = [];
|
||||||
for (var element in contact.addresses) {
|
|
||||||
if (!coins.contains(element.coin)) {
|
for (final coin in Coin.values) {
|
||||||
coins.add(element.coin);
|
if (contact.addresses.where((e) => e.coin == coin).isNotEmpty) {
|
||||||
|
coins.add(coin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,11 +25,13 @@ class CoinCard extends ConsumerWidget {
|
||||||
required this.walletId,
|
required this.walletId,
|
||||||
required this.width,
|
required this.width,
|
||||||
required this.height,
|
required this.height,
|
||||||
|
required this.isFavorite,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String walletId;
|
final String walletId;
|
||||||
final double width;
|
final double width;
|
||||||
final double height;
|
final double height;
|
||||||
|
final bool isFavorite;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
@ -38,7 +40,7 @@ class CoinCard extends ConsumerWidget {
|
||||||
.select((value) => value.getManager(walletId).coin),
|
.select((value) => value.getManager(walletId).coin),
|
||||||
);
|
);
|
||||||
|
|
||||||
final bool hasCardImageBg = ref.watch(coinCardProvider(coin)) != null;
|
final bool hasCardImageBg = (isFavorite) ? ref.watch(coinCardFavoritesProvider(coin)) != null : ref.watch(coinCardProvider(coin)) != null;
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
|
@ -47,11 +49,16 @@ class CoinCard extends ConsumerWidget {
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(
|
||||||
|
Constants.size.circularBorderRadius,
|
||||||
|
),
|
||||||
image: DecorationImage(
|
image: DecorationImage(
|
||||||
fit: BoxFit.fill,
|
fit: BoxFit.cover,
|
||||||
image: FileImage(
|
image: FileImage(
|
||||||
File(
|
File(
|
||||||
ref.watch(coinCardProvider(coin))!,
|
(isFavorite)
|
||||||
|
? ref.watch(coinCardFavoritesProvider(coin))!
|
||||||
|
: ref.watch(coinCardProvider(coin))!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
446
lib/widgets/desktop/desktop_fee_dialog.dart
Normal file
446
lib/widgets/desktop/desktop_fee_dialog.dart
Normal file
|
@ -0,0 +1,446 @@
|
||||||
|
import 'package:cw_core/monero_transaction_priority.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:stackwallet/models/models.dart';
|
||||||
|
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
|
||||||
|
import 'package:stackwallet/pages/token_view/token_view.dart';
|
||||||
|
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart';
|
||||||
|
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||||
|
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
|
||||||
|
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
|
||||||
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
|
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
||||||
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
|
import 'package:stackwallet/widgets/animated_text.dart';
|
||||||
|
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||||
|
|
||||||
|
class DesktopFeeDialog extends ConsumerStatefulWidget {
|
||||||
|
const DesktopFeeDialog({
|
||||||
|
Key? key,
|
||||||
|
required this.walletId,
|
||||||
|
this.isToken = false,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final String walletId;
|
||||||
|
final bool isToken;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<DesktopFeeDialog> createState() => _DesktopFeeDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DesktopFeeDialogState extends ConsumerState<DesktopFeeDialog> {
|
||||||
|
late final String walletId;
|
||||||
|
|
||||||
|
FeeObject? feeObject;
|
||||||
|
FeeRateType feeRateType = FeeRateType.average;
|
||||||
|
|
||||||
|
Future<Amount> feeFor({
|
||||||
|
required Amount amount,
|
||||||
|
required FeeRateType feeRateType,
|
||||||
|
required int feeRate,
|
||||||
|
required Coin coin,
|
||||||
|
}) async {
|
||||||
|
switch (feeRateType) {
|
||||||
|
case FeeRateType.fast:
|
||||||
|
if (ref
|
||||||
|
.read(widget.isToken
|
||||||
|
? tokenFeeSessionCacheProvider
|
||||||
|
: feeSheetSessionCacheProvider)
|
||||||
|
.fast[amount] ==
|
||||||
|
null) {
|
||||||
|
if (widget.isToken == false) {
|
||||||
|
final manager =
|
||||||
|
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||||
|
|
||||||
|
if (coin == Coin.monero || coin == Coin.wownero) {
|
||||||
|
final fee = await manager.estimateFeeFor(
|
||||||
|
amount, MoneroTransactionPriority.fast.raw!);
|
||||||
|
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
|
||||||
|
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||||
|
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
||||||
|
"Private") {
|
||||||
|
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
||||||
|
await (manager.wallet as FiroWallet)
|
||||||
|
.estimateFeeForPublic(amount, feeRate);
|
||||||
|
} else {
|
||||||
|
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
||||||
|
await manager.estimateFeeFor(amount, feeRate);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final tokenWallet = ref.read(tokenServiceProvider)!;
|
||||||
|
final fee = tokenWallet.estimateFeeFor(feeRate);
|
||||||
|
ref.read(tokenFeeSessionCacheProvider).fast[amount] = fee;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ref
|
||||||
|
.read(widget.isToken
|
||||||
|
? tokenFeeSessionCacheProvider
|
||||||
|
: feeSheetSessionCacheProvider)
|
||||||
|
.fast[amount]!;
|
||||||
|
|
||||||
|
case FeeRateType.average:
|
||||||
|
if (ref
|
||||||
|
.read(widget.isToken
|
||||||
|
? tokenFeeSessionCacheProvider
|
||||||
|
: feeSheetSessionCacheProvider)
|
||||||
|
.average[amount] ==
|
||||||
|
null) {
|
||||||
|
if (widget.isToken == false) {
|
||||||
|
final manager =
|
||||||
|
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||||
|
|
||||||
|
if (coin == Coin.monero || coin == Coin.wownero) {
|
||||||
|
final fee = await manager.estimateFeeFor(
|
||||||
|
amount, MoneroTransactionPriority.regular.raw!);
|
||||||
|
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
|
||||||
|
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||||
|
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
||||||
|
"Private") {
|
||||||
|
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
||||||
|
await (manager.wallet as FiroWallet)
|
||||||
|
.estimateFeeForPublic(amount, feeRate);
|
||||||
|
} else {
|
||||||
|
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
||||||
|
await manager.estimateFeeFor(amount, feeRate);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final tokenWallet = ref.read(tokenServiceProvider)!;
|
||||||
|
final fee = tokenWallet.estimateFeeFor(feeRate);
|
||||||
|
ref.read(tokenFeeSessionCacheProvider).average[amount] = fee;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ref
|
||||||
|
.read(widget.isToken
|
||||||
|
? tokenFeeSessionCacheProvider
|
||||||
|
: feeSheetSessionCacheProvider)
|
||||||
|
.average[amount]!;
|
||||||
|
|
||||||
|
case FeeRateType.slow:
|
||||||
|
if (ref
|
||||||
|
.read(widget.isToken
|
||||||
|
? tokenFeeSessionCacheProvider
|
||||||
|
: feeSheetSessionCacheProvider)
|
||||||
|
.slow[amount] ==
|
||||||
|
null) {
|
||||||
|
if (widget.isToken == false) {
|
||||||
|
final manager =
|
||||||
|
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||||
|
|
||||||
|
if (coin == Coin.monero || coin == Coin.wownero) {
|
||||||
|
final fee = await manager.estimateFeeFor(
|
||||||
|
amount, MoneroTransactionPriority.slow.raw!);
|
||||||
|
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
|
||||||
|
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||||
|
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
||||||
|
"Private") {
|
||||||
|
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
||||||
|
await (manager.wallet as FiroWallet)
|
||||||
|
.estimateFeeForPublic(amount, feeRate);
|
||||||
|
} else {
|
||||||
|
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
||||||
|
await manager.estimateFeeFor(amount, feeRate);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final tokenWallet = ref.read(tokenServiceProvider)!;
|
||||||
|
final fee = tokenWallet.estimateFeeFor(feeRate);
|
||||||
|
ref.read(tokenFeeSessionCacheProvider).slow[amount] = fee;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ref
|
||||||
|
.read(widget.isToken
|
||||||
|
? tokenFeeSessionCacheProvider
|
||||||
|
: feeSheetSessionCacheProvider)
|
||||||
|
.slow[amount]!;
|
||||||
|
default:
|
||||||
|
return Amount.zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
walletId = widget.walletId;
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DesktopDialog(
|
||||||
|
maxWidth: 450,
|
||||||
|
maxHeight: double.infinity,
|
||||||
|
child: FutureBuilder(
|
||||||
|
future: ref.watch(
|
||||||
|
walletsChangeNotifierProvider.select(
|
||||||
|
(value) => value.getManager(walletId).fees,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.done &&
|
||||||
|
snapshot.hasData) {
|
||||||
|
feeObject = snapshot.data!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 32),
|
||||||
|
child: Text(
|
||||||
|
"Choose fee",
|
||||||
|
style: STextStyles.desktopH3(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const DesktopDialogCloseButton(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
...FeeRateType.values.map(
|
||||||
|
(e) => Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 32,
|
||||||
|
right: 32,
|
||||||
|
bottom: 16,
|
||||||
|
),
|
||||||
|
child: DesktopFeeItem(
|
||||||
|
feeObject: feeObject,
|
||||||
|
feeRateType: e,
|
||||||
|
walletId: walletId,
|
||||||
|
feeFor: feeFor,
|
||||||
|
isSelected: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DesktopFeeItem extends ConsumerStatefulWidget {
|
||||||
|
const DesktopFeeItem({
|
||||||
|
Key? key,
|
||||||
|
required this.feeObject,
|
||||||
|
required this.feeRateType,
|
||||||
|
required this.walletId,
|
||||||
|
required this.feeFor,
|
||||||
|
required this.isSelected,
|
||||||
|
this.isButton = true,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final FeeObject? feeObject;
|
||||||
|
final FeeRateType feeRateType;
|
||||||
|
final String walletId;
|
||||||
|
final Future<Amount> Function({
|
||||||
|
required Amount amount,
|
||||||
|
required FeeRateType feeRateType,
|
||||||
|
required int feeRate,
|
||||||
|
required Coin coin,
|
||||||
|
}) feeFor;
|
||||||
|
final bool isSelected;
|
||||||
|
final bool isButton;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<DesktopFeeItem> createState() => _DesktopFeeItemState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DesktopFeeItemState extends ConsumerState<DesktopFeeItem> {
|
||||||
|
String? feeString;
|
||||||
|
String? timeString;
|
||||||
|
|
||||||
|
static const stringsToLoopThrough = [
|
||||||
|
"Calculating",
|
||||||
|
"Calculating.",
|
||||||
|
"Calculating..",
|
||||||
|
"Calculating...",
|
||||||
|
];
|
||||||
|
|
||||||
|
String estimatedTimeToBeIncludedInNextBlock(
|
||||||
|
int targetBlockTime, int estimatedNumberOfBlocks) {
|
||||||
|
int time = targetBlockTime * estimatedNumberOfBlocks;
|
||||||
|
|
||||||
|
int hours = (time / 3600).floor();
|
||||||
|
if (hours > 1) {
|
||||||
|
return "~$hours hours";
|
||||||
|
} else if (hours == 1) {
|
||||||
|
return "~$hours hour";
|
||||||
|
}
|
||||||
|
|
||||||
|
// less than an hour
|
||||||
|
|
||||||
|
final string = (time / 60).toStringAsFixed(1);
|
||||||
|
|
||||||
|
if (string == "1.0") {
|
||||||
|
return "~1 minute";
|
||||||
|
} else {
|
||||||
|
if (string.endsWith(".0")) {
|
||||||
|
return "~${(time / 60).floor()} minutes";
|
||||||
|
}
|
||||||
|
return "~$string minutes";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
debugPrint("BUILD: $runtimeType : ${widget.feeRateType}");
|
||||||
|
|
||||||
|
return ConditionalParent(
|
||||||
|
condition: widget.isButton,
|
||||||
|
builder: (child) => MaterialButton(
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop(
|
||||||
|
(
|
||||||
|
widget.feeRateType,
|
||||||
|
feeString,
|
||||||
|
timeString,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
child: Builder(
|
||||||
|
builder: (_) {
|
||||||
|
if (!widget.isButton) {
|
||||||
|
final coin = ref.watch(
|
||||||
|
walletsChangeNotifierProvider.select(
|
||||||
|
(value) => value.getManager(widget.walletId).coin,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||||
|
ref.watch(publicPrivateBalanceStateProvider.state).state ==
|
||||||
|
"Private") {
|
||||||
|
return Text(
|
||||||
|
"~${ref.watch(pAmountFormatter(coin)).format(
|
||||||
|
Amount(
|
||||||
|
rawValue: BigInt.parse("3794"),
|
||||||
|
fractionDigits: coin.decimals,
|
||||||
|
),
|
||||||
|
indicatePrecisionLoss: false,
|
||||||
|
)}",
|
||||||
|
style: STextStyles.desktopTextExtraExtraSmall(context).copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFieldActiveText,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.feeRateType == FeeRateType.custom) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
widget.feeRateType.prettyName,
|
||||||
|
style:
|
||||||
|
STextStyles.desktopTextExtraExtraSmall(context).copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFieldActiveText,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final manager = ref.watch(walletsChangeNotifierProvider
|
||||||
|
.select((value) => value.getManager(widget.walletId)));
|
||||||
|
|
||||||
|
if (widget.feeObject == null) {
|
||||||
|
return AnimatedText(
|
||||||
|
stringsToLoopThrough: stringsToLoopThrough,
|
||||||
|
style: STextStyles.desktopTextExtraExtraSmall(context).copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFieldActiveText,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return FutureBuilder(
|
||||||
|
future: widget.feeFor(
|
||||||
|
coin: manager.coin,
|
||||||
|
feeRateType: widget.feeRateType,
|
||||||
|
feeRate: widget.feeRateType == FeeRateType.fast
|
||||||
|
? widget.feeObject!.fast
|
||||||
|
: widget.feeRateType == FeeRateType.slow
|
||||||
|
? widget.feeObject!.slow
|
||||||
|
: widget.feeObject!.medium,
|
||||||
|
amount: ref.watch(sendAmountProvider.state).state,
|
||||||
|
),
|
||||||
|
builder: (_, AsyncSnapshot<Amount> snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.done &&
|
||||||
|
snapshot.hasData) {
|
||||||
|
feeString = "${widget.feeRateType.prettyName} "
|
||||||
|
"(~${ref.watch(pAmountFormatter(manager.coin)).format(
|
||||||
|
snapshot.data!,
|
||||||
|
indicatePrecisionLoss: false,
|
||||||
|
)})";
|
||||||
|
|
||||||
|
timeString = manager.coin == Coin.ethereum
|
||||||
|
? ""
|
||||||
|
: estimatedTimeToBeIncludedInNextBlock(
|
||||||
|
Constants.targetBlockTimeInSeconds(manager.coin),
|
||||||
|
widget.feeRateType == FeeRateType.fast
|
||||||
|
? widget.feeObject!.numberOfBlocksFast
|
||||||
|
: widget.feeRateType == FeeRateType.slow
|
||||||
|
? widget.feeObject!.numberOfBlocksSlow
|
||||||
|
: widget.feeObject!.numberOfBlocksAverage,
|
||||||
|
);
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
feeString!,
|
||||||
|
style: STextStyles.desktopTextExtraExtraSmall(context)
|
||||||
|
.copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFieldActiveText,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
if (widget.feeObject != null)
|
||||||
|
Text(
|
||||||
|
timeString!,
|
||||||
|
style: STextStyles.desktopTextExtraExtraSmall(context)
|
||||||
|
.copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFieldActiveSearchIconRight,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return AnimatedText(
|
||||||
|
stringsToLoopThrough: stringsToLoopThrough,
|
||||||
|
style: STextStyles.desktopTextExtraExtraSmall(context)
|
||||||
|
.copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFieldActiveText,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
66
lib/widgets/fee_slider.dart
Normal file
66
lib/widgets/fee_slider.dart
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
|
|
||||||
|
class FeeSlider extends StatefulWidget {
|
||||||
|
const FeeSlider({
|
||||||
|
super.key,
|
||||||
|
required this.onSatVByteChanged,
|
||||||
|
required this.coin,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Coin coin;
|
||||||
|
final void Function(int) onSatVByteChanged;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FeeSlider> createState() => _FeeSliderState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FeeSliderState extends State<FeeSlider> {
|
||||||
|
static const double min = 1;
|
||||||
|
static const double max = 4;
|
||||||
|
|
||||||
|
double sliderValue = 0;
|
||||||
|
|
||||||
|
int rate = min.toInt();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"sat/vByte",
|
||||||
|
style: STextStyles.smallMed12(context),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"$rate",
|
||||||
|
style: STextStyles.smallMed12(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Slider(
|
||||||
|
value: sliderValue,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
sliderValue = value;
|
||||||
|
final number = pow(sliderValue * (max - min) + min, 4).toDouble();
|
||||||
|
switch (widget.coin) {
|
||||||
|
case Coin.dogecoin:
|
||||||
|
case Coin.dogecoinTestNet:
|
||||||
|
rate = (number * 1000).toInt();
|
||||||
|
default:
|
||||||
|
rate = number.toInt();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
widget.onSatVByteChanged(rate);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,17 +9,19 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stackwallet/models/exchange/aggregate_currency.dart';
|
import 'package:stackwallet/models/exchange/aggregate_currency.dart';
|
||||||
import 'package:stackwallet/pages/buy_view/sub_widgets/crypto_selection_view.dart';
|
import 'package:stackwallet/pages/buy_view/sub_widgets/crypto_selection_view.dart';
|
||||||
|
import 'package:stackwallet/providers/global/locale_provider.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/amount/amount_input_formatter.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||||
|
|
||||||
class ExchangeTextField extends StatefulWidget {
|
class ExchangeTextField extends ConsumerStatefulWidget {
|
||||||
const ExchangeTextField({
|
const ExchangeTextField({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.borderRadius = 0,
|
this.borderRadius = 0,
|
||||||
|
@ -55,10 +57,10 @@ class ExchangeTextField extends StatefulWidget {
|
||||||
final AggregateCurrency? currency;
|
final AggregateCurrency? currency;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ExchangeTextField> createState() => _ExchangeTextFieldState();
|
ConsumerState<ExchangeTextField> createState() => _ExchangeTextFieldState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ExchangeTextFieldState extends State<ExchangeTextField> {
|
class _ExchangeTextFieldState extends ConsumerState<ExchangeTextField> {
|
||||||
late final TextEditingController controller;
|
late final TextEditingController controller;
|
||||||
late final FocusNode focusNode;
|
late final FocusNode focusNode;
|
||||||
late final TextStyle textStyle;
|
late final TextStyle textStyle;
|
||||||
|
@ -130,12 +132,17 @@ class _ExchangeTextFieldState extends State<ExchangeTextField> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
// regex to validate a crypto amount with 8 decimal places
|
AmountInputFormatter(
|
||||||
TextInputFormatter.withFunction((oldValue, newValue) =>
|
decimals: 8, // todo change this
|
||||||
RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
|
locale: ref.watch(localeServiceChangeNotifierProvider
|
||||||
.hasMatch(newValue.text)
|
.select((value) => value.locale)),
|
||||||
? newValue
|
),
|
||||||
: oldValue),
|
// // regex to validate a crypto amount with 8 decimal places
|
||||||
|
// TextInputFormatter.withFunction((oldValue, newValue) =>
|
||||||
|
// RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
|
||||||
|
// .hasMatch(newValue.text)
|
||||||
|
// ? newValue
|
||||||
|
// : oldValue),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -230,7 +230,9 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
child: Text(
|
child: Text(
|
||||||
_transaction.isCancelled
|
_transaction.isCancelled
|
||||||
? "Cancelled"
|
? coin == Coin.ethereum
|
||||||
|
? "Failed"
|
||||||
|
: "Cancelled"
|
||||||
: whatIsIt(
|
: whatIsIt(
|
||||||
_transaction.type,
|
_transaction.type,
|
||||||
coin,
|
coin,
|
||||||
|
|
|
@ -26,6 +26,7 @@ import 'package:stackwallet/services/ethereum/ethereum_token_service.dart';
|
||||||
import 'package:stackwallet/services/transaction_notification_tracker.dart';
|
import 'package:stackwallet/services/transaction_notification_tracker.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
import 'package:stackwallet/utilities/show_loading.dart';
|
import 'package:stackwallet/utilities/show_loading.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||||
|
@ -136,17 +137,18 @@ class SimpleWalletCard extends ConsumerWidget {
|
||||||
context: desktopNavigatorState?.context ?? context,
|
context: desktopNavigatorState?.context ?? context,
|
||||||
opaqueBG: true,
|
opaqueBG: true,
|
||||||
message: "Loading ${contract.name}",
|
message: "Loading ${contract.name}",
|
||||||
|
isDesktop: Util.isDesktop,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
|
// TODO: show error dialog here?
|
||||||
|
Logging.instance.log(
|
||||||
|
"Failed to load token wallet for $contract",
|
||||||
|
level: LogLevel.Error,
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desktopNavigatorState == null) {
|
|
||||||
// pop loading
|
|
||||||
nav.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (desktopNavigatorState != null) {
|
if (desktopNavigatorState != null) {
|
||||||
await desktopNavigatorState!.pushNamed(
|
await desktopNavigatorState!.pushNamed(
|
||||||
DesktopTokenView.routeName,
|
DesktopTokenView.routeName,
|
||||||
|
|
|
@ -6,6 +6,9 @@ import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import connectivity_plus
|
import connectivity_plus
|
||||||
|
import cw_monero
|
||||||
|
import cw_shared_external
|
||||||
|
import cw_wownero
|
||||||
import desktop_drop
|
import desktop_drop
|
||||||
import device_info_plus
|
import device_info_plus
|
||||||
import devicelocale
|
import devicelocale
|
||||||
|
@ -13,10 +16,10 @@ import flutter_libepiccash
|
||||||
import flutter_local_notifications
|
import flutter_local_notifications
|
||||||
import flutter_secure_storage_macos
|
import flutter_secure_storage_macos
|
||||||
import isar_flutter_libs
|
import isar_flutter_libs
|
||||||
|
import lelantus
|
||||||
import package_info_plus
|
import package_info_plus
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
import share_plus
|
import share_plus
|
||||||
import shared_preferences_foundation
|
|
||||||
import stack_wallet_backup
|
import stack_wallet_backup
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
import wakelock_macos
|
import wakelock_macos
|
||||||
|
@ -24,6 +27,9 @@ import window_size
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
|
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
|
||||||
|
CwMoneroPlugin.register(with: registry.registrar(forPlugin: "CwMoneroPlugin"))
|
||||||
|
CwSharedExternalPlugin.register(with: registry.registrar(forPlugin: "CwSharedExternalPlugin"))
|
||||||
|
CwWowneroPlugin.register(with: registry.registrar(forPlugin: "CwWowneroPlugin"))
|
||||||
DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin"))
|
DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin"))
|
||||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin"))
|
DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin"))
|
||||||
|
@ -31,10 +37,10 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||||
IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin"))
|
IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin"))
|
||||||
|
LelantusPlugin.register(with: registry.registrar(forPlugin: "LelantusPlugin"))
|
||||||
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
|
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
|
||||||
StackWalletBackupPlugin.register(with: registry.registrar(forPlugin: "StackWalletBackupPlugin"))
|
StackWalletBackupPlugin.register(with: registry.registrar(forPlugin: "StackWalletBackupPlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin"))
|
WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin"))
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
platform :osx, '10.11'
|
platform :osx, '10.14'
|
||||||
|
|
||||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
|
@ -31,6 +31,9 @@ target 'Runner' do
|
||||||
use_modular_headers!
|
use_modular_headers!
|
||||||
|
|
||||||
flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
|
flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
|
||||||
|
target 'RunnerTests' do
|
||||||
|
inherit! :search_paths
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
post_install do |installer|
|
post_install do |installer|
|
||||||
|
|
|
@ -1,24 +1,76 @@
|
||||||
PODS:
|
PODS:
|
||||||
- connectivity_plus_macos (0.0.1):
|
- connectivity_plus (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- ReachabilitySwift
|
- ReachabilitySwift
|
||||||
|
- cw_monero (0.0.1):
|
||||||
|
- cw_monero/Boost (= 0.0.1)
|
||||||
|
- cw_monero/Monero (= 0.0.1)
|
||||||
|
- cw_monero/OpenSSL (= 0.0.1)
|
||||||
|
- cw_monero/Sodium (= 0.0.1)
|
||||||
|
- cw_monero/Unbound (= 0.0.1)
|
||||||
|
- FlutterMacOS
|
||||||
|
- cw_monero/Boost (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- cw_monero/Monero (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- cw_monero/OpenSSL (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- cw_monero/Sodium (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- cw_monero/Unbound (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- cw_shared_external (0.0.1):
|
||||||
|
- cw_shared_external/Boost (= 0.0.1)
|
||||||
|
- cw_shared_external/OpenSSL (= 0.0.1)
|
||||||
|
- cw_shared_external/Sodium (= 0.0.1)
|
||||||
|
- FlutterMacOS
|
||||||
|
- cw_shared_external/Boost (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- cw_shared_external/OpenSSL (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- cw_shared_external/Sodium (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- cw_wownero (0.0.1):
|
||||||
|
- cw_wownero/Boost (= 0.0.1)
|
||||||
|
- cw_wownero/OpenSSL (= 0.0.1)
|
||||||
|
- cw_wownero/Sodium (= 0.0.1)
|
||||||
|
- cw_wownero/Unbound (= 0.0.1)
|
||||||
|
- cw_wownero/Wownero (= 0.0.1)
|
||||||
|
- FlutterMacOS
|
||||||
|
- cw_wownero/Boost (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- cw_wownero/OpenSSL (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- cw_wownero/Sodium (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- cw_wownero/Unbound (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- cw_wownero/Wownero (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- desktop_drop (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- device_info_plus (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- devicelocale (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
- flutter_libepiccash (0.0.1):
|
- flutter_libepiccash (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- flutter_local_notifications (0.0.1):
|
- flutter_local_notifications (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- flutter_secure_storage_macos (3.3.1):
|
- flutter_secure_storage_macos (6.1.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- FlutterMacOS (1.0.0)
|
- FlutterMacOS (1.0.0)
|
||||||
- isar_flutter_libs (1.0.0):
|
- isar_flutter_libs (1.0.0):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- package_info_plus_macos (0.0.1):
|
- lelantus (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- path_provider_macos (0.0.1):
|
- package_info_plus (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- path_provider_foundation (0.0.1):
|
||||||
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- ReachabilitySwift (5.0.0)
|
- ReachabilitySwift (5.0.0)
|
||||||
- share_plus_macos (0.0.1):
|
- share_plus (0.0.1):
|
||||||
- FlutterMacOS
|
|
||||||
- shared_preferences_macos (0.0.1):
|
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- stack_wallet_backup (0.0.1):
|
- stack_wallet_backup (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
@ -30,16 +82,22 @@ PODS:
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- connectivity_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus_macos/macos`)
|
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`)
|
||||||
|
- cw_monero (from `Flutter/ephemeral/.symlinks/plugins/cw_monero/macos`)
|
||||||
|
- cw_shared_external (from `Flutter/ephemeral/.symlinks/plugins/cw_shared_external/macos`)
|
||||||
|
- cw_wownero (from `Flutter/ephemeral/.symlinks/plugins/cw_wownero/macos`)
|
||||||
|
- desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`)
|
||||||
|
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
|
||||||
|
- devicelocale (from `Flutter/ephemeral/.symlinks/plugins/devicelocale/macos`)
|
||||||
- flutter_libepiccash (from `Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos`)
|
- flutter_libepiccash (from `Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos`)
|
||||||
- flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`)
|
- flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`)
|
||||||
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
|
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
|
||||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||||
- isar_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos`)
|
- isar_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos`)
|
||||||
- package_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos`)
|
- lelantus (from `Flutter/ephemeral/.symlinks/plugins/lelantus/macos`)
|
||||||
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
|
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
|
||||||
- share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`)
|
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`)
|
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
|
||||||
- stack_wallet_backup (from `Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos`)
|
- stack_wallet_backup (from `Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos`)
|
||||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||||
- wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`)
|
- wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`)
|
||||||
|
@ -50,8 +108,20 @@ SPEC REPOS:
|
||||||
- ReachabilitySwift
|
- ReachabilitySwift
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
connectivity_plus_macos:
|
connectivity_plus:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos
|
||||||
|
cw_monero:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/cw_monero/macos
|
||||||
|
cw_shared_external:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/cw_shared_external/macos
|
||||||
|
cw_wownero:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/cw_wownero/macos
|
||||||
|
desktop_drop:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos
|
||||||
|
device_info_plus:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos
|
||||||
|
devicelocale:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/devicelocale/macos
|
||||||
flutter_libepiccash:
|
flutter_libepiccash:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos
|
||||||
flutter_local_notifications:
|
flutter_local_notifications:
|
||||||
|
@ -62,14 +132,14 @@ EXTERNAL SOURCES:
|
||||||
:path: Flutter/ephemeral
|
:path: Flutter/ephemeral
|
||||||
isar_flutter_libs:
|
isar_flutter_libs:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos
|
||||||
package_info_plus_macos:
|
lelantus:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/lelantus/macos
|
||||||
path_provider_macos:
|
package_info_plus:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
|
||||||
share_plus_macos:
|
path_provider_foundation:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
|
||||||
shared_preferences_macos:
|
share_plus:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos
|
||||||
stack_wallet_backup:
|
stack_wallet_backup:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos
|
||||||
url_launcher_macos:
|
url_launcher_macos:
|
||||||
|
@ -80,22 +150,28 @@ EXTERNAL SOURCES:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/window_size/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/window_size/macos
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
connectivity_plus_macos: f6e86fd000e971d361e54b5afcadc8c8fa773308
|
connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747
|
||||||
flutter_libepiccash: b33f7396504712b513b8ff019a3f6f3bdae54cfb
|
cw_monero: a3442556ad3c06365c912735e4a23942a28692b1
|
||||||
|
cw_shared_external: 1f631d1132521baac5f4caed43176fa10d4e0d8b
|
||||||
|
cw_wownero: b4adb1e701fc363de27fa222fcaf4eff6f5fa63a
|
||||||
|
desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898
|
||||||
|
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
|
||||||
|
devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225
|
||||||
|
flutter_libepiccash: 9113ac75dd325f8bcf00bc3ab583c7fc2780cf3c
|
||||||
flutter_local_notifications: 3805ca215b2fb7f397d78b66db91f6a747af52e4
|
flutter_local_notifications: 3805ca215b2fb7f397d78b66db91f6a747af52e4
|
||||||
flutter_secure_storage_macos: 6ceee8fbc7f484553ad17f79361b556259df89aa
|
flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea
|
||||||
FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811
|
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||||
isar_flutter_libs: 1948109973b6c2e46d6196b1537688a36a6edeac
|
isar_flutter_libs: 43385c99864c168fadba7c9adeddc5d38838ca6a
|
||||||
package_info_plus_macos: f010621b07802a241d96d01876d6705f15e77c1c
|
lelantus: 3dfbf92b1e66b3573494dfe3d6a21c4988b5361b
|
||||||
path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
|
package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce
|
||||||
|
path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
|
||||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||||
share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4
|
share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7
|
||||||
shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727
|
|
||||||
stack_wallet_backup: 6ebc60b1bdcf11cf1f1cbad9aa78332e1e15778c
|
stack_wallet_backup: 6ebc60b1bdcf11cf1f1cbad9aa78332e1e15778c
|
||||||
url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3
|
url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451
|
||||||
wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9
|
wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9
|
||||||
window_size: 339dafa0b27a95a62a843042038fa6c3c48de195
|
window_size: 339dafa0b27a95a62a843042038fa6c3c48de195
|
||||||
|
|
||||||
PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c
|
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
|
||||||
|
|
||||||
COCOAPODS: 1.11.3
|
COCOAPODS: 1.11.3
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 51;
|
objectVersion = 54;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXAggregateTarget section */
|
/* Begin PBXAggregateTarget section */
|
||||||
|
@ -21,15 +21,27 @@
|
||||||
/* End PBXAggregateTarget section */
|
/* End PBXAggregateTarget section */
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
|
||||||
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
|
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
|
||||||
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
|
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
|
||||||
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
|
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
|
||||||
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
|
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
|
||||||
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
|
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
|
||||||
36299DF6FDF6725B2B9C51D5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6BB87EF657A3ADFB1CE3E959 /* Pods_Runner.framework */; };
|
B98151812A674022009D013C /* mobileliblelantus.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B98151802A674022009D013C /* mobileliblelantus.framework */; };
|
||||||
|
B98151822A67402A009D013C /* mobileliblelantus.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = B98151802A674022009D013C /* mobileliblelantus.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
|
B98151842A674143009D013C /* libsqlite3.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = B98151832A674143009D013C /* libsqlite3.0.tbd */; };
|
||||||
|
BFD0376C00E1FFD46376BB9D /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9206484E84CB0AD93E3E68CA /* Pods_RunnerTests.framework */; };
|
||||||
|
F653CA022D33E8B60E11A9F3 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E6036BF01BF05EA773C76D22 /* Pods_Runner.framework */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 33CC10EC2044A3C60003C045;
|
||||||
|
remoteInfo = Runner;
|
||||||
|
};
|
||||||
33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
|
33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
|
||||||
isa = PBXContainerItemProxy;
|
isa = PBXContainerItemProxy;
|
||||||
containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
|
containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
|
||||||
|
@ -46,6 +58,7 @@
|
||||||
dstPath = "";
|
dstPath = "";
|
||||||
dstSubfolderSpec = 10;
|
dstSubfolderSpec = 10;
|
||||||
files = (
|
files = (
|
||||||
|
B98151822A67402A009D013C /* mobileliblelantus.framework in Bundle Framework */,
|
||||||
);
|
);
|
||||||
name = "Bundle Framework";
|
name = "Bundle Framework";
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -53,9 +66,14 @@
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
0FA914E59929120BA65E8403 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
174539D042E7AC2AB25A83EB /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
27CB73AACA5743180CC6CD50 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||||
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
||||||
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
||||||
33CC10ED2044A3C60003C045 /* Stack Wallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Stack Wallet.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
33CC10ED2044A3C60003C045 /* Stack Wallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Stack Wallet.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
|
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
|
||||||
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||||
|
@ -67,26 +85,47 @@
|
||||||
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
|
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
|
||||||
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
|
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
|
||||||
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
|
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
|
||||||
6BB87EF657A3ADFB1CE3E959 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
41EE721BF40B8DE895352A2C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
|
||||||
937DF254AD7EDA15AFE96BD9 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
9206484E84CB0AD93E3E68CA /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
BC4589C48A71C3A1A477DD76 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
ACB8E553D75AA4AC9A7656CE /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
EA2D897BC13EBFB1DE697D5C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
B98151802A674022009D013C /* mobileliblelantus.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = mobileliblelantus.framework; path = ../crypto_plugins/flutter_liblelantus/scripts/macos/mobileliblelantus/mobileliblelantus.framework; sourceTree = "<group>"; };
|
||||||
|
B98151832A674143009D013C /* libsqlite3.0.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.0.tbd; path = usr/lib/libsqlite3.0.tbd; sourceTree = SDKROOT; };
|
||||||
|
BF5E76865ACB46314AC27D8F /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
E6036BF01BF05EA773C76D22 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
331C80D2294CF70F00263BE5 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
BFD0376C00E1FFD46376BB9D /* Pods_RunnerTests.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
33CC10EA2044A3C60003C045 /* Frameworks */ = {
|
33CC10EA2044A3C60003C045 /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
36299DF6FDF6725B2B9C51D5 /* Pods_Runner.framework in Frameworks */,
|
B98151842A674143009D013C /* libsqlite3.0.tbd in Frameworks */,
|
||||||
|
B98151812A674022009D013C /* mobileliblelantus.framework in Frameworks */,
|
||||||
|
F653CA022D33E8B60E11A9F3 /* Pods_Runner.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
331C80D6294CF71000263BE5 /* RunnerTests */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
331C80D7294CF71000263BE5 /* RunnerTests.swift */,
|
||||||
|
);
|
||||||
|
path = RunnerTests;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
33BA886A226E78AF003329D5 /* Configs */ = {
|
33BA886A226E78AF003329D5 /* Configs */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -103,9 +142,10 @@
|
||||||
children = (
|
children = (
|
||||||
33FAB671232836740065AC1E /* Runner */,
|
33FAB671232836740065AC1E /* Runner */,
|
||||||
33CEB47122A05771004F2AC0 /* Flutter */,
|
33CEB47122A05771004F2AC0 /* Flutter */,
|
||||||
|
331C80D6294CF71000263BE5 /* RunnerTests */,
|
||||||
33CC10EE2044A3C60003C045 /* Products */,
|
33CC10EE2044A3C60003C045 /* Products */,
|
||||||
D73912EC22F37F3D000D13A0 /* Frameworks */,
|
D73912EC22F37F3D000D13A0 /* Frameworks */,
|
||||||
9000119722579F22067B9BC0 /* Pods */,
|
F0D4A0626F78BE1EF2A1E0D6 /* Pods */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
@ -113,6 +153,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
33CC10ED2044A3C60003C045 /* Stack Wallet.app */,
|
33CC10ED2044A3C60003C045 /* Stack Wallet.app */,
|
||||||
|
331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -152,39 +193,63 @@
|
||||||
path = Runner;
|
path = Runner;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
9000119722579F22067B9BC0 /* Pods */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
EA2D897BC13EBFB1DE697D5C /* Pods-Runner.debug.xcconfig */,
|
|
||||||
937DF254AD7EDA15AFE96BD9 /* Pods-Runner.release.xcconfig */,
|
|
||||||
BC4589C48A71C3A1A477DD76 /* Pods-Runner.profile.xcconfig */,
|
|
||||||
);
|
|
||||||
name = Pods;
|
|
||||||
path = Pods;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
|
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
6BB87EF657A3ADFB1CE3E959 /* Pods_Runner.framework */,
|
B98151832A674143009D013C /* libsqlite3.0.tbd */,
|
||||||
|
B98151802A674022009D013C /* mobileliblelantus.framework */,
|
||||||
|
E6036BF01BF05EA773C76D22 /* Pods_Runner.framework */,
|
||||||
|
9206484E84CB0AD93E3E68CA /* Pods_RunnerTests.framework */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
F0D4A0626F78BE1EF2A1E0D6 /* Pods */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
41EE721BF40B8DE895352A2C /* Pods-Runner.debug.xcconfig */,
|
||||||
|
0FA914E59929120BA65E8403 /* Pods-Runner.release.xcconfig */,
|
||||||
|
ACB8E553D75AA4AC9A7656CE /* Pods-Runner.profile.xcconfig */,
|
||||||
|
BF5E76865ACB46314AC27D8F /* Pods-RunnerTests.debug.xcconfig */,
|
||||||
|
174539D042E7AC2AB25A83EB /* Pods-RunnerTests.release.xcconfig */,
|
||||||
|
27CB73AACA5743180CC6CD50 /* Pods-RunnerTests.profile.xcconfig */,
|
||||||
|
);
|
||||||
|
path = Pods;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
|
331C80D4294CF70F00263BE5 /* RunnerTests */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||||
|
buildPhases = (
|
||||||
|
FF8CC09C83E12FC1C6EE6A8F /* [CP] Check Pods Manifest.lock */,
|
||||||
|
331C80D1294CF70F00263BE5 /* Sources */,
|
||||||
|
331C80D2294CF70F00263BE5 /* Frameworks */,
|
||||||
|
331C80D3294CF70F00263BE5 /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
331C80DA294CF71000263BE5 /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
name = RunnerTests;
|
||||||
|
productName = RunnerTests;
|
||||||
|
productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;
|
||||||
|
productType = "com.apple.product-type.bundle.unit-test";
|
||||||
|
};
|
||||||
33CC10EC2044A3C60003C045 /* Runner */ = {
|
33CC10EC2044A3C60003C045 /* Runner */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
|
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
DF80A3E256A63BF2D2008937 /* [CP] Check Pods Manifest.lock */,
|
AAFD86C9DC38B4393BC9D8E0 /* [CP] Check Pods Manifest.lock */,
|
||||||
33CC10E92044A3C60003C045 /* Sources */,
|
33CC10E92044A3C60003C045 /* Sources */,
|
||||||
33CC10EA2044A3C60003C045 /* Frameworks */,
|
33CC10EA2044A3C60003C045 /* Frameworks */,
|
||||||
33CC10EB2044A3C60003C045 /* Resources */,
|
33CC10EB2044A3C60003C045 /* Resources */,
|
||||||
33CC110E2044A8840003C045 /* Bundle Framework */,
|
33CC110E2044A8840003C045 /* Bundle Framework */,
|
||||||
3399D490228B24CF009A79C7 /* ShellScript */,
|
3399D490228B24CF009A79C7 /* ShellScript */,
|
||||||
8D7CC24E5AE846869656D4D1 /* [CP] Embed Pods Frameworks */,
|
529691D83C3BADE14E2EAC03 /* [CP] Embed Pods Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
|
@ -206,6 +271,10 @@
|
||||||
LastUpgradeCheck = 1300;
|
LastUpgradeCheck = 1300;
|
||||||
ORGANIZATIONNAME = "";
|
ORGANIZATIONNAME = "";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
|
331C80D4294CF70F00263BE5 = {
|
||||||
|
CreatedOnToolsVersion = 14.0;
|
||||||
|
TestTargetID = 33CC10EC2044A3C60003C045;
|
||||||
|
};
|
||||||
33CC10EC2044A3C60003C045 = {
|
33CC10EC2044A3C60003C045 = {
|
||||||
CreatedOnToolsVersion = 9.2;
|
CreatedOnToolsVersion = 9.2;
|
||||||
LastSwiftMigration = 1100;
|
LastSwiftMigration = 1100;
|
||||||
|
@ -236,12 +305,20 @@
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
33CC10EC2044A3C60003C045 /* Runner */,
|
33CC10EC2044A3C60003C045 /* Runner */,
|
||||||
|
331C80D4294CF70F00263BE5 /* RunnerTests */,
|
||||||
33CC111A2044C6BA0003C045 /* Flutter Assemble */,
|
33CC111A2044C6BA0003C045 /* Flutter Assemble */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
|
|
||||||
/* Begin PBXResourcesBuildPhase section */
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
331C80D3294CF70F00263BE5 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
33CC10EB2044A3C60003C045 /* Resources */ = {
|
33CC10EB2044A3C60003C045 /* Resources */ = {
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -256,6 +333,7 @@
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
3399D490228B24CF009A79C7 /* ShellScript */ = {
|
3399D490228B24CF009A79C7 /* ShellScript */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
alwaysOutOfDate = 1;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
|
@ -291,7 +369,7 @@
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
|
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
|
||||||
};
|
};
|
||||||
8D7CC24E5AE846869656D4D1 /* [CP] Embed Pods Frameworks */ = {
|
529691D83C3BADE14E2EAC03 /* [CP] Embed Pods Frameworks */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
@ -308,7 +386,7 @@
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
DF80A3E256A63BF2D2008937 /* [CP] Check Pods Manifest.lock */ = {
|
AAFD86C9DC38B4393BC9D8E0 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
@ -330,9 +408,39 @@
|
||||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
|
FF8CC09C83E12FC1C6EE6A8F /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
331C80D1294CF70F00263BE5 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
33CC10E92044A3C60003C045 /* Sources */ = {
|
33CC10E92044A3C60003C045 /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -346,6 +454,11 @@
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXTargetDependency section */
|
/* Begin PBXTargetDependency section */
|
||||||
|
331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 33CC10EC2044A3C60003C045 /* Runner */;
|
||||||
|
targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
|
33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
|
target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
|
||||||
|
@ -366,6 +479,51 @@
|
||||||
/* End PBXVariantGroup section */
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
|
331C80DB294CF71000263BE5 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = BF5E76865ACB46314AC27D8F /* Pods-RunnerTests.debug.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackWallet.RunnerTests;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Stack Wallet.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/stack_wallet";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
331C80DC294CF71000263BE5 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 174539D042E7AC2AB25A83EB /* Pods-RunnerTests.release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackWallet.RunnerTests;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Stack Wallet.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/stack_wallet";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
331C80DD294CF71000263BE5 /* Profile */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 27CB73AACA5743180CC6CD50 /* Pods-RunnerTests.profile.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackWallet.RunnerTests;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Stack Wallet.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/stack_wallet";
|
||||||
|
};
|
||||||
|
name = Profile;
|
||||||
|
};
|
||||||
338D0CE9231458BD00FA5F75 /* Profile */ = {
|
338D0CE9231458BD00FA5F75 /* Profile */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
|
@ -404,9 +562,11 @@
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
|
STRIP_INSTALLED_PRODUCT = NO;
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
};
|
};
|
||||||
|
@ -421,6 +581,30 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/connectivity_plus\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/cw_monero\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/cw_shared_external\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/cw_wownero\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/desktop_drop\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/device_info_plus\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/devicelocale\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libepiccash\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_local_notifications\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_secure_storage_macos\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/isar_flutter_libs\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/lelantus\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/package_info_plus\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/path_provider_foundation\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/share_plus\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/stack_wallet_backup\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/url_launcher_macos\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/wakelock_macos\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/window_size\"",
|
||||||
|
"\"${PROJECT_DIR}/../crypto_plugins/flutter_liblelantus/scripts/macos/mobileliblelantus\"",
|
||||||
|
);
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -483,10 +667,11 @@
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
|
STRIP_INSTALLED_PRODUCT = NO;
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
};
|
};
|
||||||
|
@ -530,9 +715,11 @@
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
|
STRIP_INSTALLED_PRODUCT = NO;
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
};
|
};
|
||||||
|
@ -547,6 +734,30 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/connectivity_plus\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/cw_monero\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/cw_shared_external\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/cw_wownero\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/desktop_drop\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/device_info_plus\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/devicelocale\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libepiccash\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_local_notifications\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_secure_storage_macos\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/isar_flutter_libs\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/lelantus\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/package_info_plus\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/path_provider_foundation\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/share_plus\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/stack_wallet_backup\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/url_launcher_macos\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/wakelock_macos\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/window_size\"",
|
||||||
|
"\"${PROJECT_DIR}/../crypto_plugins/flutter_liblelantus/scripts/macos/mobileliblelantus\"",
|
||||||
|
);
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -567,6 +778,30 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/connectivity_plus\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/cw_monero\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/cw_shared_external\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/cw_wownero\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/desktop_drop\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/device_info_plus\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/devicelocale\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libepiccash\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_local_notifications\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_secure_storage_macos\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/isar_flutter_libs\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/lelantus\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/package_info_plus\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/path_provider_foundation\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/share_plus\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/stack_wallet_backup\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/url_launcher_macos\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/wakelock_macos\"",
|
||||||
|
"\"${PODS_CONFIGURATION_BUILD_DIR}/window_size\"",
|
||||||
|
"\"${PROJECT_DIR}/../crypto_plugins/flutter_liblelantus/scripts/macos/mobileliblelantus\"",
|
||||||
|
);
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -596,6 +831,16 @@
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
|
331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
331C80DB294CF71000263BE5 /* Debug */,
|
||||||
|
331C80DC294CF71000263BE5 /* Release */,
|
||||||
|
331C80DD294CF71000263BE5 /* Profile */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
|
33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
|
|
|
@ -37,6 +37,17 @@
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
<Testables>
|
<Testables>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO"
|
||||||
|
parallelizable = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "331C80D4294CF70F00263BE5"
|
||||||
|
BuildableName = "RunnerTests.xctest"
|
||||||
|
BlueprintName = "RunnerTests"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
</Testables>
|
</Testables>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue