diff --git a/.metadata b/.metadata index d1551205d..675f4a740 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled. version: - revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + revision: f92f44110e87bad5ff168335c36da6f6053036e6 channel: stable project_type: app @@ -13,17 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - - platform: linux - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + create_revision: f92f44110e87bad5ff168335c36da6f6053036e6 + base_revision: f92f44110e87bad5ff168335c36da6f6053036e6 - platform: macos - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - - platform: windows - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + create_revision: f92f44110e87bad5ff168335c36da6f6053036e6 + base_revision: f92f44110e87bad5ff168335c36da6f6053036e6 # User provided section diff --git a/assets/default_themes/dark.zip b/assets/default_themes/dark.zip index 07c4d6521..fe11a5463 100644 Binary files a/assets/default_themes/dark.zip and b/assets/default_themes/dark.zip differ diff --git a/assets/default_themes/light.zip b/assets/default_themes/light.zip index 649716126..1453d6ba0 100644 Binary files a/assets/default_themes/light.zip and b/assets/default_themes/light.zip differ diff --git a/assets/icon/macos-icon.png b/assets/icon/macos-icon.png new file mode 100644 index 000000000..e4a263022 Binary files /dev/null and b/assets/icon/macos-icon.png differ diff --git a/assets/svg/monkey.svg b/assets/svg/monkey.svg new file mode 100644 index 000000000..565ac4fdf --- /dev/null +++ b/assets/svg/monkey.svg @@ -0,0 +1,3 @@ +<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M6.32099 5.11654C6.36635 4.1732 5.61228 3.38665 4.66934 3.38665H4.33832C3.26477 3.38665 2.34238 4.25531 2.34238 5.35395V13.0533C2.34238 15.0164 3.93487 16.6089 5.89793 16.6089H6.38407L6.39773 16.1229L6.42638 15.1044L6.42658 15.1044V15.0904C6.42658 12.5838 8.06723 10.4614 10.3343 9.74146C11.2194 10.8231 12.5669 11.5163 14.0747 11.5163C14.341 11.5163 14.5977 11.4793 14.8307 11.4349L17.2447 13.7633L17.2463 13.7649C18.1535 14.6318 18.6673 15.8317 18.6673 17.0892V17.1274C18.6673 17.4146 18.436 17.6459 18.1488 17.6459C17.8616 17.6459 17.6303 17.4146 17.6303 17.1274V17.0892C17.6303 16.1162 17.2343 15.1861 16.5298 14.5151L14.4197 12.5036L13.5747 11.6981V12.8655V16.1089V16.6089H14.0747H15.0932C15.3805 16.6089 15.6118 16.8402 15.6118 17.1274C15.6118 17.4146 15.3805 17.6459 15.0932 17.6459H5.92658C3.38996 17.6459 1.33398 15.5908 1.33398 13.0533V5.35395C1.33398 3.6952 2.67931 2.34962 4.33832 2.34962H4.66934C6.20478 2.34962 7.42841 3.6318 7.35595 5.16484L7.35592 5.16564C7.29694 6.45768 6.32086 7.52368 5.03701 7.70095L5.03698 7.70076L5.0232 7.70306L4.97418 7.71122C4.69124 7.74677 4.43295 7.54849 4.39441 7.26866C4.35516 6.98361 4.55436 6.72184 4.83595 6.68323L4.83596 6.68333L4.84599 6.68174L4.90097 6.67306C5.6898 6.56499 6.28431 5.90776 6.32099 5.11654ZM6.32099 5.11654C6.32098 5.11674 6.32097 5.11695 6.32096 5.11715L5.82154 5.09295L6.32101 5.11592C6.321 5.11612 6.32099 5.11633 6.32099 5.11654ZM11.4399 7.74158L11.3069 7.46881L11.0036 7.46089C10.1647 7.43901 9.48213 6.76462 9.48213 5.92368C9.48213 5.07034 10.1733 4.38712 10.9908 4.38665L11.308 4.38646C11.1303 4.68787 11.0284 5.03924 11.0284 5.41442C11.0284 6.26965 11.5569 6.99893 12.3044 7.29712C12.3563 8.22999 13.1288 8.96998 14.0747 8.96998C15.0206 8.96998 15.7931 8.22999 15.8451 7.29712C16.5926 6.99893 17.121 6.26965 17.121 5.41442C17.121 5.03925 17.0191 4.68789 16.8415 4.38648L17.13 4.38665C17.9808 4.38713 18.6673 5.07495 18.6673 5.92368C18.6673 6.76031 17.9892 7.43872 17.1176 7.46088L16.7929 7.46914L16.6685 7.76911C16.247 8.78503 15.2445 9.49776 14.0747 9.49776C13.2838 9.49776 12.5655 9.16901 12.0541 8.64197C11.812 8.38874 11.6082 8.08661 11.4399 7.74158ZM13.0562 3.38665C12.3341 3.38665 11.6999 3.76432 11.3406 4.33302L11.4483 4.08766C11.8986 3.0621 12.9068 2.34961 14.0747 2.34961C15.2444 2.34961 16.2469 3.06287 16.6685 4.07834L16.7212 4.20536C16.3516 3.7085 15.7599 3.38665 15.0932 3.38665H13.0562ZM13.0469 5.41442C13.0469 5.41314 13.0471 5.41269 13.0473 5.4122C13.0477 5.41135 13.0485 5.40988 13.0501 5.40831C13.0517 5.40674 13.0531 5.4059 13.054 5.40554C13.0542 5.40545 13.0544 5.40538 13.0546 5.40531C13.055 5.40522 13.0555 5.40517 13.0562 5.40517C13.0575 5.40517 13.0579 5.40533 13.0584 5.40554C13.0593 5.4059 13.0607 5.40674 13.0623 5.40831C13.0639 5.40988 13.0647 5.41135 13.0651 5.4122C13.0653 5.41269 13.0655 5.41314 13.0655 5.41442C13.0655 5.415 13.0654 5.41541 13.0654 5.41573C13.0653 5.41612 13.0652 5.41638 13.0651 5.41665C13.0647 5.41749 13.0639 5.41897 13.0623 5.42054C13.0607 5.4221 13.0593 5.42295 13.0584 5.42331C13.0579 5.42352 13.0575 5.42368 13.0562 5.42368C13.0556 5.42368 13.0551 5.42364 13.0548 5.42358C13.0545 5.42351 13.0542 5.42341 13.054 5.42331C13.0531 5.42295 13.0517 5.4221 13.0501 5.42054C13.0485 5.41897 13.0477 5.41749 13.0473 5.41665C13.0471 5.41616 13.0469 5.41571 13.0469 5.41442ZM15.1025 5.41442C15.1025 5.41571 15.1023 5.41616 15.1021 5.41665C15.1018 5.41749 15.1009 5.41897 15.0994 5.42054C15.0978 5.4221 15.0963 5.42295 15.0955 5.42331C15.095 5.42352 15.0945 5.42368 15.0932 5.42368C15.092 5.42368 15.0915 5.42352 15.091 5.42331C15.0902 5.42295 15.0887 5.4221 15.0871 5.42054C15.0856 5.41897 15.0847 5.41749 15.0844 5.41665C15.0843 5.41643 15.0842 5.41621 15.0841 5.41592C15.084 5.41557 15.084 5.41512 15.084 5.41442C15.084 5.41314 15.0842 5.41269 15.0844 5.4122C15.0845 5.41178 15.0848 5.41121 15.0853 5.41054C15.0857 5.40987 15.0863 5.4091 15.0871 5.40831C15.0887 5.40674 15.0902 5.4059 15.091 5.40554C15.0915 5.40533 15.092 5.40517 15.0932 5.40517C15.0939 5.40517 15.0943 5.4052 15.0946 5.40527C15.095 5.40534 15.0952 5.40543 15.0955 5.40554C15.0963 5.4059 15.0978 5.40674 15.0994 5.40831C15.1009 5.40988 15.1018 5.41135 15.1021 5.4122C15.1023 5.41269 15.1025 5.41314 15.1025 5.41442Z" fill="#8E9192" stroke="#8E9192"/> +</svg> diff --git a/assets/svg/ordinal.svg b/assets/svg/ordinal.svg new file mode 100644 index 000000000..7ac863a84 --- /dev/null +++ b/assets/svg/ordinal.svg @@ -0,0 +1,12 @@ +<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> +<g id="ordinal"> +<path id="Rectangle 43 (Stroke)" fill-rule="evenodd" clip-rule="evenodd" d="M4.99935 3.33366C4.07887 3.33366 3.33268 4.07985 3.33268 5.00033V15.0003C3.33268 15.9208 4.07887 16.667 4.99935 16.667H14.9993C15.9198 16.667 16.666 15.9208 16.666 15.0003V5.00033C16.666 4.07985 15.9198 3.33366 14.9993 3.33366H4.99935ZM1.66602 5.00033C1.66602 3.15938 3.1584 1.66699 4.99935 1.66699H14.9993C16.8403 1.66699 18.3327 3.15938 18.3327 5.00033V15.0003C18.3327 16.8413 16.8403 18.3337 14.9993 18.3337H4.99935C3.1584 18.3337 1.66602 16.8413 1.66602 15.0003V5.00033Z" fill="#8E9192"/> +<circle id="Ellipse 76" cx="6.25" cy="6.25" r="1.25" fill="#8E9192"/> +<g id="Vector"> +<path d="M6.89025 11.0396L5.11402 13.704C4.74482 14.2578 5.14181 14.9996 5.80739 14.9996H9.35986C10.0254 14.9996 10.4224 14.2578 10.0532 13.704L8.277 11.0396C7.94715 10.5449 7.2201 10.5449 6.89025 11.0396Z" fill="#8E9192"/> +<path d="M10.5883 8.15695L7.76997 13.7936C7.49292 14.3476 7.89584 14.9996 8.51532 14.9996H14.1519C14.7714 14.9996 15.1743 14.3476 14.8973 13.7936L12.079 8.15694C11.7719 7.54274 10.8954 7.54274 10.5883 8.15695Z" fill="#8E9192"/> +<path d="M6.89025 11.0396L5.11402 13.704C4.74482 14.2578 5.14181 14.9996 5.80739 14.9996H9.35986C10.0254 14.9996 10.4224 14.2578 10.0532 13.704L8.277 11.0396C7.94715 10.5449 7.2201 10.5449 6.89025 11.0396Z" stroke="#8E9192"/> +<path d="M10.5883 8.15695L7.76997 13.7936C7.49292 14.3476 7.89584 14.9996 8.51532 14.9996H14.1519C14.7714 14.9996 15.1743 14.3476 14.8973 13.7936L12.079 8.15694C11.7719 7.54274 10.8954 7.54274 10.5883 8.15695Z" stroke="#8E9192"/> +</g> +</g> +</svg> diff --git a/assets/svg/send.svg b/assets/svg/send.svg new file mode 100644 index 000000000..61fe2a206 --- /dev/null +++ b/assets/svg/send.svg @@ -0,0 +1,5 @@ +<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg"> +<g id="send"> +<path id="Vector" d="M9.16537 4.9832C9.16537 5.1919 9.04104 5.3807 8.84889 5.46272L1.55925 8.60772C1.49302 8.63625 1.4232 8.65001 1.35397 8.65001C1.21334 8.65001 1.07532 8.59295 0.974398 8.48615C0.824086 8.32692 0.789503 8.09076 0.887266 7.89478L2.08827 5.49311L6.03562 4.99794L2.08801 4.50221L0.887005 2.10053C0.789292 1.90488 0.823936 1.66862 0.974235 1.50949C1.12505 1.35026 1.35837 1.30161 1.55908 1.38797L8.86373 4.51851C9.05612 4.60004 9.16537 4.78917 9.16537 4.9832Z" fill="white"/> +</g> +</svg> diff --git a/crypto_plugins/flutter_libepiccash b/crypto_plugins/flutter_libepiccash index cd12741de..f677dec0b 160000 --- a/crypto_plugins/flutter_libepiccash +++ b/crypto_plugins/flutter_libepiccash @@ -1 +1 @@ -Subproject commit cd12741de19e4faef39a23b7d543a2452524990a +Subproject commit f677dec0b34d3f9fe8fce2bc8ff5c508c3f3bb9a diff --git a/crypto_plugins/flutter_liblelantus b/crypto_plugins/flutter_liblelantus index ec3cf5e8e..9cd241b5e 160000 --- a/crypto_plugins/flutter_liblelantus +++ b/crypto_plugins/flutter_liblelantus @@ -1 +1 @@ -Subproject commit ec3cf5e8e1b90e006188aa8c323d4cd52dbfa9b9 +Subproject commit 9cd241b5ea142e21c01dd7639b42603281c43287 diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index 13f19022e..e48952185 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit 13f19022e8d929b215a65f775b0dda7a5881608c +Subproject commit e48952185556a10f182184fd572bcb04365f5831 diff --git a/dockerfile.linux b/dockerfile.linux deleted file mode 100644 index 4a3867008..000000000 --- a/dockerfile.linux +++ /dev/null @@ -1,17 +0,0 @@ -FROM ubuntu:20.04 as base -COPY . /stack_wallet -WORKDIR /stack_wallet/scripts/linux -RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y git=1:2.25.1-1ubuntu3.6 make=4.2.1-1.2 curl=7.68.0-1ubuntu2.14 cargo=0.62.0ubuntu0libgit2-0ubuntu0.20.04.1 \ - file=1:5.38-4 ca-certificates=20211016ubuntu0.20.04.1 cmake=3.16.3-1ubuntu1.20.04.1 cmake-data=3.16.3-1ubuntu1.20.04.1 g++=4:9.3.0-1ubuntu2 libgmp-dev=2:6.2.0+dfsg-4ubuntu0.1 libssl-dev=1.1.1f-1ubuntu2.16 \ - libclang-dev=1:10.0-50~exp1 unzip=6.0-25ubuntu1.1 python3=3.8.2-0ubuntu2 pkg-config=0.29.1-0ubuntu4 libglib2.0-dev=2.64.6-1~ubuntu20.04.4 libgcrypt20-dev=1.8.5-5ubuntu1.1 gettext-base=0.19.8.1-10build1 \ - libgirepository1.0-dev=1.64.1-1~ubuntu20.04.1 valac=0.48.6-0ubuntu1 xsltproc=1.1.34-4ubuntu0.20.04.1 docbook-xsl=1.79.1+dfsg-2 python3-pip=20.0.2-5ubuntu1.6 ninja-build=1.10.0-1build1 clang=1:10.0-50~exp1 \ - libgtk-3-dev=3.24.20-0ubuntu1.1 libunbound-dev=1.9.4-2ubuntu1.4 libzmq3-dev=4.3.2-2ubuntu1 libtool=2.4.6-14 autoconf=2.69-11.1 automake=1:1.16.1-4ubuntu6 bison=2:3.5.1+dfsg-1 \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* \ - && pip3 install --upgrade meson==0.64.1 markdown==3.4.1 markupsafe==2.1.1 jinja2==3.1.2 pygments==2.13.0 toml==0.10.2 typogrify==2.0.7 tomli==2.0.1 && cd .. && ./prebuild.sh && cd linux && ./build_all.sh -WORKDIR / -RUN git clone https://github.com/flutter/flutter.git -b 3.3.4 -ENV PATH "$PATH:/flutter/bin" -WORKDIR /stack_wallet -RUN flutter pub get Linux && flutter build linux -ENTRYPOINT ["/bin/bash"] diff --git a/lib/db/hive/db.dart b/lib/db/hive/db.dart index 931a1cee4..2fb82c806 100644 --- a/lib/db/hive/db.dart +++ b/lib/db/hive/db.dart @@ -177,6 +177,9 @@ class DB { } Future<Box<dynamic>> getTxCacheBox({required Coin coin}) async { + if (_txCacheBoxes[coin]?.isOpen != true) { + _txCacheBoxes.remove(coin); + } return _txCacheBoxes[coin] ??= await Hive.openBox<dynamic>(_boxNameTxCache(coin: coin)); } @@ -186,6 +189,9 @@ class DB { } Future<Box<dynamic>> getAnonymitySetCacheBox({required Coin coin}) async { + if (_setCacheBoxes[coin]?.isOpen != true) { + _setCacheBoxes.remove(coin); + } return _setCacheBoxes[coin] ??= await Hive.openBox<dynamic>(_boxNameSetCache(coin: coin)); } @@ -195,6 +201,9 @@ class DB { } Future<Box<dynamic>> getUsedSerialsCacheBox({required Coin coin}) async { + if (_usedSerialsCacheBoxes[coin]?.isOpen != true) { + _usedSerialsCacheBoxes.remove(coin); + } return _usedSerialsCacheBoxes[coin] ??= await Hive.openBox<dynamic>(_boxNameUsedSerialsCache(coin: coin)); } @@ -265,8 +274,12 @@ class DB { {required dynamic key, required String boxName}) async => await mutex.protect(() async => await Hive.box<T>(boxName).delete(key)); - Future<void> deleteAll<T>({required String boxName}) async => - await mutex.protect(() async => await Hive.box<T>(boxName).clear()); + Future<void> deleteAll<T>({required String boxName}) async { + await mutex.protect(() async { + final box = await Hive.openBox<T>(boxName); + await box.clear(); + }); + } Future<void> deleteBoxFromDisk({required String boxName}) async => await mutex.protect(() async => await Hive.deleteBoxFromDisk(boxName)); diff --git a/lib/db/isar/main_db.dart b/lib/db/isar/main_db.dart index bb6ea5e8d..9465de12b 100644 --- a/lib/db/isar/main_db.dart +++ b/lib/db/isar/main_db.dart @@ -15,6 +15,7 @@ import 'package:stackwallet/exceptions/main_db/main_db_exception.dart'; import 'package:stackwallet/models/isar/models/block_explorer.dart'; import 'package:stackwallet/models/isar/models/contact_entry.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; +import 'package:stackwallet/models/isar/ordinal.dart'; import 'package:stackwallet/models/isar/stack_theme.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -54,6 +55,8 @@ class MainDB { TransactionBlockExplorerSchema, StackThemeSchema, ContactEntrySchema, + OrdinalSchema, + LelantusCoinSchema, ], directory: (await StackFileSystem.applicationIsarDirectory()).path, // inspector: kDebugMode, @@ -246,7 +249,8 @@ class MainDB { await isar.utxos.putAll(utxos); }); - Future<void> updateUTXOs(String walletId, List<UTXO> utxos) async { + Future<bool> updateUTXOs(String walletId, List<UTXO> utxos) async { + bool newUTXO = false; await isar.writeTxn(() async { final set = utxos.toSet(); for (final utxo in utxos) { @@ -268,12 +272,16 @@ class MainDB { blockHash: utxo.blockHash, ), ); + } else { + newUTXO = true; } } await isar.utxos.where().walletIdEqualTo(walletId).deleteAll(); await isar.utxos.putAll(set.toList()); }); + + return newUTXO; } Stream<UTXO?> watchUTXO({ @@ -370,6 +378,8 @@ class MainDB { final transactionCount = await getTransactions(walletId).count(); final addressCount = await getAddresses(walletId).count(); final utxoCount = await getUTXOs(walletId).count(); + final lelantusCoinCount = + await isar.lelantusCoins.where().walletIdEqualTo(walletId).count(); await isar.writeTxn(() async { const paginateLimit = 50; @@ -403,6 +413,18 @@ class MainDB { .findAll(); await isar.utxos.deleteAll(utxoIds); } + + // lelantusCoins + for (int i = 0; i < lelantusCoinCount; i += paginateLimit) { + final lelantusCoinIds = await isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .offset(i) + .limit(paginateLimit) + .idProperty() + .findAll(); + await isar.lelantusCoins.deleteAll(lelantusCoinIds); + } }); } @@ -497,4 +519,15 @@ class MainDB { isar.writeTxn(() async { await isar.ethContracts.putAll(contracts); }); + + // ========== Lelantus ======================================================= + + Future<int?> getHighestUsedMintIndex({required String walletId}) async { + return await isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .sortByMintIndexDesc() + .mintIndexProperty() + .findFirst(); + } } diff --git a/lib/dto/ethereum/eth_token_tx_extra_dto.dart b/lib/dto/ethereum/eth_token_tx_extra_dto.dart index 144194914..aab41bb76 100644 --- a/lib/dto/ethereum/eth_token_tx_extra_dto.dart +++ b/lib/dto/ethereum/eth_token_tx_extra_dto.dart @@ -46,7 +46,7 @@ class EthTokenTxExtraDTO { ), gas: _amountFromJsonNum(map['gas']), gasPrice: _amountFromJsonNum(map['gasPrice']), - nonce: map['nonce'] as int, + nonce: map['nonce'] as int?, input: map['input'] as String, gasCost: _amountFromJsonNum(map['gasCost']), gasUsed: _amountFromJsonNum(map['gasUsed']), @@ -63,7 +63,7 @@ class EthTokenTxExtraDTO { final Amount gas; final Amount gasPrice; final String input; - final int nonce; + final int? nonce; final Amount gasCost; final Amount gasUsed; diff --git a/lib/dto/ethereum/eth_tx_dto.dart b/lib/dto/ethereum/eth_tx_dto.dart index 7289cda15..260675cf9 100644 --- a/lib/dto/ethereum/eth_tx_dto.dart +++ b/lib/dto/ethereum/eth_tx_dto.dart @@ -127,16 +127,16 @@ class EthTxDTO { map['timestamp'] = timestamp; map['from'] = from; map['to'] = to; - map['value'] = value; - map['gas'] = gas; - map['gasPrice'] = gasPrice; - map['maxFeePerGas'] = maxFeePerGas; - map['maxPriorityFeePerGas'] = maxPriorityFeePerGas; + map['value'] = value.toString(); + map['gas'] = gas.toString(); + map['gasPrice'] = gasPrice.toString(); + map['maxFeePerGas'] = maxFeePerGas.toString(); + map['maxPriorityFeePerGas'] = maxPriorityFeePerGas.toString(); map['isError'] = isError; map['hasToken'] = hasToken; map['compressedTx'] = compressedTx; - map['gasCost'] = gasCost; - map['gasUsed'] = gasUsed; + map['gasCost'] = gasCost.toString(); + map['gasUsed'] = gasUsed.toString(); return map; } diff --git a/lib/dto/ordinals/address_inscription_response.dart b/lib/dto/ordinals/address_inscription_response.dart new file mode 100644 index 000000000..240374284 --- /dev/null +++ b/lib/dto/ordinals/address_inscription_response.dart @@ -0,0 +1,39 @@ +import 'package:stackwallet/dto/ordinals/litescribe_response.dart'; +import 'package:stackwallet/dto/ordinals/inscription_data.dart'; + +class AddressInscriptionResponse extends LitescribeResponse<AddressInscriptionResponse> { + final int status; + final String message; + final AddressInscriptionResult result; + + AddressInscriptionResponse({ + required this.status, + required this.message, + required this.result, + }); + + factory AddressInscriptionResponse.fromJson(Map<String, dynamic> json) { + return AddressInscriptionResponse( + status: json['status'] as int, + message: json['message'] as String, + result: AddressInscriptionResult.fromJson(json['result'] as Map<String, dynamic>), + ); + } +} + +class AddressInscriptionResult { + final List<InscriptionData> list; + final int total; + + AddressInscriptionResult({ + required this.list, + required this.total, + }); + + factory AddressInscriptionResult.fromJson(Map<String, dynamic> json) { + return AddressInscriptionResult( + list: (json['list'] as List).map((item) => InscriptionData.fromJson(item as Map<String, dynamic>)).toList(), + total: json['total'] as int, + ); + } +} diff --git a/lib/dto/ordinals/inscription_data.dart b/lib/dto/ordinals/inscription_data.dart new file mode 100644 index 000000000..2f12bd670 --- /dev/null +++ b/lib/dto/ordinals/inscription_data.dart @@ -0,0 +1,73 @@ +// inscription data from litescribe /address/inscriptions endpoint +class InscriptionData { + final String inscriptionId; + final int inscriptionNumber; + final String address; + final String preview; + final String content; + final int contentLength; + final String contentType; + final String contentBody; + final int timestamp; + final String genesisTransaction; + final String location; + final String output; + final int outputValue; + final int offset; + + InscriptionData({ + required this.inscriptionId, + required this.inscriptionNumber, + required this.address, + required this.preview, + required this.content, + required this.contentLength, + required this.contentType, + required this.contentBody, + required this.timestamp, + required this.genesisTransaction, + required this.location, + required this.output, + required this.outputValue, + required this.offset, + }); + + factory InscriptionData.fromJson(Map<String, dynamic> json) { + return InscriptionData( + inscriptionId: json['inscriptionId'] as String, + inscriptionNumber: json['inscriptionNumber'] as int, + address: json['address'] as String, + preview: json['preview'] as String, + content: json['content'] as String, + contentLength: json['contentLength'] as int, + contentType: json['contentType'] as String, + contentBody: json['contentBody'] as String, + timestamp: json['timestamp'] as int, + genesisTransaction: json['genesisTransaction'] as String, + location: json['location'] as String, + output: json['output'] as String, + outputValue: json['outputValue'] as int, + offset: json['offset'] as int, + ); + } + + @override + String toString() { + return 'InscriptionData {' + ' inscriptionId: $inscriptionId,' + ' inscriptionNumber: $inscriptionNumber,' + ' address: $address,' + ' preview: $preview,' + ' content: $content,' + ' contentLength: $contentLength,' + ' contentType: $contentType,' + ' contentBody: $contentBody,' + ' timestamp: $timestamp,' + ' genesisTransaction: $genesisTransaction,' + ' location: $location,' + ' output: $output,' + ' outputValue: $outputValue,' + ' offset: $offset' + ' }'; + } +} diff --git a/lib/dto/ordinals/litescribe_response.dart b/lib/dto/ordinals/litescribe_response.dart new file mode 100644 index 000000000..bebd5ce10 --- /dev/null +++ b/lib/dto/ordinals/litescribe_response.dart @@ -0,0 +1,6 @@ +class LitescribeResponse<T> { + final T? data; + final String? error; + + LitescribeResponse({this.data, this.error}); +} diff --git a/lib/electrumx_rpc/cached_electrumx.dart b/lib/electrumx_rpc/cached_electrumx.dart index 67f170bb4..8366e259f 100644 --- a/lib/electrumx_rpc/cached_electrumx.dart +++ b/lib/electrumx_rpc/cached_electrumx.dart @@ -164,14 +164,16 @@ class CachedElectrumX { final _list = box.get("serials") as List?; - List<String> cachedSerials = - _list == null ? [] : List<String>.from(_list); + Set<String> cachedSerials = + _list == null ? {} : List<String>.from(_list).toSet(); - final startNumber = cachedSerials.length; + final startNumber = + cachedSerials.length - 10; // 10 being some arbitrary buffer - final serials = - await electrumXClient.getUsedCoinSerials(startNumber: startNumber); - List<String> newSerials = []; + final serials = await electrumXClient.getUsedCoinSerials( + startNumber: startNumber, + ); + Set<String> newSerials = {}; for (final element in (serials["serials"] as List)) { if (!isHexadecimal(element as String)) { @@ -182,12 +184,14 @@ class CachedElectrumX { } cachedSerials.addAll(newSerials); + final resultingList = cachedSerials.toList(); + await box.put( "serials", - cachedSerials, + resultingList, ); - return cachedSerials; + return resultingList; } catch (e, s) { Logging.instance.log( "Failed to process CachedElectrumX.getTransaction(): $e\n$s", diff --git a/lib/electrumx_rpc/electrumx.dart b/lib/electrumx_rpc/electrumx.dart index e0a3118eb..c9417116d 100644 --- a/lib/electrumx_rpc/electrumx.dart +++ b/lib/electrumx_rpc/electrumx.dart @@ -69,13 +69,18 @@ class ElectrumX { List<ElectrumXNode>? failovers; int currentFailoverIndex = -1; - ElectrumX( - {required String host, - required int port, - required bool useSSL, - required Prefs prefs, - required List<ElectrumXNode> failovers, - JsonRPC? client}) { + final Duration connectionTimeoutForSpecialCaseJsonRPCClients; + + ElectrumX({ + required String host, + required int port, + required bool useSSL, + required Prefs prefs, + required List<ElectrumXNode> failovers, + JsonRPC? client, + this.connectionTimeoutForSpecialCaseJsonRPCClients = + const Duration(seconds: 60), + }) { _prefs = prefs; _host = host; _port = port; @@ -108,9 +113,9 @@ class ElectrumX { Future<dynamic> request({ required String command, List<dynamic> args = const [], - Duration connectionTimeout = const Duration(seconds: 60), String? requestID, int retries = 2, + Duration requestTimeout = const Duration(seconds: 60), }) async { if (!(await _allow())) { throw WifiOnlyException(); @@ -121,26 +126,31 @@ class ElectrumX { host: host, port: port, useSSL: useSSL, - connectionTimeout: connectionTimeout, + connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients, ); } else { _rpcClient = JsonRPC( host: failovers![currentFailoverIndex].address, port: failovers![currentFailoverIndex].port, useSSL: failovers![currentFailoverIndex].useSSL, - connectionTimeout: connectionTimeout, + connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients, ); } try { final requestId = requestID ?? const Uuid().v1(); final jsonArgs = json.encode(args); - final jsonRequestString = - '{"jsonrpc": "2.0", "id": "$requestId","method": "$command","params": $jsonArgs}'; + final jsonRequestString = '{"jsonrpc": "2.0", ' + '"id": "$requestId",' + '"method": "$command",' + '"params": $jsonArgs}'; // Logging.instance.log("ElectrumX jsonRequestString: $jsonRequestString"); - final response = await _rpcClient!.request(jsonRequestString); + final response = await _rpcClient!.request( + jsonRequestString, + requestTimeout, + ); if (response.exception != null) { throw response.exception!; @@ -159,8 +169,8 @@ class ElectrumX { throw Exception( "JSONRPC response\n" " command: $command\n" - " args: $args\n" - " error: ${response.data}", + " error: ${response.data}" + " args: $args\n", ); } @@ -174,7 +184,7 @@ class ElectrumX { return request( command: command, args: args, - connectionTimeout: connectionTimeout, + requestTimeout: requestTimeout, requestID: requestID, retries: retries - 1, ); @@ -187,7 +197,7 @@ class ElectrumX { return request( command: command, args: args, - connectionTimeout: connectionTimeout, + requestTimeout: requestTimeout, requestID: requestID, ); } else { @@ -204,7 +214,7 @@ class ElectrumX { Future<List<Map<String, dynamic>>> batchRequest({ required String command, required Map<String, List<dynamic>> args, - Duration connectionTimeout = const Duration(seconds: 60), + Duration requestTimeout = const Duration(seconds: 60), int retries = 2, }) async { if (!(await _allow())) { @@ -216,14 +226,14 @@ class ElectrumX { host: host, port: port, useSSL: useSSL, - connectionTimeout: connectionTimeout, + connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients, ); } else { _rpcClient = JsonRPC( host: failovers![currentFailoverIndex].address, port: failovers![currentFailoverIndex].port, useSSL: failovers![currentFailoverIndex].useSSL, - connectionTimeout: connectionTimeout, + connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients, ); } @@ -246,7 +256,8 @@ class ElectrumX { // Logging.instance.log("batch request: $request"); // send batch request - final jsonRpcResponse = (await _rpcClient!.request(request)); + final jsonRpcResponse = + (await _rpcClient!.request(request, requestTimeout)); if (jsonRpcResponse.exception != null) { throw jsonRpcResponse.exception!; @@ -281,7 +292,7 @@ class ElectrumX { return batchRequest( command: command, args: args, - connectionTimeout: connectionTimeout, + requestTimeout: requestTimeout, retries: retries - 1, ); } else { @@ -293,7 +304,7 @@ class ElectrumX { return batchRequest( command: command, args: args, - connectionTimeout: connectionTimeout, + requestTimeout: requestTimeout, ); } else { currentFailoverIndex = -1; @@ -310,7 +321,7 @@ class ElectrumX { final response = await request( requestID: requestID, command: 'server.ping', - connectionTimeout: const Duration(seconds: 2), + requestTimeout: const Duration(seconds: 2), retries: retryCount, ).timeout(const Duration(seconds: 2)) as Map<String, dynamic>; return response.keys.contains("result") && response["result"] == null; @@ -442,7 +453,7 @@ class ElectrumX { final response = await request( requestID: requestID, command: 'blockchain.scripthash.get_history', - connectionTimeout: const Duration(minutes: 5), + requestTimeout: const Duration(minutes: 5), args: [ scripthash, ], @@ -666,11 +677,13 @@ class ElectrumX { }) async { try { final response = await request( - requestID: requestID, - command: 'lelantus.getusedcoinserials', - args: [ - "$startNumber", - ]); + requestID: requestID, + command: 'lelantus.getusedcoinserials', + args: [ + "$startNumber", + ], + requestTimeout: const Duration(minutes: 2), + ); return Map<String, dynamic>.from(response["result"] as Map); } catch (e) { Logging.instance.log(e, level: LogLevel.Error); diff --git a/lib/electrumx_rpc/rpc.dart b/lib/electrumx_rpc/rpc.dart index 0c3834ae8..2dc0d3a71 100644 --- a/lib/electrumx_rpc/rpc.dart +++ b/lib/electrumx_rpc/rpc.dart @@ -79,7 +79,6 @@ class JsonRPC { // TODO different timeout length? req.initiateTimeout( - const Duration(seconds: 10), onTimedOut: () { _requestQueue.remove(req); }, @@ -88,7 +87,10 @@ class JsonRPC { }); } - Future<JsonRPCResponse> request(String jsonRpcRequest) async { + Future<JsonRPCResponse> request( + String jsonRpcRequest, + Duration requestTimeout, + ) async { await _requestMutex.protect(() async { if (_socket == null) { Logging.instance.log( @@ -101,6 +103,7 @@ class JsonRPC { final req = _JsonRPCRequest( jsonRequest: jsonRpcRequest, + requestTimeout: requestTimeout, completer: Completer<JsonRPCResponse>(), ); @@ -243,9 +246,14 @@ class _JsonRPCRequest { final String jsonRequest; final Completer<JsonRPCResponse> completer; + final Duration requestTimeout; final List<int> _responseData = []; - _JsonRPCRequest({required this.jsonRequest, required this.completer}); + _JsonRPCRequest({ + required this.jsonRequest, + required this.completer, + required this.requestTimeout, + }); void appendDataAndCheckIfComplete(List<int> data) { _responseData.addAll(data); @@ -263,11 +271,10 @@ class _JsonRPCRequest { } } - void initiateTimeout( - Duration timeout, { + void initiateTimeout({ VoidCallback? onTimedOut, }) { - Future<void>.delayed(timeout).then((_) { + Future<void>.delayed(requestTimeout).then((_) { if (!isComplete) { try { throw Exception("_JsonRPCRequest timed out: $jsonRequest"); diff --git a/lib/models/isar/models/firo_specific/lelantus_coin.dart b/lib/models/isar/models/firo_specific/lelantus_coin.dart new file mode 100644 index 000000000..ca4c11919 --- /dev/null +++ b/lib/models/isar/models/firo_specific/lelantus_coin.dart @@ -0,0 +1,81 @@ +import 'package:isar/isar.dart'; + +part 'lelantus_coin.g.dart'; + +@collection +class LelantusCoin { + Id id = Isar.autoIncrement; + + @Index() + final String walletId; + + final String txid; + + final String value; // can't use BigInt in isar :shrug: + + @Index( + unique: true, + replace: false, + composite: [ + CompositeIndex("walletId"), + ], + ) + final int mintIndex; + + final int anonymitySetId; + + final bool isUsed; + + final bool isJMint; + + final String? otherData; + + LelantusCoin({ + required this.walletId, + required this.txid, + required this.value, + required this.mintIndex, + required this.anonymitySetId, + required this.isUsed, + required this.isJMint, + required this.otherData, + }); + + LelantusCoin copyWith({ + String? walletId, + String? publicCoin, + String? txid, + String? value, + int? mintIndex, + int? anonymitySetId, + bool? isUsed, + bool? isJMint, + String? otherData, + }) { + return LelantusCoin( + walletId: walletId ?? this.walletId, + txid: txid ?? this.txid, + value: value ?? this.value, + mintIndex: mintIndex ?? this.mintIndex, + anonymitySetId: anonymitySetId ?? this.anonymitySetId, + isUsed: isUsed ?? this.isUsed, + isJMint: isJMint ?? this.isJMint, + otherData: otherData ?? this.otherData, + ); + } + + @override + String toString() { + return 'LelantusCoin{' + 'id: $id, ' + 'walletId: $walletId, ' + 'txid: $txid, ' + 'value: $value, ' + 'mintIndex: $mintIndex, ' + 'anonymitySetId: $anonymitySetId, ' + 'otherData: $otherData, ' + 'isJMint: $isJMint, ' + 'isUsed: $isUsed' + '}'; + } +} diff --git a/lib/models/isar/models/firo_specific/lelantus_coin.g.dart b/lib/models/isar/models/firo_specific/lelantus_coin.g.dart new file mode 100644 index 000000000..4b9214889 --- /dev/null +++ b/lib/models/isar/models/firo_specific/lelantus_coin.g.dart @@ -0,0 +1,1629 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'lelantus_coin.dart'; + +// ************************************************************************** +// IsarCollectionGenerator +// ************************************************************************** + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters + +extension GetLelantusCoinCollection on Isar { + IsarCollection<LelantusCoin> get lelantusCoins => this.collection(); +} + +const LelantusCoinSchema = CollectionSchema( + name: r'LelantusCoin', + id: -6795633185033299066, + properties: { + r'anonymitySetId': PropertySchema( + id: 0, + name: r'anonymitySetId', + type: IsarType.long, + ), + r'isJMint': PropertySchema( + id: 1, + name: r'isJMint', + type: IsarType.bool, + ), + r'isUsed': PropertySchema( + id: 2, + name: r'isUsed', + type: IsarType.bool, + ), + r'mintIndex': PropertySchema( + id: 3, + name: r'mintIndex', + type: IsarType.long, + ), + r'otherData': PropertySchema( + id: 4, + name: r'otherData', + type: IsarType.string, + ), + r'txid': PropertySchema( + id: 5, + name: r'txid', + type: IsarType.string, + ), + r'value': PropertySchema( + id: 6, + name: r'value', + type: IsarType.string, + ), + r'walletId': PropertySchema( + id: 7, + name: r'walletId', + type: IsarType.string, + ) + }, + estimateSize: _lelantusCoinEstimateSize, + serialize: _lelantusCoinSerialize, + deserialize: _lelantusCoinDeserialize, + deserializeProp: _lelantusCoinDeserializeProp, + idName: r'id', + indexes: { + r'walletId': IndexSchema( + id: -1783113319798776304, + name: r'walletId', + unique: false, + replace: false, + properties: [ + IndexPropertySchema( + name: r'walletId', + type: IndexType.hash, + caseSensitive: true, + ) + ], + ), + r'mintIndex_walletId': IndexSchema( + id: -9147309777276196770, + name: r'mintIndex_walletId', + unique: true, + replace: false, + properties: [ + IndexPropertySchema( + name: r'mintIndex', + type: IndexType.value, + caseSensitive: false, + ), + IndexPropertySchema( + name: r'walletId', + type: IndexType.hash, + caseSensitive: true, + ) + ], + ) + }, + links: {}, + embeddedSchemas: {}, + getId: _lelantusCoinGetId, + getLinks: _lelantusCoinGetLinks, + attach: _lelantusCoinAttach, + version: '3.0.5', +); + +int _lelantusCoinEstimateSize( + LelantusCoin object, + List<int> offsets, + Map<Type, List<int>> allOffsets, +) { + var bytesCount = offsets.last; + { + final value = object.otherData; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + bytesCount += 3 + object.txid.length * 3; + bytesCount += 3 + object.value.length * 3; + bytesCount += 3 + object.walletId.length * 3; + return bytesCount; +} + +void _lelantusCoinSerialize( + LelantusCoin object, + IsarWriter writer, + List<int> offsets, + Map<Type, List<int>> allOffsets, +) { + writer.writeLong(offsets[0], object.anonymitySetId); + writer.writeBool(offsets[1], object.isJMint); + writer.writeBool(offsets[2], object.isUsed); + writer.writeLong(offsets[3], object.mintIndex); + writer.writeString(offsets[4], object.otherData); + writer.writeString(offsets[5], object.txid); + writer.writeString(offsets[6], object.value); + writer.writeString(offsets[7], object.walletId); +} + +LelantusCoin _lelantusCoinDeserialize( + Id id, + IsarReader reader, + List<int> offsets, + Map<Type, List<int>> allOffsets, +) { + final object = LelantusCoin( + anonymitySetId: reader.readLong(offsets[0]), + isJMint: reader.readBool(offsets[1]), + isUsed: reader.readBool(offsets[2]), + mintIndex: reader.readLong(offsets[3]), + otherData: reader.readStringOrNull(offsets[4]), + txid: reader.readString(offsets[5]), + value: reader.readString(offsets[6]), + walletId: reader.readString(offsets[7]), + ); + object.id = id; + return object; +} + +P _lelantusCoinDeserializeProp<P>( + IsarReader reader, + int propertyId, + int offset, + Map<Type, List<int>> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readLong(offset)) as P; + case 1: + return (reader.readBool(offset)) as P; + case 2: + return (reader.readBool(offset)) as P; + case 3: + return (reader.readLong(offset)) as P; + case 4: + return (reader.readStringOrNull(offset)) as P; + case 5: + return (reader.readString(offset)) as P; + case 6: + return (reader.readString(offset)) as P; + case 7: + return (reader.readString(offset)) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +Id _lelantusCoinGetId(LelantusCoin object) { + return object.id; +} + +List<IsarLinkBase<dynamic>> _lelantusCoinGetLinks(LelantusCoin object) { + return []; +} + +void _lelantusCoinAttach( + IsarCollection<dynamic> col, Id id, LelantusCoin object) { + object.id = id; +} + +extension LelantusCoinByIndex on IsarCollection<LelantusCoin> { + Future<LelantusCoin?> getByMintIndexWalletId(int mintIndex, String walletId) { + return getByIndex(r'mintIndex_walletId', [mintIndex, walletId]); + } + + LelantusCoin? getByMintIndexWalletIdSync(int mintIndex, String walletId) { + return getByIndexSync(r'mintIndex_walletId', [mintIndex, walletId]); + } + + Future<bool> deleteByMintIndexWalletId(int mintIndex, String walletId) { + return deleteByIndex(r'mintIndex_walletId', [mintIndex, walletId]); + } + + bool deleteByMintIndexWalletIdSync(int mintIndex, String walletId) { + return deleteByIndexSync(r'mintIndex_walletId', [mintIndex, walletId]); + } + + Future<List<LelantusCoin?>> getAllByMintIndexWalletId( + List<int> mintIndexValues, List<String> walletIdValues) { + final len = mintIndexValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = <List<dynamic>>[]; + for (var i = 0; i < len; i++) { + values.add([mintIndexValues[i], walletIdValues[i]]); + } + + return getAllByIndex(r'mintIndex_walletId', values); + } + + List<LelantusCoin?> getAllByMintIndexWalletIdSync( + List<int> mintIndexValues, List<String> walletIdValues) { + final len = mintIndexValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = <List<dynamic>>[]; + for (var i = 0; i < len; i++) { + values.add([mintIndexValues[i], walletIdValues[i]]); + } + + return getAllByIndexSync(r'mintIndex_walletId', values); + } + + Future<int> deleteAllByMintIndexWalletId( + List<int> mintIndexValues, List<String> walletIdValues) { + final len = mintIndexValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = <List<dynamic>>[]; + for (var i = 0; i < len; i++) { + values.add([mintIndexValues[i], walletIdValues[i]]); + } + + return deleteAllByIndex(r'mintIndex_walletId', values); + } + + int deleteAllByMintIndexWalletIdSync( + List<int> mintIndexValues, List<String> walletIdValues) { + final len = mintIndexValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = <List<dynamic>>[]; + for (var i = 0; i < len; i++) { + values.add([mintIndexValues[i], walletIdValues[i]]); + } + + return deleteAllByIndexSync(r'mintIndex_walletId', values); + } + + Future<Id> putByMintIndexWalletId(LelantusCoin object) { + return putByIndex(r'mintIndex_walletId', object); + } + + Id putByMintIndexWalletIdSync(LelantusCoin object, {bool saveLinks = true}) { + return putByIndexSync(r'mintIndex_walletId', object, saveLinks: saveLinks); + } + + Future<List<Id>> putAllByMintIndexWalletId(List<LelantusCoin> objects) { + return putAllByIndex(r'mintIndex_walletId', objects); + } + + List<Id> putAllByMintIndexWalletIdSync(List<LelantusCoin> objects, + {bool saveLinks = true}) { + return putAllByIndexSync(r'mintIndex_walletId', objects, + saveLinks: saveLinks); + } +} + +extension LelantusCoinQueryWhereSort + on QueryBuilder<LelantusCoin, LelantusCoin, QWhere> { + QueryBuilder<LelantusCoin, LelantusCoin, QAfterWhere> anyId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension LelantusCoinQueryWhere + on QueryBuilder<LelantusCoin, LelantusCoin, QWhereClause> { + QueryBuilder<LelantusCoin, LelantusCoin, QAfterWhereClause> idEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: id, + upper: id, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterWhereClause> idNotEqualTo( + Id id) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ) + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ); + } else { + return query + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ) + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ); + } + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterWhereClause> idGreaterThan( + Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: include), + ); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterWhereClause> idLessThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: include), + ); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterWhereClause> idBetween( + Id lowerId, + Id upperId, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterWhereClause> walletIdEqualTo( + String walletId) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'walletId', + value: [walletId], + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterWhereClause> + walletIdNotEqualTo(String walletId) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [], + upper: [walletId], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [walletId], + includeLower: false, + upper: [], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [walletId], + includeLower: false, + upper: [], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [], + upper: [walletId], + includeUpper: false, + )); + } + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterWhereClause> + mintIndexEqualToAnyWalletId(int mintIndex) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'mintIndex_walletId', + value: [mintIndex], + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterWhereClause> + mintIndexNotEqualToAnyWalletId(int mintIndex) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [], + upper: [mintIndex], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + includeLower: false, + upper: [], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + includeLower: false, + upper: [], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [], + upper: [mintIndex], + includeUpper: false, + )); + } + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterWhereClause> + mintIndexGreaterThanAnyWalletId( + int mintIndex, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + includeLower: include, + upper: [], + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterWhereClause> + mintIndexLessThanAnyWalletId( + int mintIndex, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [], + upper: [mintIndex], + includeUpper: include, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterWhereClause> + mintIndexBetweenAnyWalletId( + int lowerMintIndex, + int upperMintIndex, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [lowerMintIndex], + includeLower: includeLower, + upper: [upperMintIndex], + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterWhereClause> + mintIndexWalletIdEqualTo(int mintIndex, String walletId) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'mintIndex_walletId', + value: [mintIndex, walletId], + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterWhereClause> + mintIndexEqualToWalletIdNotEqualTo(int mintIndex, String walletId) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + upper: [mintIndex, walletId], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex, walletId], + includeLower: false, + upper: [mintIndex], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex, walletId], + includeLower: false, + upper: [mintIndex], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + upper: [mintIndex, walletId], + includeUpper: false, + )); + } + }); + } +} + +extension LelantusCoinQueryFilter + on QueryBuilder<LelantusCoin, LelantusCoin, QFilterCondition> { + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + anonymitySetIdEqualTo(int value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'anonymitySetId', + value: value, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + anonymitySetIdGreaterThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'anonymitySetId', + value: value, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + anonymitySetIdLessThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'anonymitySetId', + value: value, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + anonymitySetIdBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'anonymitySetId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> idEqualTo( + Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'id', + value: value, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> idGreaterThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> idLessThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> idBetween( + Id lower, + Id upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + isJMintEqualTo(bool value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'isJMint', + value: value, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> isUsedEqualTo( + bool value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'isUsed', + value: value, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + mintIndexEqualTo(int value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'mintIndex', + value: value, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + mintIndexGreaterThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'mintIndex', + value: value, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + mintIndexLessThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'mintIndex', + value: value, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + mintIndexBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'mintIndex', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + otherDataIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'otherData', + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + otherDataIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'otherData', + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + otherDataEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + otherDataGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + otherDataLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + otherDataBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'otherData', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + otherDataStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + otherDataEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + otherDataContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + otherDataMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'otherData', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + otherDataIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'otherData', + value: '', + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + otherDataIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'otherData', + value: '', + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> txidEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + txidGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> txidLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> txidBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'txid', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + txidStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> txidEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> txidContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> txidMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'txid', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + txidIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'txid', + value: '', + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + txidIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'txid', + value: '', + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> valueEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + valueGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> valueLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> valueBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'value', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + valueStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> valueEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> valueContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> valueMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'value', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + valueIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'value', + value: '', + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + valueIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'value', + value: '', + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + walletIdEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + walletIdGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + walletIdLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + walletIdBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'walletId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + walletIdStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + walletIdEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + walletIdContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + walletIdMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'walletId', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + walletIdIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'walletId', + value: '', + )); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterFilterCondition> + walletIdIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'walletId', + value: '', + )); + }); + } +} + +extension LelantusCoinQueryObject + on QueryBuilder<LelantusCoin, LelantusCoin, QFilterCondition> {} + +extension LelantusCoinQueryLinks + on QueryBuilder<LelantusCoin, LelantusCoin, QFilterCondition> {} + +extension LelantusCoinQuerySortBy + on QueryBuilder<LelantusCoin, LelantusCoin, QSortBy> { + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> + sortByAnonymitySetId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'anonymitySetId', Sort.asc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> + sortByAnonymitySetIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'anonymitySetId', Sort.desc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> sortByIsJMint() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isJMint', Sort.asc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> sortByIsJMintDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isJMint', Sort.desc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> sortByIsUsed() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isUsed', Sort.asc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> sortByIsUsedDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isUsed', Sort.desc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> sortByMintIndex() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mintIndex', Sort.asc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> sortByMintIndexDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mintIndex', Sort.desc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> sortByOtherData() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'otherData', Sort.asc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> sortByOtherDataDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'otherData', Sort.desc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> sortByTxid() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.asc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> sortByTxidDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.desc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> sortByValue() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.asc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> sortByValueDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.desc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> sortByWalletId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.asc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> sortByWalletIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.desc); + }); + } +} + +extension LelantusCoinQuerySortThenBy + on QueryBuilder<LelantusCoin, LelantusCoin, QSortThenBy> { + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> + thenByAnonymitySetId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'anonymitySetId', Sort.asc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> + thenByAnonymitySetIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'anonymitySetId', Sort.desc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> thenById() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.asc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> thenByIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.desc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> thenByIsJMint() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isJMint', Sort.asc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> thenByIsJMintDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isJMint', Sort.desc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> thenByIsUsed() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isUsed', Sort.asc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> thenByIsUsedDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isUsed', Sort.desc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> thenByMintIndex() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mintIndex', Sort.asc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> thenByMintIndexDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mintIndex', Sort.desc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> thenByOtherData() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'otherData', Sort.asc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> thenByOtherDataDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'otherData', Sort.desc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> thenByTxid() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.asc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> thenByTxidDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.desc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> thenByValue() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.asc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> thenByValueDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.desc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> thenByWalletId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.asc); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QAfterSortBy> thenByWalletIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.desc); + }); + } +} + +extension LelantusCoinQueryWhereDistinct + on QueryBuilder<LelantusCoin, LelantusCoin, QDistinct> { + QueryBuilder<LelantusCoin, LelantusCoin, QDistinct> + distinctByAnonymitySetId() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'anonymitySetId'); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QDistinct> distinctByIsJMint() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'isJMint'); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QDistinct> distinctByIsUsed() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'isUsed'); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QDistinct> distinctByMintIndex() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'mintIndex'); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QDistinct> distinctByOtherData( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'otherData', caseSensitive: caseSensitive); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QDistinct> distinctByTxid( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'txid', caseSensitive: caseSensitive); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QDistinct> distinctByValue( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'value', caseSensitive: caseSensitive); + }); + } + + QueryBuilder<LelantusCoin, LelantusCoin, QDistinct> distinctByWalletId( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'walletId', caseSensitive: caseSensitive); + }); + } +} + +extension LelantusCoinQueryProperty + on QueryBuilder<LelantusCoin, LelantusCoin, QQueryProperty> { + QueryBuilder<LelantusCoin, int, QQueryOperations> idProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'id'); + }); + } + + QueryBuilder<LelantusCoin, int, QQueryOperations> anonymitySetIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'anonymitySetId'); + }); + } + + QueryBuilder<LelantusCoin, bool, QQueryOperations> isJMintProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'isJMint'); + }); + } + + QueryBuilder<LelantusCoin, bool, QQueryOperations> isUsedProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'isUsed'); + }); + } + + QueryBuilder<LelantusCoin, int, QQueryOperations> mintIndexProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'mintIndex'); + }); + } + + QueryBuilder<LelantusCoin, String?, QQueryOperations> otherDataProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'otherData'); + }); + } + + QueryBuilder<LelantusCoin, String, QQueryOperations> txidProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'txid'); + }); + } + + QueryBuilder<LelantusCoin, String, QQueryOperations> valueProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'value'); + }); + } + + QueryBuilder<LelantusCoin, String, QQueryOperations> walletIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'walletId'); + }); + } +} diff --git a/lib/models/isar/models/isar_models.dart b/lib/models/isar/models/isar_models.dart index ce7652a46..9de91fc84 100644 --- a/lib/models/isar/models/isar_models.dart +++ b/lib/models/isar/models/isar_models.dart @@ -15,5 +15,6 @@ export 'blockchain_data/output.dart'; export 'blockchain_data/transaction.dart'; export 'blockchain_data/utxo.dart'; export 'ethereum/eth_contract.dart'; +export 'firo_specific/lelantus_coin.dart'; export 'log.dart'; export 'transaction_note.dart'; diff --git a/lib/models/isar/ordinal.dart b/lib/models/isar/ordinal.dart new file mode 100644 index 000000000..06ba52ef5 --- /dev/null +++ b/lib/models/isar/ordinal.dart @@ -0,0 +1,89 @@ +import 'package:isar/isar.dart'; +import 'package:stackwallet/db/isar/main_db.dart'; +import 'package:stackwallet/dto/ordinals/inscription_data.dart'; +import 'package:stackwallet/models/isar/models/isar_models.dart'; + +part 'ordinal.g.dart'; + +@collection +class Ordinal { + Id id = Isar.autoIncrement; + + final String walletId; + + @Index(unique: true, replace: true, composite: [ + CompositeIndex("utxoTXID"), + CompositeIndex("utxoVOUT"), + ]) + final String inscriptionId; + + final int inscriptionNumber; + + final String content; + + // following two are used to look up the UTXO object in isar combined w/ walletId + final String utxoTXID; + final int utxoVOUT; + + Ordinal({ + required this.walletId, + required this.inscriptionId, + required this.inscriptionNumber, + required this.content, + required this.utxoTXID, + required this.utxoVOUT, + }); + + factory Ordinal.fromInscriptionData(InscriptionData data, String walletId) { + return Ordinal( + walletId: walletId, + inscriptionId: data.inscriptionId, + inscriptionNumber: data.inscriptionNumber, + content: data.content, + utxoTXID: data.output.split(':')[ + 0], // "output": "062f32e21aa04246b8873b5d9a929576addd0339881e1ea478b406795d6b6c47:0" + utxoVOUT: int.parse(data.output.split(':')[1]), + ); + } + + Ordinal copyWith({ + String? walletId, + String? inscriptionId, + int? inscriptionNumber, + String? content, + String? utxoTXID, + int? utxoVOUT, + }) { + return Ordinal( + walletId: walletId ?? this.walletId, + inscriptionId: inscriptionId ?? this.inscriptionId, + inscriptionNumber: inscriptionNumber ?? this.inscriptionNumber, + content: content ?? this.content, + utxoTXID: utxoTXID ?? this.utxoTXID, + utxoVOUT: utxoVOUT ?? this.utxoVOUT, + ); + } + + UTXO? getUTXO(MainDB db) { + return db.isar.utxos + .where() + .walletIdEqualTo(walletId) + .filter() + .txidEqualTo(utxoTXID) + .and() + .voutEqualTo(utxoVOUT) + .findFirstSync(); + } + + @override + String toString() { + return 'Ordinal {' + ' walletId: $walletId,' + ' inscriptionId: $inscriptionId,' + ' inscriptionNumber: $inscriptionNumber,' + ' content: $content,' + ' utxoTXID: $utxoTXID,' + ' utxoVOUT: $utxoVOUT' + ' }'; + } +} diff --git a/lib/models/isar/ordinal.g.dart b/lib/models/isar/ordinal.g.dart new file mode 100644 index 000000000..89c967cb0 --- /dev/null +++ b/lib/models/isar/ordinal.g.dart @@ -0,0 +1,1489 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'ordinal.dart'; + +// ************************************************************************** +// IsarCollectionGenerator +// ************************************************************************** + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters + +extension GetOrdinalCollection on Isar { + IsarCollection<Ordinal> get ordinals => this.collection(); +} + +const OrdinalSchema = CollectionSchema( + name: r'Ordinal', + id: -7772149326141951436, + properties: { + r'content': PropertySchema( + id: 0, + name: r'content', + type: IsarType.string, + ), + r'inscriptionId': PropertySchema( + id: 1, + name: r'inscriptionId', + type: IsarType.string, + ), + r'inscriptionNumber': PropertySchema( + id: 2, + name: r'inscriptionNumber', + type: IsarType.long, + ), + r'utxoTXID': PropertySchema( + id: 3, + name: r'utxoTXID', + type: IsarType.string, + ), + r'utxoVOUT': PropertySchema( + id: 4, + name: r'utxoVOUT', + type: IsarType.long, + ), + r'walletId': PropertySchema( + id: 5, + name: r'walletId', + type: IsarType.string, + ) + }, + estimateSize: _ordinalEstimateSize, + serialize: _ordinalSerialize, + deserialize: _ordinalDeserialize, + deserializeProp: _ordinalDeserializeProp, + idName: r'id', + indexes: { + r'inscriptionId_utxoTXID_utxoVOUT': IndexSchema( + id: 2138008085066605381, + name: r'inscriptionId_utxoTXID_utxoVOUT', + unique: true, + replace: true, + properties: [ + IndexPropertySchema( + name: r'inscriptionId', + type: IndexType.hash, + caseSensitive: true, + ), + IndexPropertySchema( + name: r'utxoTXID', + type: IndexType.hash, + caseSensitive: true, + ), + IndexPropertySchema( + name: r'utxoVOUT', + type: IndexType.value, + caseSensitive: false, + ) + ], + ) + }, + links: {}, + embeddedSchemas: {}, + getId: _ordinalGetId, + getLinks: _ordinalGetLinks, + attach: _ordinalAttach, + version: '3.0.5', +); + +int _ordinalEstimateSize( + Ordinal object, + List<int> offsets, + Map<Type, List<int>> allOffsets, +) { + var bytesCount = offsets.last; + bytesCount += 3 + object.content.length * 3; + bytesCount += 3 + object.inscriptionId.length * 3; + bytesCount += 3 + object.utxoTXID.length * 3; + bytesCount += 3 + object.walletId.length * 3; + return bytesCount; +} + +void _ordinalSerialize( + Ordinal object, + IsarWriter writer, + List<int> offsets, + Map<Type, List<int>> allOffsets, +) { + writer.writeString(offsets[0], object.content); + writer.writeString(offsets[1], object.inscriptionId); + writer.writeLong(offsets[2], object.inscriptionNumber); + writer.writeString(offsets[3], object.utxoTXID); + writer.writeLong(offsets[4], object.utxoVOUT); + writer.writeString(offsets[5], object.walletId); +} + +Ordinal _ordinalDeserialize( + Id id, + IsarReader reader, + List<int> offsets, + Map<Type, List<int>> allOffsets, +) { + final object = Ordinal( + content: reader.readString(offsets[0]), + inscriptionId: reader.readString(offsets[1]), + inscriptionNumber: reader.readLong(offsets[2]), + utxoTXID: reader.readString(offsets[3]), + utxoVOUT: reader.readLong(offsets[4]), + walletId: reader.readString(offsets[5]), + ); + object.id = id; + return object; +} + +P _ordinalDeserializeProp<P>( + IsarReader reader, + int propertyId, + int offset, + Map<Type, List<int>> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readString(offset)) as P; + case 1: + return (reader.readString(offset)) as P; + case 2: + return (reader.readLong(offset)) as P; + case 3: + return (reader.readString(offset)) as P; + case 4: + return (reader.readLong(offset)) as P; + case 5: + return (reader.readString(offset)) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +Id _ordinalGetId(Ordinal object) { + return object.id; +} + +List<IsarLinkBase<dynamic>> _ordinalGetLinks(Ordinal object) { + return []; +} + +void _ordinalAttach(IsarCollection<dynamic> col, Id id, Ordinal object) { + object.id = id; +} + +extension OrdinalByIndex on IsarCollection<Ordinal> { + Future<Ordinal?> getByInscriptionIdUtxoTXIDUtxoVOUT( + String inscriptionId, String utxoTXID, int utxoVOUT) { + return getByIndex(r'inscriptionId_utxoTXID_utxoVOUT', + [inscriptionId, utxoTXID, utxoVOUT]); + } + + Ordinal? getByInscriptionIdUtxoTXIDUtxoVOUTSync( + String inscriptionId, String utxoTXID, int utxoVOUT) { + return getByIndexSync(r'inscriptionId_utxoTXID_utxoVOUT', + [inscriptionId, utxoTXID, utxoVOUT]); + } + + Future<bool> deleteByInscriptionIdUtxoTXIDUtxoVOUT( + String inscriptionId, String utxoTXID, int utxoVOUT) { + return deleteByIndex(r'inscriptionId_utxoTXID_utxoVOUT', + [inscriptionId, utxoTXID, utxoVOUT]); + } + + bool deleteByInscriptionIdUtxoTXIDUtxoVOUTSync( + String inscriptionId, String utxoTXID, int utxoVOUT) { + return deleteByIndexSync(r'inscriptionId_utxoTXID_utxoVOUT', + [inscriptionId, utxoTXID, utxoVOUT]); + } + + Future<List<Ordinal?>> getAllByInscriptionIdUtxoTXIDUtxoVOUT( + List<String> inscriptionIdValues, + List<String> utxoTXIDValues, + List<int> utxoVOUTValues) { + final len = inscriptionIdValues.length; + assert(utxoTXIDValues.length == len && utxoVOUTValues.length == len, + 'All index values must have the same length'); + final values = <List<dynamic>>[]; + for (var i = 0; i < len; i++) { + values + .add([inscriptionIdValues[i], utxoTXIDValues[i], utxoVOUTValues[i]]); + } + + return getAllByIndex(r'inscriptionId_utxoTXID_utxoVOUT', values); + } + + List<Ordinal?> getAllByInscriptionIdUtxoTXIDUtxoVOUTSync( + List<String> inscriptionIdValues, + List<String> utxoTXIDValues, + List<int> utxoVOUTValues) { + final len = inscriptionIdValues.length; + assert(utxoTXIDValues.length == len && utxoVOUTValues.length == len, + 'All index values must have the same length'); + final values = <List<dynamic>>[]; + for (var i = 0; i < len; i++) { + values + .add([inscriptionIdValues[i], utxoTXIDValues[i], utxoVOUTValues[i]]); + } + + return getAllByIndexSync(r'inscriptionId_utxoTXID_utxoVOUT', values); + } + + Future<int> deleteAllByInscriptionIdUtxoTXIDUtxoVOUT( + List<String> inscriptionIdValues, + List<String> utxoTXIDValues, + List<int> utxoVOUTValues) { + final len = inscriptionIdValues.length; + assert(utxoTXIDValues.length == len && utxoVOUTValues.length == len, + 'All index values must have the same length'); + final values = <List<dynamic>>[]; + for (var i = 0; i < len; i++) { + values + .add([inscriptionIdValues[i], utxoTXIDValues[i], utxoVOUTValues[i]]); + } + + return deleteAllByIndex(r'inscriptionId_utxoTXID_utxoVOUT', values); + } + + int deleteAllByInscriptionIdUtxoTXIDUtxoVOUTSync( + List<String> inscriptionIdValues, + List<String> utxoTXIDValues, + List<int> utxoVOUTValues) { + final len = inscriptionIdValues.length; + assert(utxoTXIDValues.length == len && utxoVOUTValues.length == len, + 'All index values must have the same length'); + final values = <List<dynamic>>[]; + for (var i = 0; i < len; i++) { + values + .add([inscriptionIdValues[i], utxoTXIDValues[i], utxoVOUTValues[i]]); + } + + return deleteAllByIndexSync(r'inscriptionId_utxoTXID_utxoVOUT', values); + } + + Future<Id> putByInscriptionIdUtxoTXIDUtxoVOUT(Ordinal object) { + return putByIndex(r'inscriptionId_utxoTXID_utxoVOUT', object); + } + + Id putByInscriptionIdUtxoTXIDUtxoVOUTSync(Ordinal object, + {bool saveLinks = true}) { + return putByIndexSync(r'inscriptionId_utxoTXID_utxoVOUT', object, + saveLinks: saveLinks); + } + + Future<List<Id>> putAllByInscriptionIdUtxoTXIDUtxoVOUT( + List<Ordinal> objects) { + return putAllByIndex(r'inscriptionId_utxoTXID_utxoVOUT', objects); + } + + List<Id> putAllByInscriptionIdUtxoTXIDUtxoVOUTSync(List<Ordinal> objects, + {bool saveLinks = true}) { + return putAllByIndexSync(r'inscriptionId_utxoTXID_utxoVOUT', objects, + saveLinks: saveLinks); + } +} + +extension OrdinalQueryWhereSort on QueryBuilder<Ordinal, Ordinal, QWhere> { + QueryBuilder<Ordinal, Ordinal, QAfterWhere> anyId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension OrdinalQueryWhere on QueryBuilder<Ordinal, Ordinal, QWhereClause> { + QueryBuilder<Ordinal, Ordinal, QAfterWhereClause> idEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: id, + upper: id, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterWhereClause> idNotEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ) + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ); + } else { + return query + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ) + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ); + } + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterWhereClause> idGreaterThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: include), + ); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterWhereClause> idLessThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: include), + ); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterWhereClause> idBetween( + Id lowerId, + Id upperId, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterWhereClause> + inscriptionIdEqualToAnyUtxoTXIDUtxoVOUT(String inscriptionId) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + value: [inscriptionId], + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterWhereClause> + inscriptionIdNotEqualToAnyUtxoTXIDUtxoVOUT(String inscriptionId) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [], + upper: [inscriptionId], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId], + includeLower: false, + upper: [], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId], + includeLower: false, + upper: [], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [], + upper: [inscriptionId], + includeUpper: false, + )); + } + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterWhereClause> + inscriptionIdUtxoTXIDEqualToAnyUtxoVOUT( + String inscriptionId, String utxoTXID) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + value: [inscriptionId, utxoTXID], + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterWhereClause> + inscriptionIdEqualToUtxoTXIDNotEqualToAnyUtxoVOUT( + String inscriptionId, String utxoTXID) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId], + upper: [inscriptionId, utxoTXID], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId, utxoTXID], + includeLower: false, + upper: [inscriptionId], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId, utxoTXID], + includeLower: false, + upper: [inscriptionId], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId], + upper: [inscriptionId, utxoTXID], + includeUpper: false, + )); + } + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterWhereClause> + inscriptionIdUtxoTXIDUtxoVOUTEqualTo( + String inscriptionId, String utxoTXID, int utxoVOUT) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + value: [inscriptionId, utxoTXID, utxoVOUT], + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterWhereClause> + inscriptionIdUtxoTXIDEqualToUtxoVOUTNotEqualTo( + String inscriptionId, String utxoTXID, int utxoVOUT) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId, utxoTXID], + upper: [inscriptionId, utxoTXID, utxoVOUT], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId, utxoTXID, utxoVOUT], + includeLower: false, + upper: [inscriptionId, utxoTXID], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId, utxoTXID, utxoVOUT], + includeLower: false, + upper: [inscriptionId, utxoTXID], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId, utxoTXID], + upper: [inscriptionId, utxoTXID, utxoVOUT], + includeUpper: false, + )); + } + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterWhereClause> + inscriptionIdUtxoTXIDEqualToUtxoVOUTGreaterThan( + String inscriptionId, + String utxoTXID, + int utxoVOUT, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId, utxoTXID, utxoVOUT], + includeLower: include, + upper: [inscriptionId, utxoTXID], + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterWhereClause> + inscriptionIdUtxoTXIDEqualToUtxoVOUTLessThan( + String inscriptionId, + String utxoTXID, + int utxoVOUT, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId, utxoTXID], + upper: [inscriptionId, utxoTXID, utxoVOUT], + includeUpper: include, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterWhereClause> + inscriptionIdUtxoTXIDEqualToUtxoVOUTBetween( + String inscriptionId, + String utxoTXID, + int lowerUtxoVOUT, + int upperUtxoVOUT, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId, utxoTXID, lowerUtxoVOUT], + includeLower: includeLower, + upper: [inscriptionId, utxoTXID, upperUtxoVOUT], + includeUpper: includeUpper, + )); + }); + } +} + +extension OrdinalQueryFilter + on QueryBuilder<Ordinal, Ordinal, QFilterCondition> { + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> contentEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'content', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> contentGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'content', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> contentLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'content', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> contentBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'content', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> contentStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'content', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> contentEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'content', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> contentContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'content', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> contentMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'content', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> contentIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'content', + value: '', + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> contentIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'content', + value: '', + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> idEqualTo(Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'id', + value: value, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> idGreaterThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> idLessThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> idBetween( + Id lower, + Id upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> inscriptionIdEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'inscriptionId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> + inscriptionIdGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'inscriptionId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> inscriptionIdLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'inscriptionId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> inscriptionIdBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'inscriptionId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> inscriptionIdStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'inscriptionId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> inscriptionIdEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'inscriptionId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> inscriptionIdContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'inscriptionId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> inscriptionIdMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'inscriptionId', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> inscriptionIdIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'inscriptionId', + value: '', + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> + inscriptionIdIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'inscriptionId', + value: '', + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> + inscriptionNumberEqualTo(int value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'inscriptionNumber', + value: value, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> + inscriptionNumberGreaterThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'inscriptionNumber', + value: value, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> + inscriptionNumberLessThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'inscriptionNumber', + value: value, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> + inscriptionNumberBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'inscriptionNumber', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> utxoTXIDEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'utxoTXID', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> utxoTXIDGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'utxoTXID', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> utxoTXIDLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'utxoTXID', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> utxoTXIDBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'utxoTXID', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> utxoTXIDStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'utxoTXID', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> utxoTXIDEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'utxoTXID', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> utxoTXIDContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'utxoTXID', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> utxoTXIDMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'utxoTXID', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> utxoTXIDIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'utxoTXID', + value: '', + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> utxoTXIDIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'utxoTXID', + value: '', + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> utxoVOUTEqualTo( + int value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'utxoVOUT', + value: value, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> utxoVOUTGreaterThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'utxoVOUT', + value: value, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> utxoVOUTLessThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'utxoVOUT', + value: value, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> utxoVOUTBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'utxoVOUT', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> walletIdEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> walletIdGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> walletIdLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> walletIdBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'walletId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> walletIdStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> walletIdEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> walletIdContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> walletIdMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'walletId', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> walletIdIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'walletId', + value: '', + )); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterFilterCondition> walletIdIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'walletId', + value: '', + )); + }); + } +} + +extension OrdinalQueryObject + on QueryBuilder<Ordinal, Ordinal, QFilterCondition> {} + +extension OrdinalQueryLinks + on QueryBuilder<Ordinal, Ordinal, QFilterCondition> {} + +extension OrdinalQuerySortBy on QueryBuilder<Ordinal, Ordinal, QSortBy> { + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> sortByContent() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'content', Sort.asc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> sortByContentDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'content', Sort.desc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> sortByInscriptionId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'inscriptionId', Sort.asc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> sortByInscriptionIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'inscriptionId', Sort.desc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> sortByInscriptionNumber() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'inscriptionNumber', Sort.asc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> sortByInscriptionNumberDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'inscriptionNumber', Sort.desc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> sortByUtxoTXID() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'utxoTXID', Sort.asc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> sortByUtxoTXIDDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'utxoTXID', Sort.desc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> sortByUtxoVOUT() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'utxoVOUT', Sort.asc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> sortByUtxoVOUTDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'utxoVOUT', Sort.desc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> sortByWalletId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.asc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> sortByWalletIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.desc); + }); + } +} + +extension OrdinalQuerySortThenBy + on QueryBuilder<Ordinal, Ordinal, QSortThenBy> { + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> thenByContent() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'content', Sort.asc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> thenByContentDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'content', Sort.desc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> thenById() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.asc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> thenByIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.desc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> thenByInscriptionId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'inscriptionId', Sort.asc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> thenByInscriptionIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'inscriptionId', Sort.desc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> thenByInscriptionNumber() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'inscriptionNumber', Sort.asc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> thenByInscriptionNumberDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'inscriptionNumber', Sort.desc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> thenByUtxoTXID() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'utxoTXID', Sort.asc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> thenByUtxoTXIDDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'utxoTXID', Sort.desc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> thenByUtxoVOUT() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'utxoVOUT', Sort.asc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> thenByUtxoVOUTDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'utxoVOUT', Sort.desc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> thenByWalletId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.asc); + }); + } + + QueryBuilder<Ordinal, Ordinal, QAfterSortBy> thenByWalletIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.desc); + }); + } +} + +extension OrdinalQueryWhereDistinct + on QueryBuilder<Ordinal, Ordinal, QDistinct> { + QueryBuilder<Ordinal, Ordinal, QDistinct> distinctByContent( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'content', caseSensitive: caseSensitive); + }); + } + + QueryBuilder<Ordinal, Ordinal, QDistinct> distinctByInscriptionId( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'inscriptionId', + caseSensitive: caseSensitive); + }); + } + + QueryBuilder<Ordinal, Ordinal, QDistinct> distinctByInscriptionNumber() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'inscriptionNumber'); + }); + } + + QueryBuilder<Ordinal, Ordinal, QDistinct> distinctByUtxoTXID( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'utxoTXID', caseSensitive: caseSensitive); + }); + } + + QueryBuilder<Ordinal, Ordinal, QDistinct> distinctByUtxoVOUT() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'utxoVOUT'); + }); + } + + QueryBuilder<Ordinal, Ordinal, QDistinct> distinctByWalletId( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'walletId', caseSensitive: caseSensitive); + }); + } +} + +extension OrdinalQueryProperty + on QueryBuilder<Ordinal, Ordinal, QQueryProperty> { + QueryBuilder<Ordinal, int, QQueryOperations> idProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'id'); + }); + } + + QueryBuilder<Ordinal, String, QQueryOperations> contentProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'content'); + }); + } + + QueryBuilder<Ordinal, String, QQueryOperations> inscriptionIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'inscriptionId'); + }); + } + + QueryBuilder<Ordinal, int, QQueryOperations> inscriptionNumberProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'inscriptionNumber'); + }); + } + + QueryBuilder<Ordinal, String, QQueryOperations> utxoTXIDProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'utxoTXID'); + }); + } + + QueryBuilder<Ordinal, int, QQueryOperations> utxoVOUTProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'utxoVOUT'); + }); + } + + QueryBuilder<Ordinal, String, QQueryOperations> walletIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'walletId'); + }); + } +} diff --git a/lib/models/isar/stack_theme.dart b/lib/models/isar/stack_theme.dart index 557090584..01e32bb2d 100644 --- a/lib/models/isar/stack_theme.dart +++ b/lib/models/isar/stack_theme.dart @@ -45,7 +45,7 @@ class StackTheme { case "dark": return Brightness.dark; default: - // just return light instead of a possible crash causing error + // just return light instead of a possible crash causing error return Brightness.light; } } @@ -131,8 +131,8 @@ class StackTheme { @ignore Color get accentColorBlue => _accentColorBlue ??= Color( - accentColorBlueInt, - ); + accentColorBlueInt, + ); @ignore Color? _accentColorBlue; late final int accentColorBlueInt; @@ -141,8 +141,8 @@ class StackTheme { @ignore Color get accentColorGreen => _accentColorGreen ??= Color( - accentColorGreenInt, - ); + accentColorGreenInt, + ); @ignore Color? _accentColorGreen; late final int accentColorGreenInt; @@ -151,8 +151,8 @@ class StackTheme { @ignore Color get accentColorYellow => _accentColorYellow ??= Color( - accentColorYellowInt, - ); + accentColorYellowInt, + ); @ignore Color? _accentColorYellow; late final int accentColorYellowInt; @@ -161,8 +161,8 @@ class StackTheme { @ignore Color get accentColorRed => _accentColorRed ??= Color( - accentColorRedInt, - ); + accentColorRedInt, + ); @ignore Color? _accentColorRed; late final int accentColorRedInt; @@ -171,8 +171,8 @@ class StackTheme { @ignore Color get accentColorOrange => _accentColorOrange ??= Color( - accentColorOrangeInt, - ); + accentColorOrangeInt, + ); @ignore Color? _accentColorOrange; late final int accentColorOrangeInt; @@ -181,8 +181,8 @@ class StackTheme { @ignore Color get accentColorDark => _accentColorDark ??= Color( - accentColorDarkInt, - ); + accentColorDarkInt, + ); @ignore Color? _accentColorDark; late final int accentColorDarkInt; @@ -191,8 +191,8 @@ class StackTheme { @ignore Color get shadow => _shadow ??= Color( - shadowInt, - ); + shadowInt, + ); @ignore Color? _shadow; late final int shadowInt; @@ -201,8 +201,8 @@ class StackTheme { @ignore Color get textDark => _textDark ??= Color( - textDarkInt, - ); + textDarkInt, + ); @ignore Color? _textDark; late final int textDarkInt; @@ -211,8 +211,8 @@ class StackTheme { @ignore Color get textDark2 => _textDark2 ??= Color( - textDark2Int, - ); + textDark2Int, + ); @ignore Color? _textDark2; late final int textDark2Int; @@ -221,8 +221,8 @@ class StackTheme { @ignore Color get textDark3 => _textDark3 ??= Color( - textDark3Int, - ); + textDark3Int, + ); @ignore Color? _textDark3; late final int textDark3Int; @@ -231,8 +231,8 @@ class StackTheme { @ignore Color get textSubtitle1 => _textSubtitle1 ??= Color( - textSubtitle1Int, - ); + textSubtitle1Int, + ); @ignore Color? _textSubtitle1; late final int textSubtitle1Int; @@ -241,8 +241,8 @@ class StackTheme { @ignore Color get textSubtitle2 => _textSubtitle2 ??= Color( - textSubtitle2Int, - ); + textSubtitle2Int, + ); @ignore Color? _textSubtitle2; late final int textSubtitle2Int; @@ -251,8 +251,8 @@ class StackTheme { @ignore Color get textSubtitle3 => _textSubtitle3 ??= Color( - textSubtitle3Int, - ); + textSubtitle3Int, + ); @ignore Color? _textSubtitle3; late final int textSubtitle3Int; @@ -261,8 +261,8 @@ class StackTheme { @ignore Color get textSubtitle4 => _textSubtitle4 ??= Color( - textSubtitle4Int, - ); + textSubtitle4Int, + ); @ignore Color? _textSubtitle4; late final int textSubtitle4Int; @@ -271,8 +271,8 @@ class StackTheme { @ignore Color get textSubtitle5 => _textSubtitle5 ??= Color( - textSubtitle5Int, - ); + textSubtitle5Int, + ); @ignore Color? _textSubtitle5; late final int textSubtitle5Int; @@ -281,8 +281,8 @@ class StackTheme { @ignore Color get textSubtitle6 => _textSubtitle6 ??= Color( - textSubtitle6Int, - ); + textSubtitle6Int, + ); @ignore Color? _textSubtitle6; late final int textSubtitle6Int; @@ -291,8 +291,8 @@ class StackTheme { @ignore Color get textWhite => _textWhite ??= Color( - textWhiteInt, - ); + textWhiteInt, + ); @ignore Color? _textWhite; late final int textWhiteInt; @@ -301,8 +301,8 @@ class StackTheme { @ignore Color get textFavoriteCard => _textFavoriteCard ??= Color( - textFavoriteCardInt, - ); + textFavoriteCardInt, + ); @ignore Color? _textFavoriteCard; late final int textFavoriteCardInt; @@ -311,8 +311,8 @@ class StackTheme { @ignore Color get textError => _textError ??= Color( - textErrorInt, - ); + textErrorInt, + ); @ignore Color? _textError; late final int textErrorInt; @@ -321,8 +321,8 @@ class StackTheme { @ignore Color get textRestore => _textRestore ??= Color( - textRestoreInt, - ); + textRestoreInt, + ); @ignore Color? _textRestore; late final int textRestoreInt; @@ -331,8 +331,8 @@ class StackTheme { @ignore Color get buttonBackPrimary => _buttonBackPrimary ??= Color( - buttonBackPrimaryInt, - ); + buttonBackPrimaryInt, + ); @ignore Color? _buttonBackPrimary; late final int buttonBackPrimaryInt; @@ -341,8 +341,8 @@ class StackTheme { @ignore Color get buttonBackSecondary => _buttonBackSecondary ??= Color( - buttonBackSecondaryInt, - ); + buttonBackSecondaryInt, + ); @ignore Color? _buttonBackSecondary; late final int buttonBackSecondaryInt; @@ -351,8 +351,8 @@ class StackTheme { @ignore Color get buttonBackPrimaryDisabled => _buttonBackPrimaryDisabled ??= Color( - buttonBackPrimaryDisabledInt, - ); + buttonBackPrimaryDisabledInt, + ); @ignore Color? _buttonBackPrimaryDisabled; late final int buttonBackPrimaryDisabledInt; @@ -372,8 +372,8 @@ class StackTheme { @ignore Color get buttonBackBorder => _buttonBackBorder ??= Color( - buttonBackBorderInt, - ); + buttonBackBorderInt, + ); @ignore Color? _buttonBackBorder; late final int buttonBackBorderInt; @@ -382,8 +382,8 @@ class StackTheme { @ignore Color get buttonBackBorderDisabled => _buttonBackBorderDisabled ??= Color( - buttonBackBorderDisabledInt, - ); + buttonBackBorderDisabledInt, + ); @ignore Color? _buttonBackBorderDisabled; late final int buttonBackBorderDisabledInt; @@ -392,8 +392,8 @@ class StackTheme { @ignore Color get buttonBackBorderSecondary => _buttonBackBorderSecondary ??= Color( - buttonBackBorderSecondaryInt, - ); + buttonBackBorderSecondaryInt, + ); @ignore Color? _buttonBackBorderSecondary; late final int buttonBackBorderSecondaryInt; @@ -413,8 +413,8 @@ class StackTheme { @ignore Color get numberBackDefault => _numberBackDefault ??= Color( - numberBackDefaultInt, - ); + numberBackDefaultInt, + ); @ignore Color? _numberBackDefault; late final int numberBackDefaultInt; @@ -423,8 +423,8 @@ class StackTheme { @ignore Color get numpadBackDefault => _numpadBackDefault ??= Color( - numpadBackDefaultInt, - ); + numpadBackDefaultInt, + ); @ignore Color? _numpadBackDefault; late final int numpadBackDefaultInt; @@ -433,8 +433,8 @@ class StackTheme { @ignore Color get bottomNavBack => _bottomNavBack ??= Color( - bottomNavBackInt, - ); + bottomNavBackInt, + ); @ignore Color? _bottomNavBack; late final int bottomNavBackInt; @@ -443,8 +443,8 @@ class StackTheme { @ignore Color get buttonTextPrimary => _buttonTextPrimary ??= Color( - buttonTextPrimaryInt, - ); + buttonTextPrimaryInt, + ); @ignore Color? _buttonTextPrimary; late final int buttonTextPrimaryInt; @@ -453,8 +453,8 @@ class StackTheme { @ignore Color get buttonTextSecondary => _buttonTextSecondary ??= Color( - buttonTextSecondaryInt, - ); + buttonTextSecondaryInt, + ); @ignore Color? _buttonTextSecondary; late final int buttonTextSecondaryInt; @@ -463,8 +463,8 @@ class StackTheme { @ignore Color get buttonTextPrimaryDisabled => _buttonTextPrimaryDisabled ??= Color( - buttonTextPrimaryDisabledInt, - ); + buttonTextPrimaryDisabledInt, + ); @ignore Color? _buttonTextPrimaryDisabled; late final int buttonTextPrimaryDisabledInt; @@ -1517,117 +1517,117 @@ class StackTheme { ..version = version ..assetsV1 = version == 1 ? ThemeAssets.fromJson( - json: Map<String, dynamic>.from(json["assets"] as Map), - themeId: json["id"] as String, - ) + json: Map<String, dynamic>.from(json["assets"] as Map), + themeId: json["id"] as String, + ) : null ..assetsV2 = version == 2 ? ThemeAssetsV2.fromJson( - json: Map<String, dynamic>.from(json["assets"] as Map), - themeId: json["id"] as String, - ) + json: Map<String, dynamic>.from(json["assets"] as Map), + themeId: json["id"] as String, + ) : null ..assetsV3 = version >= 3 ? ThemeAssetsV3.fromJson( - json: Map<String, dynamic>.from(json["assets"] as Map), - themeId: json["id"] as String, - ) + json: Map<String, dynamic>.from(json["assets"] as Map), + themeId: json["id"] as String, + ) : null ..themeId = json["id"] as String ..name = json["name"] as String ..brightnessString = json["brightness"] as String ..backgroundInt = parseColor(json["colors"]["background"] as String) ..backgroundAppBarInt = - parseColor(json["colors"]["background_app_bar"] as String) + parseColor(json["colors"]["background_app_bar"] as String) ..gradientBackgroundString = json["colors"]["gradients"] != null ? jsonEncode(json["colors"]["gradients"]) : null ..standardBoxShadowString = - jsonEncode(json["colors"]["box_shadows"]["standard"] as Map) + jsonEncode(json["colors"]["box_shadows"]["standard"] as Map) ..homeViewButtonBarBoxShadowString = - json["colors"]["box_shadows"]["home_view_button_bar"] == null - ? null - : jsonEncode( - json["colors"]["box_shadows"]["home_view_button_bar"] as Map) + json["colors"]["box_shadows"]["home_view_button_bar"] == null + ? null + : jsonEncode( + json["colors"]["box_shadows"]["home_view_button_bar"] as Map) ..coinColorsJsonString = jsonEncode(json["colors"]['coin'] as Map) ..overlayInt = parseColor(json["colors"]["overlay"] as String) ..accentColorBlueInt = - parseColor(json["colors"]["accent_color_blue"] as String) + parseColor(json["colors"]["accent_color_blue"] as String) ..accentColorGreenInt = - parseColor(json["colors"]["accent_color_green"] as String) + parseColor(json["colors"]["accent_color_green"] as String) ..accentColorYellowInt = - parseColor(json["colors"]["accent_color_yellow"] as String) + parseColor(json["colors"]["accent_color_yellow"] as String) ..accentColorRedInt = - parseColor(json["colors"]["accent_color_red"] as String) + parseColor(json["colors"]["accent_color_red"] as String) ..accentColorOrangeInt = - parseColor(json["colors"]["accent_color_orange"] as String) + parseColor(json["colors"]["accent_color_orange"] as String) ..accentColorDarkInt = - parseColor(json["colors"]["accent_color_dark"] as String) + parseColor(json["colors"]["accent_color_dark"] as String) ..shadowInt = parseColor(json["colors"]["shadow"] as String) ..textDarkInt = parseColor(json["colors"]["text_dark_one"] as String) ..textDark2Int = parseColor(json["colors"]["text_dark_two"] as String) ..textDark3Int = parseColor(json["colors"]["text_dark_three"] as String) ..textWhiteInt = parseColor(json["colors"]["text_white"] as String) ..textFavoriteCardInt = - parseColor(json["colors"]["text_favorite"] as String) + parseColor(json["colors"]["text_favorite"] as String) ..textErrorInt = parseColor(json["colors"]["text_error"] as String) ..textRestoreInt = parseColor(json["colors"]["text_restore"] as String) ..buttonBackPrimaryInt = - parseColor(json["colors"]["button_back_primary"] as String) + parseColor(json["colors"]["button_back_primary"] as String) ..buttonBackSecondaryInt = - parseColor(json["colors"]["button_back_secondary"] as String) + parseColor(json["colors"]["button_back_secondary"] as String) ..buttonBackPrimaryDisabledInt = - parseColor(json["colors"]["button_back_primary_disabled"] as String) + parseColor(json["colors"]["button_back_primary_disabled"] as String) ..buttonBackSecondaryDisabledInt = - parseColor(json["colors"]["button_back_secondary_disabled"] as String) + parseColor(json["colors"]["button_back_secondary_disabled"] as String) ..buttonBackBorderInt = - parseColor(json["colors"]["button_back_border"] as String) + parseColor(json["colors"]["button_back_border"] as String) ..buttonBackBorderDisabledInt = - parseColor(json["colors"]["button_back_border_disabled"] as String) + parseColor(json["colors"]["button_back_border_disabled"] as String) ..buttonBackBorderSecondaryInt = - parseColor(json["colors"]["button_back_border_secondary"] as String) + parseColor(json["colors"]["button_back_border_secondary"] as String) ..buttonBackBorderSecondaryDisabledInt = parseColor( json["colors"]["button_back_border_secondary_disabled"] as String) ..numberBackDefaultInt = - parseColor(json["colors"]["number_back_default"] as String) + parseColor(json["colors"]["number_back_default"] as String) ..numpadBackDefaultInt = - parseColor(json["colors"]["numpad_back_default"] as String) + parseColor(json["colors"]["numpad_back_default"] as String) ..bottomNavBackInt = - parseColor(json["colors"]["bottom_nav_back"] as String) + parseColor(json["colors"]["bottom_nav_back"] as String) ..textSubtitle1Int = - parseColor(json["colors"]["text_subtitle_one"] as String) + parseColor(json["colors"]["text_subtitle_one"] as String) ..textSubtitle2Int = - parseColor(json["colors"]["text_subtitle_two"] as String) + parseColor(json["colors"]["text_subtitle_two"] as String) ..textSubtitle3Int = - parseColor(json["colors"]["text_subtitle_three"] as String) + parseColor(json["colors"]["text_subtitle_three"] as String) ..textSubtitle4Int = - parseColor(json["colors"]["text_subtitle_four"] as String) + parseColor(json["colors"]["text_subtitle_four"] as String) ..textSubtitle5Int = - parseColor(json["colors"]["text_subtitle_five"] as String) + parseColor(json["colors"]["text_subtitle_five"] as String) ..textSubtitle6Int = - parseColor(json["colors"]["text_subtitle_six"] as String) + parseColor(json["colors"]["text_subtitle_six"] as String) ..buttonTextPrimaryInt = - parseColor(json["colors"]["button_text_primary"] as String) + parseColor(json["colors"]["button_text_primary"] as String) ..buttonTextSecondaryInt = - parseColor(json["colors"]["button_text_secondary"] as String) + parseColor(json["colors"]["button_text_secondary"] as String) ..buttonTextPrimaryDisabledInt = - parseColor(json["colors"]["button_text_primary_disabled"] as String) + parseColor(json["colors"]["button_text_primary_disabled"] as String) ..buttonTextSecondaryDisabledInt = - parseColor(json["colors"]["button_text_secondary_disabled"] as String) + parseColor(json["colors"]["button_text_secondary_disabled"] as String) ..buttonTextBorderInt = - parseColor(json["colors"]["button_text_border"] as String) + parseColor(json["colors"]["button_text_border"] as String) ..buttonTextDisabledInt = - parseColor(json["colors"]["button_text_disabled"] as String) + parseColor(json["colors"]["button_text_disabled"] as String) ..buttonTextBorderlessInt = - parseColor(json["colors"]["button_text_borderless"] as String) + parseColor(json["colors"]["button_text_borderless"] as String) ..buttonTextBorderlessDisabledInt = parseColor( json["colors"]["button_text_borderless_disabled"] as String) ..numberTextDefaultInt = - parseColor(json["colors"]["number_text_default"] as String) + parseColor(json["colors"]["number_text_default"] as String) ..numpadTextDefaultInt = - parseColor(json["colors"]["numpad_text_default"] as String) + parseColor(json["colors"]["numpad_text_default"] as String) ..bottomNavTextInt = - parseColor(json["colors"]["bottom_nav_text"] as String) + parseColor(json["colors"]["bottom_nav_text"] as String) ..customTextButtonEnabledTextInt = parseColor( json["colors"]["custom_text_button_enabled_text"] as String) ..customTextButtonDisabledTextInt = parseColor( @@ -1635,87 +1635,87 @@ class StackTheme { ..switchBGOnInt = parseColor(json["colors"]["switch_bg_on"] as String) ..switchBGOffInt = parseColor(json["colors"]["switch_bg_off"] as String) ..switchBGDisabledInt = - parseColor(json["colors"]["switch_bg_disabled"] as String) + parseColor(json["colors"]["switch_bg_disabled"] as String) ..switchCircleOnInt = - parseColor(json["colors"]["switch_circle_on"] as String) + parseColor(json["colors"]["switch_circle_on"] as String) ..switchCircleOffInt = - parseColor(json["colors"]["switch_circle_off"] as String) + parseColor(json["colors"]["switch_circle_off"] as String) ..switchCircleDisabledInt = - parseColor(json["colors"]["switch_circle_disabled"] as String) + parseColor(json["colors"]["switch_circle_disabled"] as String) ..stepIndicatorBGCheckInt = - parseColor(json["colors"]["step_indicator_bg_check"] as String) + parseColor(json["colors"]["step_indicator_bg_check"] as String) ..stepIndicatorBGNumberInt = - parseColor(json["colors"]["step_indicator_bg_number"] as String) + parseColor(json["colors"]["step_indicator_bg_number"] as String) ..stepIndicatorBGInactiveInt = - parseColor(json["colors"]["step_indicator_bg_inactive"] as String) + parseColor(json["colors"]["step_indicator_bg_inactive"] as String) ..stepIndicatorBGLinesInt = - parseColor(json["colors"]["step_indicator_bg_lines"] as String) + parseColor(json["colors"]["step_indicator_bg_lines"] as String) ..stepIndicatorBGLinesInactiveInt = parseColor( json["colors"]["step_indicator_bg_lines_inactive"] as String) ..stepIndicatorIconTextInt = - parseColor(json["colors"]["step_indicator_icon_text"] as String) + parseColor(json["colors"]["step_indicator_icon_text"] as String) ..stepIndicatorIconNumberInt = - parseColor(json["colors"]["step_indicator_icon_number"] as String) + parseColor(json["colors"]["step_indicator_icon_number"] as String) ..stepIndicatorIconInactiveInt = - parseColor(json["colors"]["step_indicator_icon_inactive"] as String) + parseColor(json["colors"]["step_indicator_icon_inactive"] as String) ..checkboxBGCheckedInt = - parseColor(json["colors"]["checkbox_bg_checked"] as String) + parseColor(json["colors"]["checkbox_bg_checked"] as String) ..checkboxBorderEmptyInt = - parseColor(json["colors"]["checkbox_border_empty"] as String) + parseColor(json["colors"]["checkbox_border_empty"] as String) ..checkboxBGDisabledInt = - parseColor(json["colors"]["checkbox_bg_disabled"] as String) + parseColor(json["colors"]["checkbox_bg_disabled"] as String) ..checkboxIconCheckedInt = - parseColor(json["colors"]["checkbox_icon_checked"] as String) + parseColor(json["colors"]["checkbox_icon_checked"] as String) ..checkboxIconDisabledInt = - parseColor(json["colors"]["checkbox_icon_disabled"] as String) + parseColor(json["colors"]["checkbox_icon_disabled"] as String) ..checkboxTextLabelInt = - parseColor(json["colors"]["checkbox_text_label"] as String) + parseColor(json["colors"]["checkbox_text_label"] as String) ..snackBarBackSuccessInt = - parseColor(json["colors"]["snack_bar_back_success"] as String) + parseColor(json["colors"]["snack_bar_back_success"] as String) ..snackBarBackErrorInt = - parseColor(json["colors"]["snack_bar_back_error"] as String) + parseColor(json["colors"]["snack_bar_back_error"] as String) ..snackBarBackInfoInt = - parseColor(json["colors"]["snack_bar_back_info"] as String) + parseColor(json["colors"]["snack_bar_back_info"] as String) ..snackBarTextSuccessInt = - parseColor(json["colors"]["snack_bar_text_success"] as String) + parseColor(json["colors"]["snack_bar_text_success"] as String) ..snackBarTextErrorInt = - parseColor(json["colors"]["snack_bar_text_error"] as String) + parseColor(json["colors"]["snack_bar_text_error"] as String) ..snackBarTextInfoInt = - parseColor(json["colors"]["snack_bar_text_info"] as String) + parseColor(json["colors"]["snack_bar_text_info"] as String) ..bottomNavIconBackInt = - parseColor(json["colors"]["bottom_nav_icon_back"] as String) + parseColor(json["colors"]["bottom_nav_icon_back"] as String) ..bottomNavIconIconInt = - parseColor(json["colors"]["bottom_nav_icon_icon"] as String) + parseColor(json["colors"]["bottom_nav_icon_icon"] as String) ..bottomNavIconIconHighlightedInt = parseColor( json["colors"]["bottom_nav_icon_icon_highlighted"] as String) ..topNavIconPrimaryInt = - parseColor(json["colors"]["top_nav_icon_primary"] as String) + parseColor(json["colors"]["top_nav_icon_primary"] as String) ..topNavIconGreenInt = - parseColor(json["colors"]["top_nav_icon_green"] as String) + parseColor(json["colors"]["top_nav_icon_green"] as String) ..topNavIconYellowInt = - parseColor(json["colors"]["top_nav_icon_yellow"] as String) + parseColor(json["colors"]["top_nav_icon_yellow"] as String) ..topNavIconRedInt = - parseColor(json["colors"]["top_nav_icon_red"] as String) + parseColor(json["colors"]["top_nav_icon_red"] as String) ..settingsIconBackInt = - parseColor(json["colors"]["settings_icon_back"] as String) + parseColor(json["colors"]["settings_icon_back"] as String) ..settingsIconIconInt = - parseColor(json["colors"]["settings_icon_icon"] as String) + parseColor(json["colors"]["settings_icon_icon"] as String) ..settingsIconBack2Int = - parseColor(json["colors"]["settings_icon_back_two"] as String) + parseColor(json["colors"]["settings_icon_back_two"] as String) ..settingsIconElementInt = - parseColor(json["colors"]["settings_icon_element"] as String) + parseColor(json["colors"]["settings_icon_element"] as String) ..textFieldActiveBGInt = - parseColor(json["colors"]["text_field_active_bg"] as String) + parseColor(json["colors"]["text_field_active_bg"] as String) ..textFieldDefaultBGInt = - parseColor(json["colors"]["text_field_default_bg"] as String) + parseColor(json["colors"]["text_field_default_bg"] as String) ..textFieldErrorBGInt = - parseColor(json["colors"]["text_field_error_bg"] as String) + parseColor(json["colors"]["text_field_error_bg"] as String) ..textFieldSuccessBGInt = - parseColor(json["colors"]["text_field_success_bg"] as String) + parseColor(json["colors"]["text_field_success_bg"] as String) ..textFieldErrorBorderInt = - parseColor(json["colors"]["text_field_error_border"] as String) + parseColor(json["colors"]["text_field_error_border"] as String) ..textFieldSuccessBorderInt = - parseColor(json["colors"]["text_field_success_border"] as String) + parseColor(json["colors"]["text_field_success_border"] as String) ..textFieldActiveSearchIconLeftInt = parseColor( json["colors"]["text_field_active_search_icon_left"] as String) ..textFieldDefaultSearchIconLeftInt = parseColor( @@ -1725,19 +1725,19 @@ class StackTheme { ..textFieldSuccessSearchIconLeftInt = parseColor( json["colors"]["text_field_success_search_icon_left"] as String) ..textFieldActiveTextInt = - parseColor(json["colors"]["text_field_active_text"] as String) + parseColor(json["colors"]["text_field_active_text"] as String) ..textFieldDefaultTextInt = - parseColor(json["colors"]["text_field_default_text"] as String) + parseColor(json["colors"]["text_field_default_text"] as String) ..textFieldErrorTextInt = - parseColor(json["colors"]["text_field_error_text"] as String) + parseColor(json["colors"]["text_field_error_text"] as String) ..textFieldSuccessTextInt = - parseColor(json["colors"]["text_field_success_text"] as String) + parseColor(json["colors"]["text_field_success_text"] as String) ..textFieldActiveLabelInt = - parseColor(json["colors"]["text_field_active_label"] as String) + parseColor(json["colors"]["text_field_active_label"] as String) ..textFieldErrorLabelInt = - parseColor(json["colors"]["text_field_error_label"] as String) + parseColor(json["colors"]["text_field_error_label"] as String) ..textFieldSuccessLabelInt = - parseColor(json["colors"]["text_field_success_label"] as String) + parseColor(json["colors"]["text_field_success_label"] as String) ..textFieldActiveSearchIconRightInt = parseColor( json["colors"]["text_field_active_search_icon_right"] as String) ..textFieldDefaultSearchIconRightInt = parseColor( @@ -1753,61 +1753,61 @@ class StackTheme { ..settingsItem2ActiveSubInt = parseColor( json["colors"]["settings_item_level_two_active_sub"] as String) ..radioButtonIconBorderInt = - parseColor(json["colors"]["radio_button_icon_border"] as String) + parseColor(json["colors"]["radio_button_icon_border"] as String) ..radioButtonIconBorderDisabledInt = parseColor( json["colors"]["radio_button_icon_border_disabled"] as String) ..radioButtonBorderEnabledInt = - parseColor(json["colors"]["radio_button_border_enabled"] as String) + parseColor(json["colors"]["radio_button_border_enabled"] as String) ..radioButtonBorderDisabledInt = - parseColor(json["colors"]["radio_button_border_disabled"] as String) + parseColor(json["colors"]["radio_button_border_disabled"] as String) ..radioButtonIconCircleInt = - parseColor(json["colors"]["radio_button_icon_circle"] as String) + parseColor(json["colors"]["radio_button_icon_circle"] as String) ..radioButtonIconEnabledInt = - parseColor(json["colors"]["radio_button_icon_enabled"] as String) + parseColor(json["colors"]["radio_button_icon_enabled"] as String) ..radioButtonTextEnabledInt = - parseColor(json["colors"]["radio_button_text_enabled"] as String) + parseColor(json["colors"]["radio_button_text_enabled"] as String) ..radioButtonTextDisabledInt = - parseColor(json["colors"]["radio_button_text_disabled"] as String) + parseColor(json["colors"]["radio_button_text_disabled"] as String) ..radioButtonLabelEnabledInt = - parseColor(json["colors"]["radio_button_label_enabled"] as String) + parseColor(json["colors"]["radio_button_label_enabled"] as String) ..radioButtonLabelDisabledInt = - parseColor(json["colors"]["radio_button_label_disabled"] as String) + parseColor(json["colors"]["radio_button_label_disabled"] as String) ..infoItemBGInt = parseColor(json["colors"]["info_item_bg"] as String) ..infoItemLabelInt = - parseColor(json["colors"]["info_item_label"] as String) + parseColor(json["colors"]["info_item_label"] as String) ..infoItemTextInt = parseColor(json["colors"]["info_item_text"] as String) ..infoItemIconsInt = - parseColor(json["colors"]["info_item_icons"] as String) + parseColor(json["colors"]["info_item_icons"] as String) ..popupBGInt = parseColor(json["colors"]["popup_bg"] as String) ..currencyListItemBGInt = - parseColor(json["colors"]["currency_list_item_bg"] as String) + parseColor(json["colors"]["currency_list_item_bg"] as String) ..stackWalletBGInt = parseColor(json["colors"]["sw_bg"] as String) ..stackWalletMidInt = parseColor(json["colors"]["sw_mid"] as String) ..stackWalletBottomInt = parseColor(json["colors"]["sw_bottom"] as String) ..bottomNavShadowInt = - parseColor(json["colors"]["bottom_nav_shadow"] as String) + parseColor(json["colors"]["bottom_nav_shadow"] as String) ..splashInt = parseColor(json["colors"]["splash"] as String) ..highlightInt = parseColor(json["colors"]["highlight"] as String) ..warningForegroundInt = - parseColor(json["colors"]["warning_foreground"] as String) + parseColor(json["colors"]["warning_foreground"] as String) ..warningBackgroundInt = - parseColor(json["colors"]["warning_background"] as String) + parseColor(json["colors"]["warning_background"] as String) ..loadingOverlayTextColorInt = - parseColor(json["colors"]["loading_overlay_text_color"] as String) + parseColor(json["colors"]["loading_overlay_text_color"] as String) ..myStackContactIconBGInt = - parseColor(json["colors"]["my_stack_contact_icon_bg"] as String) + parseColor(json["colors"]["my_stack_contact_icon_bg"] as String) ..textConfirmTotalAmountInt = - parseColor(json["colors"]["text_confirm_total_amount"] as String) + parseColor(json["colors"]["text_confirm_total_amount"] as String) ..textSelectedWordTableItemInt = - parseColor(json["colors"]["text_selected_word_table_iterm"] as String) + parseColor(json["colors"]["text_selected_word_table_iterm"] as String) ..favoriteStarActiveInt = - parseColor(json["colors"]["favorite_star_active"] as String) + parseColor(json["colors"]["favorite_star_active"] as String) ..favoriteStarInactiveInt = - parseColor(json["colors"]["favorite_star_inactive"] as String) + parseColor(json["colors"]["favorite_star_inactive"] as String) ..rateTypeToggleColorOnInt = - parseColor(json["colors"]["rate_type_toggle_color_on"] as String) + parseColor(json["colors"]["rate_type_toggle_color_on"] as String) ..rateTypeToggleColorOffInt = - parseColor(json["colors"]["rate_type_toggle_color_off"] as String) + parseColor(json["colors"]["rate_type_toggle_color_off"] as String) ..rateTypeToggleDesktopColorOnInt = parseColor( json["colors"]["rate_type_toggle_desktop_color_on"] as String) ..rateTypeToggleDesktopColorOffInt = parseColor( @@ -1815,19 +1815,19 @@ class StackTheme { ..ethTagTextInt = parseColor(json["colors"]["eth_tag_text"] as String) ..ethTagBGInt = parseColor(json["colors"]["eth_tag_bg"] as String) ..ethWalletTagTextInt = - parseColor(json["colors"]["eth_wallet_tag_text"] as String) + parseColor(json["colors"]["eth_wallet_tag_text"] as String) ..ethWalletTagBGInt = - parseColor(json["colors"]["eth_wallet_tag_bg"] as String) + parseColor(json["colors"]["eth_wallet_tag_bg"] as String) ..tokenSummaryTextPrimaryInt = - parseColor(json["colors"]["token_summary_text_primary"] as String) + parseColor(json["colors"]["token_summary_text_primary"] as String) ..tokenSummaryTextSecondaryInt = - parseColor(json["colors"]["token_summary_text_secondary"] as String) + parseColor(json["colors"]["token_summary_text_secondary"] as String) ..tokenSummaryBGInt = - parseColor(json["colors"]["token_summary_bg"] as String) + parseColor(json["colors"]["token_summary_bg"] as String) ..tokenSummaryButtonBGInt = - parseColor(json["colors"]["token_summary_button_bg"] as String) + parseColor(json["colors"]["token_summary_button_bg"] as String) ..tokenSummaryIconInt = - parseColor(json["colors"]["token_summary_icon"] as String); + parseColor(json["colors"]["token_summary_icon"] as String); } /// Grab the int value of the hex color string. @@ -1840,7 +1840,7 @@ class StackTheme { } else { throw ArgumentError( '"$colorHex" and corresponding int ' - 'value "$colorValue" is not a valid color.', + 'value "$colorValue" is not a valid color.', ); } } catch (_) { @@ -2078,18 +2078,18 @@ class ThemeAssetsV2 implements IThemeAssets { @ignore Map<Coin, String> get coinIcons => _coinIcons ??= parseCoinAssetsString( - coinIconsString, - placeHolder: coinPlaceholder, - ); + coinIconsString, + placeHolder: coinPlaceholder, + ); @ignore Map<Coin, String>? _coinIcons; late final String coinIconsString; @ignore Map<Coin, String> get coinImages => _coinImages ??= parseCoinAssetsString( - coinImagesString, - placeHolder: coinPlaceholder, - ); + coinImagesString, + placeHolder: coinPlaceholder, + ); @ignore Map<Coin, String>? _coinImages; late final String coinImagesString; @@ -2164,9 +2164,9 @@ class ThemeAssetsV2 implements IThemeAssets { } static Map<Coin, String> parseCoinAssetsString( - String jsonString, { - required String placeHolder, - }) { + String jsonString, { + required String placeHolder, + }) { final json = jsonDecode(jsonString) as Map; final map = Map<String, dynamic>.from(json); @@ -2341,8 +2341,6 @@ class ThemeAssetsV3 implements IThemeAssets { // Added some future proof params in case we want to add anything else // This should provide some buffer in stead of creating assetsV4 etc - @Name("otherStringParam1") - late final String? dummy1; @Name("otherStringParam2") late final String? dummy2; @Name("otherStringParam3") @@ -2350,18 +2348,18 @@ class ThemeAssetsV3 implements IThemeAssets { @ignore Map<Coin, String> get coinIcons => _coinIcons ??= parseCoinAssetsString( - coinIconsString, - placeHolder: coinPlaceholder, - ); + coinIconsString, + placeHolder: coinPlaceholder, + ); @ignore Map<Coin, String>? _coinIcons; late final String coinIconsString; @ignore Map<Coin, String> get coinImages => _coinImages ??= parseCoinAssetsString( - coinImagesString, - placeHolder: coinPlaceholder, - ); + coinImagesString, + placeHolder: coinPlaceholder, + ); @ignore Map<Coin, String>? _coinImages; late final String coinImagesString; @@ -2381,13 +2379,26 @@ class ThemeAssetsV3 implements IThemeAssets { _coinCardImages ??= coinCardImagesString == null ? null : parseCoinAssetsString( - coinCardImagesString!, - placeHolder: coinPlaceholder, - ); + coinCardImagesString!, + placeHolder: coinPlaceholder, + ); @ignore Map<Coin, String>? _coinCardImages; 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(); factory ThemeAssetsV3.fromJson({ @@ -2439,9 +2450,15 @@ class ThemeAssetsV3 implements IThemeAssets { ) ..coinCardImagesString = json["coins"]["cards"] is Map ? createCoinAssetsString( - "$themeId/assets", - Map<String, dynamic>.from(json["coins"]["cards"] as Map), - ) + "$themeId/assets", + Map<String, dynamic>.from(json["coins"]["cards"] as Map), + ) + : null + ..coinCardFavoritesImagesString = json["coins"]["favoriteCards"] is Map + ? createCoinAssetsString( + "$themeId/assets", + Map<String, dynamic>.from(json["coins"]["favoriteCards"] as Map), + ) : null ..loadingGifRelative = json["loading_gif"] is String ? "$themeId/assets/${json["loading_gif"] as String}" @@ -2449,7 +2466,6 @@ class ThemeAssetsV3 implements IThemeAssets { ..backgroundRelative = json["background"] is String ? "$themeId/assets/${json["background"] as String}" : null - ..dummy1 = null ..dummy2 = null ..dummy3 = null; } @@ -2483,9 +2499,9 @@ class ThemeAssetsV3 implements IThemeAssets { } static Map<Coin, String> parseCoinAssetsString( - String jsonString, { - required String placeHolder, - }) { + String jsonString, { + required String placeHolder, + }) { final json = jsonDecode(jsonString) as Map; final map = Map<String, dynamic>.from(json); @@ -2528,6 +2544,7 @@ class ThemeAssetsV3 implements IThemeAssets { 'coinImages: $coinImages, ' 'coinSecondaryImages: $coinSecondaryImages, ' 'coinCardImages: $coinCardImages' + 'coinCardFavoritesImages: $coinCardFavoritesImages' ')'; } } @@ -2554,4 +2571,4 @@ abstract class IThemeAssets { String? get loadingGif; String? get background; -} +} \ No newline at end of file diff --git a/lib/models/isar/stack_theme.g.dart b/lib/models/isar/stack_theme.g.dart index bf38c461d..03c543ff0 100644 --- a/lib/models/isar/stack_theme.g.dart +++ b/lib/models/isar/stack_theme.g.dart @@ -29626,7 +29626,7 @@ int _themeAssetsV3EstimateSize( } } { - final value = object.dummy1; + final value = object.coinCardFavoritesImagesString; if (value != null) { bytesCount += 3 + value.length * 3; } @@ -29677,7 +29677,7 @@ void _themeAssetsV3Serialize( writer.writeString(offsets[7], object.coinSecondaryImagesString); writer.writeString(offsets[8], object.exchangeRelative); writer.writeString(offsets[9], object.loadingGifRelative); - writer.writeString(offsets[10], object.dummy1); + writer.writeString(offsets[10], object.coinCardFavoritesImagesString); writer.writeString(offsets[11], object.dummy2); writer.writeString(offsets[12], object.dummy3); writer.writeString(offsets[13], object.personaEasyRelative); @@ -29714,7 +29714,7 @@ ThemeAssetsV3 _themeAssetsV3Deserialize( object.coinSecondaryImagesString = reader.readString(offsets[7]); object.exchangeRelative = reader.readString(offsets[8]); object.loadingGifRelative = reader.readStringOrNull(offsets[9]); - object.dummy1 = reader.readStringOrNull(offsets[10]); + object.coinCardFavoritesImagesString = reader.readStringOrNull(offsets[10]); object.dummy2 = reader.readStringOrNull(offsets[11]); object.dummy3 = reader.readStringOrNull(offsets[12]); object.personaEasyRelative = reader.readString(offsets[13]); @@ -31224,7 +31224,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder<ThemeAssetsV3, ThemeAssetsV3, QAfterFilterCondition> - dummy1IsNull() { + coinCardFavoritesImagesStringIsNull() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(const FilterCondition.isNull( property: r'otherStringParam1', @@ -31233,7 +31233,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder<ThemeAssetsV3, ThemeAssetsV3, QAfterFilterCondition> - dummy1IsNotNull() { + coinCardFavoritesImagesStringIsNotNull() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(const FilterCondition.isNotNull( property: r'otherStringParam1', @@ -31242,7 +31242,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder<ThemeAssetsV3, ThemeAssetsV3, QAfterFilterCondition> - dummy1EqualTo( + coinCardFavoritesImagesStringEqualTo( String? value, { bool caseSensitive = true, }) { @@ -31256,7 +31256,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder<ThemeAssetsV3, ThemeAssetsV3, QAfterFilterCondition> - dummy1GreaterThan( + coinCardFavoritesImagesStringGreaterThan( String? value, { bool include = false, bool caseSensitive = true, @@ -31272,7 +31272,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder<ThemeAssetsV3, ThemeAssetsV3, QAfterFilterCondition> - dummy1LessThan( + coinCardFavoritesImagesStringLessThan( String? value, { bool include = false, bool caseSensitive = true, @@ -31288,7 +31288,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder<ThemeAssetsV3, ThemeAssetsV3, QAfterFilterCondition> - dummy1Between( + coinCardFavoritesImagesStringBetween( String? lower, String? upper, { bool includeLower = true, @@ -31308,7 +31308,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder<ThemeAssetsV3, ThemeAssetsV3, QAfterFilterCondition> - dummy1StartsWith( + coinCardFavoritesImagesStringStartsWith( String value, { bool caseSensitive = true, }) { @@ -31322,7 +31322,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder<ThemeAssetsV3, ThemeAssetsV3, QAfterFilterCondition> - dummy1EndsWith( + coinCardFavoritesImagesStringEndsWith( String value, { bool caseSensitive = true, }) { @@ -31336,7 +31336,8 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder<ThemeAssetsV3, ThemeAssetsV3, QAfterFilterCondition> - dummy1Contains(String value, {bool caseSensitive = true}) { + coinCardFavoritesImagesStringContains(String value, + {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.contains( property: r'otherStringParam1', @@ -31347,7 +31348,8 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder<ThemeAssetsV3, ThemeAssetsV3, QAfterFilterCondition> - dummy1Matches(String pattern, {bool caseSensitive = true}) { + coinCardFavoritesImagesStringMatches(String pattern, + {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.matches( property: r'otherStringParam1', @@ -31358,7 +31360,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder<ThemeAssetsV3, ThemeAssetsV3, QAfterFilterCondition> - dummy1IsEmpty() { + coinCardFavoritesImagesStringIsEmpty() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.equalTo( property: r'otherStringParam1', @@ -31368,7 +31370,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder<ThemeAssetsV3, ThemeAssetsV3, QAfterFilterCondition> - dummy1IsNotEmpty() { + coinCardFavoritesImagesStringIsNotEmpty() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.greaterThan( property: r'otherStringParam1', diff --git a/lib/models/lelantus_coin.dart b/lib/models/lelantus_coin.dart index 0e32d33bf..56557c1cd 100644 --- a/lib/models/lelantus_coin.dart +++ b/lib/models/lelantus_coin.dart @@ -12,6 +12,7 @@ import 'package:hive/hive.dart'; part 'type_adaptors/lelantus_coin.g.dart'; +@Deprecated("Use Isar object instead") // @HiveType(typeId: 9) class LelantusCoin { // @HiveField(0) @@ -27,6 +28,7 @@ class LelantusCoin { // @HiveField(5) bool isUsed; + @Deprecated("Use Isar object instead") LelantusCoin( this.index, this.value, diff --git a/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart b/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart index a3d297b8c..dd807976b 100644 --- a/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart +++ b/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart @@ -142,7 +142,7 @@ class _AddCustomTokenViewState extends ConsumerState<AddCustomTokenView> { context: context, message: "Looking up contract", ); - currentToken = response.value; + currentToken = response!.value; if (currentToken != null) { nameController.text = currentToken!.name; symbolController.text = currentToken!.symbol; @@ -157,7 +157,7 @@ class _AddCustomTokenViewState extends ConsumerState<AddCustomTokenView> { context: context, builder: (context) => StackOkDialog( title: "Failed to look up token", - message: response.exception?.message, + message: response!.exception?.message, ), ), ); diff --git a/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart b/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart index e8c7c711c..36c914fd8 100644 --- a/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart +++ b/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart @@ -8,6 +8,8 @@ * */ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:stackwallet/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart'; import 'package:stackwallet/themes/stack_colors.dart'; @@ -32,35 +34,37 @@ class CreateWalletButtonGroup extends StatelessWidget { crossAxisAlignment: isDesktop ? CrossAxisAlignment.center : CrossAxisAlignment.stretch, children: [ - ConstrainedBox( - constraints: BoxConstraints( - minHeight: isDesktop ? 70 : 0, - minWidth: isDesktop ? 480 : 0, - ), - child: TextButton( - style: Theme.of(context) - .extension<StackColors>()! - .getPrimaryEnabledButtonStyle(context), - onPressed: () { - Navigator.of(context).pushNamed( - NameYourWalletView.routeName, - arguments: Tuple2( - AddWalletType.New, - coin, - ), - ); - }, - child: Text( - "Create new wallet", - style: isDesktop - ? STextStyles.desktopButtonEnabled(context) - : STextStyles.button(context), + if (Platform.isAndroid || coin != Coin.wownero) + ConstrainedBox( + constraints: BoxConstraints( + minHeight: isDesktop ? 70 : 0, + minWidth: isDesktop ? 480 : 0, + ), + child: TextButton( + style: Theme.of(context) + .extension<StackColors>()! + .getPrimaryEnabledButtonStyle(context), + onPressed: () { + Navigator.of(context).pushNamed( + NameYourWalletView.routeName, + arguments: Tuple2( + AddWalletType.New, + coin, + ), + ); + }, + child: Text( + "Create new wallet", + style: isDesktop + ? STextStyles.desktopButtonEnabled(context) + : STextStyles.button(context), + ), ), ), - ), - SizedBox( - height: isDesktop ? 16 : 12, - ), + if (Platform.isAndroid || coin != Coin.wownero) + SizedBox( + height: isDesktop ? 16 : 12, + ), ConstrainedBox( constraints: BoxConstraints( minHeight: isDesktop ? 70 : 0, diff --git a/lib/pages/exchange_view/exchange_form.dart b/lib/pages/exchange_view/exchange_form.dart index e65242fbd..a15e13843 100644 --- a/lib/pages/exchange_view/exchange_form.dart +++ b/lib/pages/exchange_view/exchange_form.dart @@ -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/trocador/trocador_exchange.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/constants.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -160,26 +161,15 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> { if (value == 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 Decimal.parse(value); - } catch (_) { - try { - return Decimal.parse(value.replaceAll(",", ".")); - } catch (_) { - rethrow; - } - } - } catch (_) { - return null; - } + return AmountUnit.normal + .tryParse( + value, + locale: ref.read(localeServiceChangeNotifierProvider).locale, + coin: Coin.bitcoin, // dummy value (not used due to override) + overrideWithDecimalPlacesFromString: true, + ) + ?.decimal; } Future<AggregateCurrency> _getAggregateCurrency(Currency currency) async { @@ -809,6 +799,14 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> { // if (_swapLock) { _sendController.text = ref.read(efSendAmountStringProvider); // } + + if (_sendFocusNode.hasFocus) { + _sendController.selection = TextSelection.fromPosition( + TextPosition( + offset: _sendController.text.length, + ), + ); + } } }); ref.listen(efSendAmountStringProvider, (previous, String next) { @@ -820,11 +818,19 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> { ? "-" : ref.read(efReceiveAmountStringProvider); // } + + if (_receiveFocusNode.hasFocus) { + _receiveController.selection = TextSelection.fromPosition( + TextPosition( + offset: _receiveController.text.length, + ), + ); + } } }); ref.listen(efEstimateProvider.notifier, (previous, next) { - final estimate = (next as StateController<Estimate?>).state; + final estimate = (next).state; if (ref.read(efReversedProvider)) { updateSend(estimate); } else { diff --git a/lib/pages/monkey/monkey_loaded_view.dart b/lib/pages/monkey/monkey_loaded_view.dart new file mode 100644 index 000000000..93c28b39a --- /dev/null +++ b/lib/pages/monkey/monkey_loaded_view.dart @@ -0,0 +1,275 @@ +// import 'dart:io'; +// import 'dart:typed_data'; +// +// import 'package:flutter/material.dart'; +// import 'package:flutter_riverpod/flutter_riverpod.dart'; +// import 'package:flutter_svg/svg.dart'; +// import 'package:http/http.dart' as http; +// import 'package:path_provider/path_provider.dart'; +// import 'package:permission_handler/permission_handler.dart'; +// import 'package:stackwallet/pages/wallet_view/wallet_view.dart'; +// import 'package:stackwallet/providers/global/wallets_provider.dart'; +// import 'package:stackwallet/services/coins/banano/banano_wallet.dart'; +// import 'package:stackwallet/services/coins/manager.dart'; +// import 'package:stackwallet/themes/stack_colors.dart'; +// import 'package:stackwallet/utilities/assets.dart'; +// import 'package:stackwallet/utilities/enums/coin_enum.dart'; +// import 'package:stackwallet/utilities/text_styles.dart'; +// import 'package:stackwallet/widgets/background.dart'; +// import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +// import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +// +// class MonkeyLoadedView extends ConsumerStatefulWidget { +// const MonkeyLoadedView({ +// Key? key, +// required this.walletId, +// required this.managerProvider, +// }) : super(key: key); +// +// static const String routeName = "/hasMonkey"; +// static const double navBarHeight = 65.0; +// +// final String walletId; +// final ChangeNotifierProvider<Manager> managerProvider; +// +// @override +// ConsumerState<MonkeyLoadedView> createState() => _MonkeyLoadedViewState(); +// } +// +// class _MonkeyLoadedViewState extends ConsumerState<MonkeyLoadedView> { +// late final String walletId; +// late final ChangeNotifierProvider<Manager> managerProvider; +// +// String receivingAddress = ""; +// +// void getMonkeySVG(String address) async { +// if (address.isEmpty) { +// //address shouldn't be empty +// return; +// } +// +// final http.Response response = await http +// .get(Uri.parse('https://monkey.banano.cc/api/v1/monkey/$address')); +// +// if (response.statusCode == 200) { +// final decodedResponse = response.bodyBytes; +// Directory directory = await getApplicationDocumentsDirectory(); +// late Directory sampleFolder; +// +// if (Platform.isAndroid) { +// directory = Directory("/storage/emulated/0/"); +// sampleFolder = Directory('${directory!.path}Documents'); +// } else if (Platform.isIOS) { +// sampleFolder = Directory(directory!.path); +// } else if (Platform.isLinux) { +// sampleFolder = Directory('${directory!.path}Documents'); +// } else if (Platform.isWindows) { +// sampleFolder = Directory('${directory!.path}Documents'); +// } else if (Platform.isMacOS) { +// sampleFolder = Directory('${directory!.path}Documents'); +// } +// +// try { +// if (!sampleFolder.existsSync()) { +// sampleFolder.createSync(recursive: true); +// } +// } catch (e, s) { +// // todo: come back to this +// debugPrint("$e $s"); +// } +// +// final docPath = sampleFolder.path; +// final filePath = "$docPath/monkey.svg"; +// +// File imgFile = File(filePath); +// await imgFile.writeAsBytes(decodedResponse); +// } else { +// throw Exception("Failed to get MonKey"); +// } +// } +// +// void getMonkeyPNG(String address) async { +// if (address.isEmpty) { +// //address shouldn't be empty +// return; +// } +// +// final http.Response response = await http.get(Uri.parse( +// 'https://monkey.banano.cc/api/v1/monkey/${address}?format=png&size=512&background=false')); +// +// if (response.statusCode == 200) { +// if (Platform.isAndroid) { +// await Permission.storage.request(); +// } +// +// final decodedResponse = response.bodyBytes; +// Directory directory = await getApplicationDocumentsDirectory(); +// late Directory sampleFolder; +// +// if (Platform.isAndroid) { +// directory = Directory("/storage/emulated/0/"); +// sampleFolder = Directory('${directory!.path}Documents'); +// } else if (Platform.isIOS) { +// sampleFolder = Directory(directory!.path); +// } else if (Platform.isLinux) { +// sampleFolder = Directory('${directory!.path}Documents'); +// } else if (Platform.isWindows) { +// sampleFolder = Directory('${directory!.path}Documents'); +// } else if (Platform.isMacOS) { +// sampleFolder = Directory('${directory!.path}Documents'); +// } +// +// try { +// if (!sampleFolder.existsSync()) { +// sampleFolder.createSync(recursive: true); +// } +// } catch (e, s) { +// // todo: come back to this +// debugPrint("$e $s"); +// } +// +// final docPath = sampleFolder.path; +// final filePath = "$docPath/monkey.png"; +// +// File imgFile = File(filePath); +// await imgFile.writeAsBytes(decodedResponse); +// } else { +// throw Exception("Failed to get MonKey"); +// } +// } +// +// @override +// void initState() { +// walletId = widget.walletId; +// managerProvider = widget.managerProvider; +// +// WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { +// final address = await ref +// .read(walletsChangeNotifierProvider) +// .getManager(walletId) +// .currentReceivingAddress; +// setState(() { +// receivingAddress = address; +// }); +// }); +// +// super.initState(); +// } +// +// @override +// void dispose() { +// super.dispose(); +// } +// +// @override +// Widget build(BuildContext context) { +// final Coin coin = ref.watch(managerProvider.select((value) => value.coin)); +// final manager = ref.watch(walletsChangeNotifierProvider +// .select((value) => value.getManager(widget.walletId))); +// +// List<int>? imageBytes; +// imageBytes = (manager.wallet as BananoWallet).getMonkeyImageBytes(); +// +// return Background( +// child: Stack( +// children: [ +// Scaffold( +// appBar: AppBar( +// leading: AppBarBackButton( +// onPressed: () { +// Navigator.of(context).popUntil( +// ModalRoute.withName(WalletView.routeName), +// ); +// }, +// ), +// title: Text( +// "MonKey", +// style: STextStyles.navBarTitle(context), +// ), +// actions: [ +// AspectRatio( +// aspectRatio: 1, +// child: AppBarIconButton( +// icon: SvgPicture.asset(Assets.svg.circleQuestion), +// onPressed: () { +// showDialog<dynamic>( +// context: context, +// useSafeArea: false, +// barrierDismissible: true, +// builder: (context) { +// return Dialog( +// child: Material( +// borderRadius: BorderRadius.circular( +// 20, +// ), +// child: Container( +// height: 200, +// decoration: BoxDecoration( +// color: Theme.of(context) +// .extension<StackColors>()! +// .popupBG, +// borderRadius: BorderRadius.circular( +// 20, +// ), +// ), +// child: Column( +// children: [ +// Center( +// child: Text( +// "Help", +// style: STextStyles.pageTitleH2( +// context), +// ), +// ) +// ], +// ), +// ), +// ), +// ); +// }); +// }), +// ) +// ], +// ), +// body: Column( +// children: [ +// const Spacer( +// flex: 1, +// ), +// if (imageBytes != null) +// Container( +// child: SvgPicture.memory(Uint8List.fromList(imageBytes!)), +// width: 300, +// height: 300, +// ), +// const Spacer( +// flex: 1, +// ), +// Padding( +// padding: const EdgeInsets.all(16.0), +// child: Column( +// children: [ +// SecondaryButton( +// label: "Download as SVG", +// onPressed: () async { +// getMonkeySVG(receivingAddress); +// }, +// ), +// const SizedBox(height: 12), +// SecondaryButton( +// label: "Download as PNG", +// onPressed: () { +// getMonkeyPNG(receivingAddress); +// }, +// ), +// ], +// ), +// ), +// ], +// ), +// ), +// ], +// ), +// ); +// } +// } diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart new file mode 100644 index 000000000..ebc18843c --- /dev/null +++ b/lib/pages/monkey/monkey_view.dart @@ -0,0 +1,540 @@ +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:stackwallet/notifications/show_flush_bar.dart'; +import 'package:stackwallet/providers/global/wallets_provider.dart'; +import 'package:stackwallet/services/coins/banano/banano_wallet.dart'; +import 'package:stackwallet/services/monkey_service.dart'; +import 'package:stackwallet/themes/coin_icon_provider.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/show_loading.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/background.dart'; +import 'package:stackwallet/widgets/conditional_parent.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/desktop_app_bar.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_scaffold.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +import 'package:stackwallet/widgets/stack_dialog.dart'; + +class MonkeyView extends ConsumerStatefulWidget { + const MonkeyView({ + Key? key, + required this.walletId, + }) : super(key: key); + + static const String routeName = "/monkey"; + static const double navBarHeight = 65.0; + + final String walletId; + + @override + ConsumerState<MonkeyView> createState() => _MonkeyViewState(); +} + +class _MonkeyViewState extends ConsumerState<MonkeyView> { + late final String walletId; + List<int>? imageBytes; + + Future<void> _updateWalletMonKey(Uint8List monKeyBytes) async { + final manager = + ref.read(walletsChangeNotifierProvider).getManager(walletId); + await (manager.wallet as BananoWallet) + .updateMonkeyImageBytes(monKeyBytes.toList()); + } + + Future<Directory?> _getDocsDir() async { + try { + if (Platform.isAndroid) { + return Directory("/storage/emulated/0/Documents"); + } + + return await getApplicationDocumentsDirectory(); + } catch (_) { + return null; + } + } + + String _monkeyPath = ""; + + Future<void> _saveMonKeyToFile({ + required Uint8List bytes, + bool isPNG = false, + bool overwrite = false, + }) async { + if (Platform.isAndroid) { + await Permission.storage.request(); + } + + final dir = await _getDocsDir(); + if (dir == null) { + throw Exception("Failed to get documents directory to save monKey image"); + } + + final address = await ref + .read(walletsChangeNotifierProvider) + .getManager(walletId) + .currentReceivingAddress; + final docPath = dir.path; + String filePath = "$docPath/monkey_$address"; + + filePath += isPNG ? ".png" : ".svg"; + + File imgFile = File(filePath); + + if (imgFile.existsSync() && !overwrite) { + throw Exception("File already exists"); + } + + await imgFile.writeAsBytes(bytes); + _monkeyPath = filePath; + } + + @override + void initState() { + walletId = widget.walletId; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + final manager = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(widget.walletId))); + final Coin coin = manager.coin; + + final bool isDesktop = Util.isDesktop; + + imageBytes ??= (manager.wallet as BananoWallet).getMonkeyImageBytes(); + + return Background( + child: ConditionalParent( + condition: isDesktop, + builder: (child) => DesktopScaffold( + appBar: DesktopAppBar( + background: Theme.of(context).extension<StackColors>()!.popupBG, + leading: Expanded( + child: Row( + children: [ + const SizedBox( + width: 32, + ), + AppBarIconButton( + size: 32, + color: Theme.of(context) + .extension<StackColors>()! + .textFieldDefaultBG, + shadows: const [], + icon: SvgPicture.asset( + Assets.svg.arrowLeft, + width: 18, + height: 18, + color: Theme.of(context) + .extension<StackColors>()! + .topNavIconPrimary, + ), + onPressed: Navigator.of(context).pop, + ), + const SizedBox( + width: 15, + ), + SvgPicture.asset( + Assets.svg.monkey, + width: 32, + height: 32, + color: Theme.of(context) + .extension<StackColors>()! + .textSubtitle1, + ), + const SizedBox( + width: 12, + ), + Text( + "MonKey", + style: STextStyles.desktopH3(context), + ), + ], + ), + ), + trailing: RawMaterialButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(1000), + ), + onPressed: () { + showDialog<void>( + context: context, + useSafeArea: false, + barrierDismissible: true, + builder: (context) { + return DesktopDialog( + maxHeight: double.infinity, + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only(left: 32), + child: Text( + "About MonKeys", + style: STextStyles.desktopH3(context), + ), + ), + const DesktopDialogCloseButton(), + ], + ), + Text( + "A MonKey is a visual representation of your Banano address.", + style: + STextStyles.desktopTextMedium(context).copyWith( + color: Theme.of(context) + .extension<StackColors>()! + .textDark3, + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.all( + 32, + ), + child: PrimaryButton( + width: 272.5, + label: "OK", + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + ], + ), + ], + ), + ); + }, + ); + }, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 19, + horizontal: 32, + ), + child: Row( + children: [ + SvgPicture.asset( + Assets.svg.circleQuestion, + width: 20, + height: 20, + color: Theme.of(context) + .extension<StackColors>()! + .customTextButtonEnabledText, + ), + const SizedBox( + width: 8, + ), + Text( + "What is MonKey?", + style: + STextStyles.desktopMenuItemSelected(context).copyWith( + color: Theme.of(context) + .extension<StackColors>()! + .customTextButtonEnabledText, + ), + ) + ], + ), + ), + ), + useSpacers: false, + isCompactHeight: true, + ), + body: child, + ), + child: ConditionalParent( + condition: !isDesktop, + builder: (child) => Scaffold( + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () { + Navigator.of(context).pop(); + }, + ), + title: Text( + "MonKey", + style: STextStyles.navBarTitle(context), + ), + actions: [ + AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + icon: SvgPicture.asset( + Assets.svg.circleQuestion, + ), + onPressed: () { + showDialog<dynamic>( + context: context, + useSafeArea: false, + barrierDismissible: true, + builder: (context) { + return const StackOkDialog( + title: "About MonKeys", + message: + "A MonKey is a visual representation of your Banano address.", + ); + }, + ); + }, + ), + ), + ], + ), + body: child, + ), + child: ConditionalParent( + condition: isDesktop, + builder: (child) => SizedBox( + width: 318, + child: child, + ), + child: ConditionalParent( + condition: imageBytes != null, + builder: (_) => Column( + children: [ + isDesktop + ? const SizedBox( + height: 50, + ) + : const Spacer( + flex: 1, + ), + if (imageBytes != null) + SizedBox( + width: 300, + height: 300, + child: SvgPicture.memory(Uint8List.fromList(imageBytes!)), + ), + isDesktop + ? const SizedBox( + height: 50, + ) + : const Spacer( + flex: 1, + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + SecondaryButton( + label: "Save as SVG", + onPressed: () async { + bool didError = false; + await showLoading( + whileFuture: Future.wait([ + _saveMonKeyToFile( + bytes: Uint8List.fromList( + (manager.wallet as BananoWallet) + .getMonkeyImageBytes()!), + ), + Future<void>.delayed( + const Duration(seconds: 2), + ), + ]), + context: context, + isDesktop: Util.isDesktop, + message: "Saving MonKey svg", + onException: (e) { + didError = true; + String msg = e.toString(); + while (msg.isNotEmpty && + msg.startsWith("Exception:")) { + msg = msg.substring(10).trim(); + } + showFloatingFlushBar( + type: FlushBarType.warning, + message: msg, + context: context, + ); + }, + ); + + if (!didError && mounted) { + await showFloatingFlushBar( + type: FlushBarType.success, + message: + "SVG MonKey image saved to $_monkeyPath", + context: context, + ); + } + }, + ), + const SizedBox(height: 12), + SecondaryButton( + label: "Download as PNG", + onPressed: () async { + bool didError = false; + await showLoading( + whileFuture: Future.wait([ + manager.currentReceivingAddress.then( + (address) async => await ref + .read(pMonKeyService) + .fetchMonKey( + address: address, + png: true, + ) + .then( + (monKeyBytes) async => + await _saveMonKeyToFile( + bytes: monKeyBytes, + isPNG: true, + ), + ), + ), + Future<void>.delayed( + const Duration(seconds: 2)), + ]), + context: context, + isDesktop: Util.isDesktop, + message: "Downloading MonKey png", + onException: (e) { + didError = true; + String msg = e.toString(); + while (msg.isNotEmpty && + msg.startsWith("Exception:")) { + msg = msg.substring(10).trim(); + } + showFloatingFlushBar( + type: FlushBarType.warning, + message: msg, + context: context, + ); + }, + ); + + if (!didError && mounted) { + await showFloatingFlushBar( + type: FlushBarType.success, + message: + "PNG MonKey image saved to $_monkeyPath", + context: context, + ); + } + }, + ), + ], + ), + ), + // child, + ], + ), + child: Column( + children: [ + isDesktop + ? const SizedBox( + height: 100, + ) + : const Spacer( + flex: 4, + ), + Center( + child: Column( + children: [ + Opacity( + opacity: 0.2, + child: SvgPicture.file( + File( + ref.watch(coinIconProvider(coin)), + ), + width: 200, + height: 200, + ), + ), + const SizedBox( + height: 70, + ), + Text( + "You do not have a MonKey yet. \nFetch yours now!", + style: STextStyles.smallMed14(context).copyWith( + color: Theme.of(context) + .extension<StackColors>()! + .textDark3, + ), + textAlign: TextAlign.center, + ), + ], + ), + ), + isDesktop + ? const SizedBox( + height: 50, + ) + : const Spacer( + flex: 6, + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: PrimaryButton( + label: "Fetch MonKey", + onPressed: () async { + await showLoading( + whileFuture: Future.wait([ + manager.currentReceivingAddress.then( + (address) async => await ref + .read(pMonKeyService) + .fetchMonKey(address: address) + .then( + (monKeyBytes) async => + await _updateWalletMonKey( + monKeyBytes, + ), + ), + ), + Future<void>.delayed(const Duration(seconds: 2)), + ]), + context: context, + isDesktop: Util.isDesktop, + message: "Fetching MonKey", + subMessage: "We are fetching your MonKey", + onException: (e) { + String msg = e.toString(); + while (msg.isNotEmpty && + msg.startsWith("Exception:")) { + msg = msg.substring(10).trim(); + } + showFloatingFlushBar( + type: FlushBarType.warning, + message: msg, + context: context, + ); + }, + ); + + imageBytes = (manager.wallet as BananoWallet) + .getMonkeyImageBytes(); + + if (imageBytes != null) { + setState(() {}); + } + }, + ), + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/monkey/sub_widgets/fetch_monkey_dialog.dart b/lib/pages/monkey/sub_widgets/fetch_monkey_dialog.dart new file mode 100644 index 000000000..94034fb78 --- /dev/null +++ b/lib/pages/monkey/sub_widgets/fetch_monkey_dialog.dart @@ -0,0 +1,135 @@ +/* + * 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:flutter/material.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/animated_widgets/rotating_arrows.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +import 'package:stackwallet/widgets/stack_dialog.dart'; + +class FetchMonkeyDialog extends StatefulWidget { + const FetchMonkeyDialog({ + Key? key, + required this.onCancel, + }) : super(key: key); + + final Future<void> Function() onCancel; + + @override + State<FetchMonkeyDialog> createState() => _FetchMonkeyDialogState(); +} + +class _FetchMonkeyDialogState extends State<FetchMonkeyDialog> { + late final Future<void> Function() onCancel; + @override + void initState() { + onCancel = widget.onCancel; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + if (Util.isDesktop) { + return DesktopDialog( + child: Column( + children: [ + DesktopDialogCloseButton( + onPressedOverride: () async { + await onCancel.call(); + if (mounted) { + Navigator.of(context).pop(); + } + }, + ), + const Spacer( + flex: 1, + ), + const RotatingArrows( + width: 40, + height: 40, + ), + const Spacer( + flex: 2, + ), + Text( + "Fetching MonKey", + style: STextStyles.desktopH2(context), + textAlign: TextAlign.center, + ), + const SizedBox( + height: 16, + ), + Text( + "We are fetching your MonKey", + style: STextStyles.desktopTextMedium(context).copyWith( + color: Theme.of(context).extension<StackColors>()!.textDark3, + ), + textAlign: TextAlign.center, + ), + const Spacer( + flex: 2, + ), + Padding( + padding: const EdgeInsets.only( + left: 32, + right: 32, + bottom: 32, + ), + child: SecondaryButton( + label: "Cancel", + width: 272.5, + onPressed: () async { + await onCancel.call(); + if (mounted) { + Navigator.of(context).pop(); + } + }, + ), + ), + ], + ), + ); + } else { + return WillPopScope( + onWillPop: () async { + return false; + }, + child: StackDialog( + title: "Fetching MonKey", + message: "We are fetching your MonKey", + icon: const RotatingArrows( + width: 24, + height: 24, + ), + rightButton: TextButton( + style: Theme.of(context) + .extension<StackColors>()! + .getSecondaryEnabledButtonStyle(context), + child: Text( + "Cancel", + style: STextStyles.itemSubtitle12(context), + ), + onPressed: () async { + await onCancel.call(); + if (mounted) { + Navigator.of(context).pop(); + } + }, + ), + ), + ); + } + } +} diff --git a/lib/pages/ordinals/ordinal_details_view.dart b/lib/pages/ordinals/ordinal_details_view.dart new file mode 100644 index 000000000..041401767 --- /dev/null +++ b/lib/pages/ordinals/ordinal_details_view.dart @@ -0,0 +1,374 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:http/http.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; +import 'package:stackwallet/models/isar/ordinal.dart'; +import 'package:stackwallet/notifications/show_flush_bar.dart'; +import 'package:stackwallet/providers/db/main_db_provider.dart'; +import 'package:stackwallet/providers/global/wallets_provider.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/assets.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/show_loading.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/background.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +class OrdinalDetailsView extends ConsumerStatefulWidget { + const OrdinalDetailsView({ + Key? key, + required this.walletId, + required this.ordinal, + }) : super(key: key); + + final String walletId; + final Ordinal ordinal; + + static const routeName = "/ordinalDetailsView"; + + @override + ConsumerState<OrdinalDetailsView> createState() => _OrdinalDetailsViewState(); +} + +class _OrdinalDetailsViewState extends ConsumerState<OrdinalDetailsView> { + static const _spacing = 12.0; + + late final UTXO? utxo; + + @override + void initState() { + utxo = widget.ordinal.getUTXO(ref.read(mainDBProvider)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + final coin = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(widget.walletId).coin)); + + return Background( + child: SafeArea( + child: Scaffold( + backgroundColor: + Theme.of(context).extension<StackColors>()!.background, + appBar: AppBar( + backgroundColor: + Theme.of(context).extension<StackColors>()!.background, + leading: const AppBarBackButton(), + title: Text( + "Ordinal details", + style: STextStyles.navBarTitle(context), + ), + ), + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric( + vertical: 12, + horizontal: 39, + ), + child: _OrdinalImageGroup( + ordinal: widget.ordinal, + walletId: widget.walletId, + ), + ), + _DetailsItemWCopy( + title: "Inscription number", + data: widget.ordinal.inscriptionNumber.toString(), + ), + const SizedBox( + height: _spacing, + ), + _DetailsItemWCopy( + title: "Inscription ID", + data: widget.ordinal.inscriptionId, + ), + // const SizedBox( + // height: _spacing, + // ), + // // todo: add utxo status + const SizedBox( + height: _spacing, + ), + _DetailsItemWCopy( + title: "Amount", + data: utxo == null + ? "ERROR" + : ref.watch(pAmountFormatter(coin)).format( + Amount( + rawValue: BigInt.from(utxo!.value), + fractionDigits: coin.decimals, + ), + ), + ), + const SizedBox( + height: _spacing, + ), + _DetailsItemWCopy( + title: "Owner address", + data: utxo?.address ?? "ERROR", + ), + const SizedBox( + height: _spacing, + ), + _DetailsItemWCopy( + title: "Transaction ID", + data: widget.ordinal.utxoTXID, + ), + const SizedBox( + height: _spacing, + ), + ], + ), + ), + ), + ), + ), + ); + } +} + +class _DetailsItemWCopy extends StatelessWidget { + const _DetailsItemWCopy({ + Key? key, + required this.title, + required this.data, + }) : super(key: key); + + final String title; + final String data; + + @override + Widget build(BuildContext context) { + return RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: STextStyles.itemSubtitle(context), + ), + GestureDetector( + onTap: () async { + await Clipboard.setData(ClipboardData(text: data)); + if (context.mounted) { + unawaited( + showFloatingFlushBar( + type: FlushBarType.info, + message: "Copied to clipboard", + context: context, + ), + ); + } + }, + child: Row( + children: [ + SvgPicture.asset( + Assets.svg.copy, + color: Theme.of(context) + .extension<StackColors>()! + .infoItemIcons, + width: 12, + ), + const SizedBox( + width: 6, + ), + Text( + "Copy", + style: STextStyles.infoSmall(context).copyWith( + color: Theme.of(context) + .extension<StackColors>()! + .infoItemIcons, + ), + ), + ], + ), + ), + ], + ), + const SizedBox( + height: 4, + ), + SelectableText( + data, + style: STextStyles.itemSubtitle12(context), + ), + ], + ), + ); + } +} + +class _OrdinalImageGroup extends StatelessWidget { + const _OrdinalImageGroup({ + Key? key, + required this.walletId, + required this.ordinal, + }) : super(key: key); + + final String walletId; + final Ordinal ordinal; + + static const _spacing = 12.0; + + Future<String> _savePngToFile() async { + final response = await get(Uri.parse(ordinal.content)); + + if (response.statusCode != 200) { + throw Exception( + "statusCode=${response.statusCode} body=${response.bodyBytes}"); + } + + final bytes = response.bodyBytes; + + if (Platform.isAndroid) { + await Permission.storage.request(); + } + + final dir = Platform.isAndroid + ? Directory("/storage/emulated/0/Documents") + : await getApplicationDocumentsDirectory(); + + final docPath = dir.path; + final filePath = "$docPath/ordinal_${ordinal.inscriptionNumber}.png"; + + File imgFile = File(filePath); + + if (imgFile.existsSync()) { + throw Exception("File already exists"); + } + + await imgFile.writeAsBytes(bytes); + return filePath; + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // Text( + // "${ordinal.inscriptionId}", // Use any other property you want + // style: STextStyles.w600_16(context), + // ), + // const SizedBox( + // height: _spacing, + // ), + ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: AspectRatio( + aspectRatio: 1, + child: Container( + color: Colors.transparent, + child: Image.network( + ordinal.content, // Use the preview URL as the image source + fit: BoxFit.cover, + filterQuality: + FilterQuality.none, // Set the filter mode to nearest + ), + ), + ), + ), + const SizedBox( + height: _spacing, + ), + Row( + children: [ + Expanded( + child: SecondaryButton( + label: "Download", + icon: SvgPicture.asset( + Assets.svg.arrowDown, + width: 10, + height: 12, + color: Theme.of(context) + .extension<StackColors>()! + .buttonTextSecondary, + ), + buttonHeight: ButtonHeight.l, + iconSpacing: 4, + onPressed: () async { + bool didError = false; + final filePath = await showLoading<String>( + whileFuture: _savePngToFile(), + context: context, + isDesktop: true, + message: "Saving ordinal image", + onException: (e) { + didError = true; + String msg = e.toString(); + while (msg.isNotEmpty && msg.startsWith("Exception:")) { + msg = msg.substring(10).trim(); + } + showFloatingFlushBar( + type: FlushBarType.warning, + message: msg, + context: context, + ); + }, + ); + + if (!didError && context.mounted) { + await showFloatingFlushBar( + type: FlushBarType.success, + message: "Image saved to $filePath", + context: context, + ); + } + }, + ), + ), + // const SizedBox( + // width: _spacing, + // ), + // Expanded( + // child: PrimaryButton( + // label: "Send", + // icon: SvgPicture.asset( + // Assets.svg.send, + // width: 10, + // height: 10, + // color: Theme.of(context) + // .extension<StackColors>()! + // .buttonTextPrimary, + // ), + // buttonHeight: ButtonHeight.l, + // iconSpacing: 4, + // onPressed: () async { + // final response = await showDialog<String?>( + // context: context, + // builder: (_) => const SendOrdinalUnfreezeDialog(), + // ); + // if (response == "unfreeze") { + // // TODO: unfreeze and go to send ord screen + // } + // }, + // ), + // ), + ], + ), + ], + ); + } +} diff --git a/lib/pages/ordinals/ordinals_filter_view.dart b/lib/pages/ordinals/ordinals_filter_view.dart new file mode 100644 index 000000000..631a9833a --- /dev/null +++ b/lib/pages/ordinals/ordinals_filter_view.dart @@ -0,0 +1,889 @@ +/* + * 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:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_rounded_date_picker/flutter_rounded_date_picker.dart'; +import 'package:flutter_svg/svg.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/constants.dart'; +import 'package:stackwallet/utilities/format.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/background.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +import 'package:stackwallet/widgets/icon_widgets/x_icon.dart'; +import 'package:stackwallet/widgets/stack_text_field.dart'; +import 'package:stackwallet/widgets/textfield_icon_button.dart'; + +class OrdinalFilter { + // final bool isMoonbird; + // final bool isPunk; + final DateTime? from; + final DateTime? to; + final String? inscription; + final String keyword; + + OrdinalFilter({ + // required this.isMoonbird, + // required this.isPunk, + required this.from, + required this.to, + required this.inscription, + required this.keyword, + }); + + OrdinalFilter copyWith({ + // bool? isMoonbird, + // bool? isPunk, + DateTime? from, + DateTime? to, + String? inscription, + String? keyword, + }) { + return OrdinalFilter( + // isMoonbird: isMoonbird ?? this.isMoonbird, + // isPunk: isPunk ?? this.isPunk, + from: from ?? this.from, + to: to ?? this.to, + inscription: inscription ?? this.inscription, + keyword: keyword ?? this.keyword, + ); + } +} + +final ordinalFilterProvider = StateProvider<OrdinalFilter?>((_) => null); + +class OrdinalsFilterView extends ConsumerStatefulWidget { + const OrdinalsFilterView({ + Key? key, + }) : super(key: key); + + static const String routeName = "/ordinalsFilterView"; + + @override + ConsumerState<OrdinalsFilterView> createState() => _OrdinalsFilterViewState(); +} + +class _OrdinalsFilterViewState extends ConsumerState<OrdinalsFilterView> { + final _inscriptionTextEditingController = TextEditingController(); + final _keywordTextEditingController = TextEditingController(); + + // bool _isPunk = false; + // bool _isMoonbird = false; + + String _fromDateString = ""; + String _toDateString = ""; + + final keywordTextFieldFocusNode = FocusNode(); + final inscriptionTextFieldFocusNode = FocusNode(); + + late Color baseColor; + + @override + initState() { + baseColor = ref.read(themeProvider.state).state.textSubtitle2; + final filterState = ref.read(ordinalFilterProvider.state).state; + if (filterState != null) { + // _isMoonbird = filterState.isMoonbird; + // _isPunk = filterState.isPunk; + _selectedToDate = filterState.to; + _selectedFromDate = filterState.from; + _keywordTextEditingController.text = filterState.keyword; + _inscriptionTextEditingController.text = filterState.inscription ?? ""; + } + + super.initState(); + } + + @override + dispose() { + _inscriptionTextEditingController.dispose(); + _keywordTextEditingController.dispose(); + keywordTextFieldFocusNode.dispose(); + inscriptionTextFieldFocusNode.dispose(); + + super.dispose(); + } + + // The following two getters are not required if the + // date fields are to remain unclearable. + Widget get _dateFromText { + final isDateSelected = _fromDateString.isEmpty; + return Text( + isDateSelected ? "From..." : _fromDateString, + style: STextStyles.fieldLabel(context).copyWith( + color: isDateSelected + ? Theme.of(context).extension<StackColors>()!.textSubtitle2 + : Theme.of(context).extension<StackColors>()!.accentColorDark), + ); + } + + Widget get _dateToText { + final isDateSelected = _toDateString.isEmpty; + return Text( + isDateSelected ? "To..." : _toDateString, + style: STextStyles.fieldLabel(context).copyWith( + color: isDateSelected + ? Theme.of(context).extension<StackColors>()!.textSubtitle2 + : Theme.of(context).extension<StackColors>()!.accentColorDark), + ); + } + + DateTime? _selectedFromDate = DateTime(2007); + DateTime? _selectedToDate = DateTime.now(); + + MaterialRoundedDatePickerStyle _buildDatePickerStyle() { + return MaterialRoundedDatePickerStyle( + backgroundPicker: Theme.of(context).extension<StackColors>()!.popupBG, + // backgroundHeader: Theme.of(context).extension<StackColors>()!.textSubtitle2, + paddingMonthHeader: const EdgeInsets.only(top: 11), + colorArrowNext: Theme.of(context).extension<StackColors>()!.textSubtitle1, + colorArrowPrevious: + Theme.of(context).extension<StackColors>()!.textSubtitle1, + textStyleButtonNegative: STextStyles.datePicker600(context).copyWith( + color: baseColor, + ), + textStyleButtonPositive: STextStyles.datePicker600(context).copyWith( + color: baseColor, + ), + textStyleCurrentDayOnCalendar: STextStyles.datePicker400(context), + textStyleDayHeader: STextStyles.datePicker600(context), + textStyleDayOnCalendar: STextStyles.datePicker400(context).copyWith( + color: baseColor, + ), + textStyleDayOnCalendarDisabled: + STextStyles.datePicker400(context).copyWith( + color: Theme.of(context).extension<StackColors>()!.textSubtitle3, + ), + textStyleDayOnCalendarSelected: + STextStyles.datePicker400(context).copyWith( + color: Theme.of(context).extension<StackColors>()!.textWhite, + ), + textStyleMonthYearHeader: STextStyles.datePicker600(context).copyWith( + color: Theme.of(context).extension<StackColors>()!.textSubtitle1, + ), + textStyleYearButton: STextStyles.datePicker600(context).copyWith( + color: Theme.of(context).extension<StackColors>()!.textWhite, + ), + // textStyleButtonAction: GoogleFonts.inter(), + ); + } + + MaterialRoundedYearPickerStyle _buildYearPickerStyle() { + return MaterialRoundedYearPickerStyle( + backgroundPicker: Theme.of(context).extension<StackColors>()!.popupBG, + textStyleYear: STextStyles.datePicker600(context).copyWith( + color: Theme.of(context).extension<StackColors>()!.textSubtitle2, + fontSize: 16, + ), + textStyleYearSelected: STextStyles.datePicker600(context).copyWith( + fontSize: 18, + ), + ); + } + + Widget _buildDateRangePicker() { + const middleSeparatorPadding = 2.0; + const middleSeparatorWidth = 12.0; + final isDesktop = Util.isDesktop; + + final width = isDesktop + ? null + : (MediaQuery.of(context).size.width - + (middleSeparatorWidth + + (2 * middleSeparatorPadding) + + (2 * Constants.size.standardPadding))) / + 2; + + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: GestureDetector( + key: const Key("OrdinalsViewFromDatePickerKey"), + onTap: () async { + final color = + Theme.of(context).extension<StackColors>()!.accentColorDark; + final height = MediaQuery.of(context).size.height; + // check and hide keyboard + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future<void>.delayed(const Duration(milliseconds: 125)); + } + + if (mounted) { + final date = await showRoundedDatePicker( + // This doesn't change statusbar color... + // background: CFColors.starryNight.withOpacity(0.8), + context: context, + initialDate: DateTime.now(), + height: height * 0.5, + theme: ThemeData( + primarySwatch: Util.createMaterialColor( + color, + ), + ), + //TODO pick a better initial date + // 2007 chosen as that is just before bitcoin launched + firstDate: DateTime(2007), + lastDate: DateTime.now(), + borderRadius: Constants.size.circularBorderRadius * 2, + + textPositiveButton: "SELECT", + + styleDatePicker: _buildDatePickerStyle(), + styleYearPicker: _buildYearPickerStyle(), + ); + if (date != null) { + _selectedFromDate = date; + + // flag to adjust date so from date is always before to date + final flag = _selectedToDate != null && + !_selectedFromDate!.isBefore(_selectedToDate!); + if (flag) { + _selectedToDate = DateTime.fromMillisecondsSinceEpoch( + _selectedFromDate!.millisecondsSinceEpoch); + } + + setState(() { + if (flag) { + _toDateString = _selectedToDate == null + ? "" + : Format.formatDate(_selectedToDate!); + } + _fromDateString = _selectedFromDate == null + ? "" + : Format.formatDate(_selectedFromDate!); + }); + } + } + }, + child: Container( + width: width, + decoration: BoxDecoration( + color: Theme.of(context) + .extension<StackColors>()! + .textFieldDefaultBG, + borderRadius: + BorderRadius.circular(Constants.size.circularBorderRadius), + border: Border.all( + color: Theme.of(context) + .extension<StackColors>()! + .textFieldDefaultBG, + width: 1, + ), + ), + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: 12, + vertical: isDesktop ? 17 : 12, + ), + child: Row( + children: [ + SvgPicture.asset( + Assets.svg.calendar, + height: 20, + width: 20, + color: Theme.of(context) + .extension<StackColors>()! + .textSubtitle2, + ), + const SizedBox( + width: 10, + ), + Align( + alignment: Alignment.centerLeft, + child: FittedBox( + child: _dateFromText, + ), + ) + ], + ), + ), + ), + ), + ), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: middleSeparatorPadding), + child: Container( + width: middleSeparatorWidth, + // height: 1, + // color: CFColors.smoke, + ), + ), + Expanded( + child: GestureDetector( + key: const Key("OrdinalsViewToDatePickerKey"), + onTap: () async { + final color = + Theme.of(context).extension<StackColors>()!.accentColorDark; + final height = MediaQuery.of(context).size.height; + // check and hide keyboard + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future<void>.delayed(const Duration(milliseconds: 125)); + } + + if (mounted) { + final date = await showRoundedDatePicker( + // This doesn't change statusbar color... + // background: CFColors.starryNight.withOpacity(0.8), + context: context, + height: height * 0.5, + theme: ThemeData( + primarySwatch: Util.createMaterialColor( + color, + ), + ), + //TODO pick a better initial date + // 2007 chosen as that is just before bitcoin launched + initialDate: DateTime.now(), + firstDate: DateTime(2007), + lastDate: DateTime.now(), + borderRadius: Constants.size.circularBorderRadius * 2, + + textPositiveButton: "SELECT", + + styleDatePicker: _buildDatePickerStyle(), + styleYearPicker: _buildYearPickerStyle(), + ); + if (date != null) { + _selectedToDate = date; + + // flag to adjust date so from date is always before to date + final flag = _selectedFromDate != null && + !_selectedToDate!.isAfter(_selectedFromDate!); + if (flag) { + _selectedFromDate = DateTime.fromMillisecondsSinceEpoch( + _selectedToDate!.millisecondsSinceEpoch); + } + + setState(() { + if (flag) { + _fromDateString = _selectedFromDate == null + ? "" + : Format.formatDate(_selectedFromDate!); + } + _toDateString = _selectedToDate == null + ? "" + : Format.formatDate(_selectedToDate!); + }); + } + } + }, + child: Container( + width: width, + decoration: BoxDecoration( + color: Theme.of(context) + .extension<StackColors>()! + .textFieldDefaultBG, + borderRadius: + BorderRadius.circular(Constants.size.circularBorderRadius), + border: Border.all( + color: Theme.of(context) + .extension<StackColors>()! + .textFieldDefaultBG, + width: 1, + ), + ), + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: 12, + vertical: isDesktop ? 17 : 12, + ), + child: Row( + children: [ + SvgPicture.asset( + Assets.svg.calendar, + height: 20, + width: 20, + color: Theme.of(context) + .extension<StackColors>()! + .textSubtitle2, + ), + const SizedBox( + width: 10, + ), + Align( + alignment: Alignment.centerLeft, + child: FittedBox( + child: _dateToText, + ), + ) + ], + ), + ), + ), + ), + ), + if (isDesktop) + const SizedBox( + width: 24, + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + if (Util.isDesktop) { + return DesktopDialog( + maxWidth: 576, + maxHeight: double.infinity, + child: Padding( + padding: const EdgeInsets.only( + left: 32, + bottom: 32, + ), + child: _buildContent(context), + ), + ); + } else { + return Background( + child: Scaffold( + backgroundColor: + Theme.of(context).extension<StackColors>()!.background, + appBar: AppBar( + backgroundColor: + Theme.of(context).extension<StackColors>()!.background, + leading: AppBarBackButton( + onPressed: () async { + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future<void>.delayed(const Duration(milliseconds: 75)); + } + if (mounted) { + Navigator.of(context).pop(); + } + }, + ), + title: Text( + "Ordinals filter", + style: STextStyles.navBarTitle(context), + ), + ), + body: Padding( + padding: EdgeInsets.symmetric( + horizontal: Constants.size.standardPadding, + ), + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: + BoxConstraints(minHeight: constraints.maxHeight), + child: IntrinsicHeight( + child: _buildContent(context), + ), + ), + ); + }, + ), + ), + ), + ); + } + } + + Widget _buildContent(BuildContext context) { + final isDesktop = Util.isDesktop; + + return Column( + children: [ + if (isDesktop) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Ordinals filter", + style: STextStyles.desktopH3(context), + textAlign: TextAlign.center, + ), + const DesktopDialogCloseButton(), + ], + ), + SizedBox( + height: isDesktop ? 14 : 10, + ), + // if (!isDesktop) + // Align( + // alignment: Alignment.centerLeft, + // child: FittedBox( + // child: Text( + // "Collection", + // style: STextStyles.smallMed12(context), + // ), + // ), + // ), + // if (!isDesktop) + // const SizedBox( + // height: 12, + // ), + // RoundedWhiteContainer( + // padding: EdgeInsets.all(isDesktop ? 0 : 12), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Row( + // children: [ + // GestureDetector( + // onTap: () { + // setState(() { + // _isPunk = !_isPunk; + // }); + // }, + // child: Container( + // color: Colors.transparent, + // child: Row( + // children: [ + // SizedBox( + // height: 20, + // width: 20, + // child: Checkbox( + // key: const Key("OrdinalsPunkCheckboxKey"), + // materialTapTargetSize: + // MaterialTapTargetSize.shrinkWrap, + // value: _isPunk, + // onChanged: (newValue) { + // setState(() { + // _isPunk = newValue!; + // }); + // }, + // ), + // ), + // const SizedBox( + // width: 14, + // ), + // Align( + // alignment: Alignment.centerLeft, + // child: FittedBox( + // child: Column( + // children: [ + // Text( + // "Punks", + // style: isDesktop + // ? STextStyles.desktopTextSmall(context) + // : STextStyles.itemSubtitle12(context), + // ), + // if (isDesktop) + // const SizedBox( + // height: 4, + // ), + // ], + // ), + // ), + // ) + // ], + // ), + // ), + // ), + // ], + // ), + // SizedBox( + // height: isDesktop ? 4 : 10, + // ), + // Row( + // children: [ + // GestureDetector( + // onTap: () { + // setState(() { + // _isMoonbird = !_isMoonbird; + // }); + // }, + // child: Container( + // color: Colors.transparent, + // child: Row( + // children: [ + // SizedBox( + // height: 20, + // width: 20, + // child: Checkbox( + // key: const Key( + // "OrdinalsFilterMoonbirdCheckboxKey", + // ), + // materialTapTargetSize: + // MaterialTapTargetSize.shrinkWrap, + // value: _isMoonbird, + // onChanged: (newValue) { + // setState(() { + // _isMoonbird = newValue!; + // }); + // }, + // ), + // ), + // const SizedBox( + // width: 14, + // ), + // Align( + // alignment: Alignment.centerLeft, + // child: FittedBox( + // child: Column( + // children: [ + // Text( + // "Moonbirds", + // style: isDesktop + // ? STextStyles.desktopTextSmall(context) + // : STextStyles.itemSubtitle12(context), + // ), + // if (isDesktop) + // const SizedBox( + // height: 4, + // ), + // ], + // ), + // ), + // ) + // ], + // ), + // ), + // ), + // ], + // ), + // ], + // ), + // ), + // SizedBox( + // height: isDesktop ? 32 : 24, + // ), + Align( + alignment: Alignment.centerLeft, + child: FittedBox( + child: Text( + "Date", + style: isDesktop + ? STextStyles.labelExtraExtraSmall(context) + : STextStyles.smallMed12(context), + ), + ), + ), + SizedBox( + height: isDesktop ? 10 : 8, + ), + _buildDateRangePicker(), + SizedBox( + height: isDesktop ? 32 : 24, + ), + Align( + alignment: Alignment.centerLeft, + child: FittedBox( + child: Text( + "Inscription", + style: isDesktop + ? STextStyles.labelExtraExtraSmall(context) + : STextStyles.smallMed12(context), + ), + ), + ), + SizedBox( + height: isDesktop ? 10 : 8, + ), + Padding( + padding: EdgeInsets.only(right: isDesktop ? 32 : 0), + child: ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + autocorrect: Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop ? false : true, + key: const Key("OrdinalsInscriptionFieldKey"), + controller: _inscriptionTextEditingController, + focusNode: inscriptionTextFieldFocusNode, + onChanged: (_) => setState(() {}), + style: isDesktop + ? STextStyles.desktopTextExtraSmall(context).copyWith( + color: + Theme.of(context).extension<StackColors>()!.textDark, + height: 1.8, + ) + : STextStyles.field(context), + decoration: standardInputDecoration( + "Enter inscription number...", + keywordTextFieldFocusNode, + context, + desktopMed: isDesktop, + ).copyWith( + contentPadding: isDesktop + ? const EdgeInsets.symmetric( + vertical: 10, + horizontal: 16, + ) + : null, + suffixIcon: _inscriptionTextEditingController.text.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + _inscriptionTextEditingController.text = ""; + }); + }, + ), + ], + ), + ), + ) + : null, + ), + ), + ), + ), + SizedBox( + height: isDesktop ? 32 : 24, + ), + Align( + alignment: Alignment.centerLeft, + child: FittedBox( + child: Text( + "Keyword", + style: isDesktop + ? STextStyles.labelExtraExtraSmall(context) + : STextStyles.smallMed12(context), + ), + ), + ), + SizedBox( + height: isDesktop ? 10 : 8, + ), + Padding( + padding: EdgeInsets.only(right: isDesktop ? 32 : 0), + child: ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + autocorrect: Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop ? false : true, + key: const Key("OrdinalsViewKeywordFieldKey"), + controller: _keywordTextEditingController, + focusNode: keywordTextFieldFocusNode, + style: isDesktop + ? STextStyles.desktopTextExtraSmall(context).copyWith( + color: + Theme.of(context).extension<StackColors>()!.textDark, + height: 1.8, + ) + : STextStyles.field(context), + onChanged: (_) => setState(() {}), + decoration: standardInputDecoration( + "Type keyword...", + keywordTextFieldFocusNode, + context, + desktopMed: isDesktop, + ).copyWith( + contentPadding: isDesktop + ? const EdgeInsets.symmetric( + vertical: 10, + horizontal: 16, + ) + : null, + suffixIcon: _keywordTextEditingController.text.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + _keywordTextEditingController.text = ""; + }); + }, + ), + ], + ), + ), + ) + : null, + ), + ), + ), + ), + if (!isDesktop) const Spacer(), + SizedBox( + height: isDesktop ? 32 : 20, + ), + Row( + children: [ + Expanded( + child: SecondaryButton( + label: "Cancel", + buttonHeight: isDesktop ? ButtonHeight.l : null, + onPressed: () async { + if (!isDesktop) { + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future<void>.delayed( + const Duration( + milliseconds: 75, + ), + ); + } + } + if (mounted) { + Navigator.of(context).pop(); + } + }, + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: PrimaryButton( + buttonHeight: isDesktop ? ButtonHeight.l : null, + onPressed: () async { + await _onApplyPressed(); + }, + label: "Save", + ), + ), + if (isDesktop) + const SizedBox( + width: 32, + ), + ], + ), + if (!isDesktop) + const SizedBox( + height: 20, + ), + ], + ); + } + + Future<void> _onApplyPressed() async { + final filter = OrdinalFilter( + // isPunk: _isPunk, + // isMoonbird: _isMoonbird, + from: _selectedFromDate, + to: _selectedToDate, + inscription: _inscriptionTextEditingController.text, + keyword: _keywordTextEditingController.text, + ); + + ref.read(ordinalFilterProvider.state).state = filter; + + Navigator.of(context).pop(); + } +} diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart new file mode 100644 index 000000000..45b30655a --- /dev/null +++ b/lib/pages/ordinals/ordinals_view.dart @@ -0,0 +1,202 @@ +/* + * 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:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/pages/ordinals/widgets/ordinals_list.dart'; +import 'package:stackwallet/providers/global/wallets_provider.dart'; +import 'package:stackwallet/services/mixins/ordinals_interface.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/show_loading.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/background.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; + +class OrdinalsView extends ConsumerStatefulWidget { + const OrdinalsView({ + super.key, + required this.walletId, + }); + + static const routeName = "/ordinalsView"; + + final String walletId; + + @override + ConsumerState<OrdinalsView> createState() => _OrdinalsViewState(); +} + +class _OrdinalsViewState extends ConsumerState<OrdinalsView> { + late final TextEditingController searchController; + late final FocusNode searchFocus; + + String _searchTerm = ""; + + @override + void initState() { + searchController = TextEditingController(); + searchFocus = FocusNode(); + + super.initState(); + } + + @override + void dispose() { + searchController.dispose(); + searchFocus.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Background( + child: SafeArea( + child: Scaffold( + backgroundColor: + Theme.of(context).extension<StackColors>()!.background, + appBar: AppBar( + automaticallyImplyLeading: false, + leading: const AppBarBackButton(), + title: Text( + "Ordinals", + style: STextStyles.navBarTitle(context), + ), + titleSpacing: 0, + actions: [ + AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + size: 36, + icon: SvgPicture.asset( + Assets.svg.arrowRotate, + width: 20, + height: 20, + color: Theme.of(context) + .extension<StackColors>()! + .topNavIconPrimary, + ), + onPressed: () async { + // show loading for a minimum of 2 seconds on refreshing + await showLoading( + whileFuture: Future.wait<void>([ + Future.delayed(const Duration(seconds: 2)), + (ref + .read(walletsChangeNotifierProvider) + .getManager(widget.walletId) + .wallet as OrdinalsInterface) + .refreshInscriptions() + ]), + context: context, + message: "Refreshing...", + ); + }, + ), + ), + // AspectRatio( + // aspectRatio: 1, + // child: AppBarIconButton( + // size: 36, + // icon: SvgPicture.asset( + // Assets.svg.filter, + // width: 20, + // height: 20, + // color: Theme.of(context) + // .extension<StackColors>()! + // .topNavIconPrimary, + // ), + // onPressed: () { + // Navigator.of(context).pushNamed( + // OrdinalsFilterView.routeName, + // ); + // }, + // ), + // ), + ], + ), + body: Padding( + padding: const EdgeInsets.only( + left: 16, + right: 16, + top: 8, + ), + child: Column( + children: [ + // ClipRRect( + // borderRadius: BorderRadius.circular( + // Constants.size.circularBorderRadius, + // ), + // child: TextField( + // autocorrect: Util.isDesktop ? false : true, + // enableSuggestions: Util.isDesktop ? false : true, + // controller: searchController, + // focusNode: searchFocus, + // onChanged: (value) { + // setState(() { + // _searchTerm = value; + // }); + // }, + // style: STextStyles.field(context), + // decoration: standardInputDecoration( + // "Search", + // searchFocus, + // context, + // ).copyWith( + // prefixIcon: Padding( + // padding: const EdgeInsets.symmetric( + // horizontal: 10, + // vertical: 16, + // ), + // child: SvgPicture.asset( + // Assets.svg.search, + // width: 16, + // height: 16, + // ), + // ), + // suffixIcon: searchController.text.isNotEmpty + // ? Padding( + // padding: const EdgeInsets.only(right: 0), + // child: UnconstrainedBox( + // child: Row( + // children: [ + // TextFieldIconButton( + // child: const XIcon(), + // onTap: () async { + // setState(() { + // searchController.text = ""; + // _searchTerm = ""; + // }); + // }, + // ), + // ], + // ), + // ), + // ) + // : null, + // ), + // ), + // ), + // const SizedBox( + // height: 16, + // ), + Expanded( + child: OrdinalsList( + walletId: widget.walletId, + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/ordinals/widgets/dialogs.dart b/lib/pages/ordinals/widgets/dialogs.dart new file mode 100644 index 000000000..ee5b57d33 --- /dev/null +++ b/lib/pages/ordinals/widgets/dialogs.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +import 'package:stackwallet/widgets/stack_dialog.dart'; + +class SendOrdinalUnfreezeDialog extends StatelessWidget { + const SendOrdinalUnfreezeDialog({super.key}); + + @override + Widget build(BuildContext context) { + return StackDialog( + title: "This ordinal is frozen", + icon: SvgPicture.asset( + Assets.svg.coinControl.blocked, + width: 24, + height: 24, + color: Theme.of(context).extension<StackColors>()!.textDark, + ), + message: "To send this ordinal, you must unfreeze it first.", + leftButton: SecondaryButton( + label: "Cancel", + onPressed: Navigator.of(context).pop, + ), + rightButton: PrimaryButton( + label: "Unfreeze", + onPressed: () { + Navigator.of(context).pop("unfreeze"); + }, + ), + ); + } +} + +class UnfreezeOrdinalDialog extends StatelessWidget { + const UnfreezeOrdinalDialog({super.key}); + + @override + Widget build(BuildContext context) { + return StackDialog( + title: "Are you sure you want to unfreeze this ordinal?", + icon: SvgPicture.asset( + Assets.svg.coinControl.blocked, + width: 24, + height: 24, + color: Theme.of(context).extension<StackColors>()!.textDark, + ), + leftButton: SecondaryButton( + label: "Cancel", + onPressed: Navigator.of(context).pop, + ), + rightButton: PrimaryButton( + label: "Unfreeze", + onPressed: () { + Navigator.of(context).pop("unfreeze"); + }, + ), + ); + } +} diff --git a/lib/pages/ordinals/widgets/ordinal_card.dart b/lib/pages/ordinals/widgets/ordinal_card.dart new file mode 100644 index 000000000..c74366d74 --- /dev/null +++ b/lib/pages/ordinals/widgets/ordinal_card.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'package:stackwallet/models/isar/ordinal.dart'; +import 'package:stackwallet/pages/ordinals/ordinal_details_view.dart'; +import 'package:stackwallet/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +class OrdinalCard extends StatelessWidget { + const OrdinalCard({ + Key? key, + required this.walletId, + required this.ordinal, + }) : super(key: key); + + final String walletId; + final Ordinal ordinal; + + @override + Widget build(BuildContext context) { + return RoundedWhiteContainer( + radiusMultiplier: 2, + onPressed: () { + Navigator.of(context).pushNamed( + Util.isDesktop + ? DesktopOrdinalDetailsView.routeName + : OrdinalDetailsView.routeName, + arguments: (walletId: walletId, ordinal: ordinal), + ); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + AspectRatio( + aspectRatio: 1, + child: ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: Image.network( + ordinal.content, // Use the preview URL as the image source + fit: BoxFit.cover, + filterQuality: + FilterQuality.none, // Set the filter mode to nearest + ), + ), + ), + const Spacer(), + Text( + 'INSC. ${ordinal.inscriptionNumber}', // infer from address associated with utxoTXID + style: STextStyles.w500_12(context), + ), + // const Spacer(), + // Text( + // "ID ${ordinal.inscriptionId}", + // style: STextStyles.w500_8(context), + // ), + ], + ), + ); + } +} diff --git a/lib/pages/ordinals/widgets/ordinals_list.dart b/lib/pages/ordinals/widgets/ordinals_list.dart new file mode 100644 index 000000000..481b0ef0a --- /dev/null +++ b/lib/pages/ordinals/widgets/ordinals_list.dart @@ -0,0 +1,119 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:isar/isar.dart'; +import 'package:stackwallet/models/isar/ordinal.dart'; +import 'package:stackwallet/pages/ordinals/widgets/ordinal_card.dart'; +import 'package:stackwallet/providers/db/main_db_provider.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +class OrdinalsList extends ConsumerStatefulWidget { + const OrdinalsList({ + Key? key, + required this.walletId, + }) : super(key: key); + + final String walletId; + + @override + ConsumerState<OrdinalsList> createState() => _OrdinalsListState(); +} + +class _OrdinalsListState extends ConsumerState<OrdinalsList> { + final double _spacing = Util.isDesktop ? 16 : 10; + + late List<Ordinal> _data; + + late final Stream<List<Ordinal>?> _stream; + + @override + void initState() { + _stream = ref + .read(mainDBProvider) + .isar + .ordinals + .where() + .filter() + .walletIdEqualTo(widget.walletId) + .watch(); + + _data = ref + .read(mainDBProvider) + .isar + .ordinals + .where() + .filter() + .walletIdEqualTo(widget.walletId) + .findAllSync(); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return StreamBuilder<List<Ordinal>?>( + stream: _stream, + builder: (context, snapshot) { + if (snapshot.hasData) { + _data = snapshot.data!; + } + + if (_data.isEmpty) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + RoundedWhiteContainer( + child: Center( + child: Text( + "Your ordinals will appear here", + style: Util.isDesktop + ? STextStyles.w500_14(context).copyWith( + color: Theme.of(context) + .extension<StackColors>()! + .textSubtitle1) + : STextStyles.label(context), + ), + ), + ), + ], + ); + } + + if (Util.isDesktop) { + return Wrap( + spacing: _spacing, + runSpacing: _spacing, + children: _data + .map((e) => SizedBox( + width: 220, + height: 270, + child: OrdinalCard( + walletId: widget.walletId, + ordinal: e, + ))) + .toList(), + ); + } else { + return GridView.builder( + shrinkWrap: true, + itemCount: _data.length, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisSpacing: _spacing, + mainAxisSpacing: _spacing, + crossAxisCount: Util.isDesktop ? 4 : 2, + childAspectRatio: 6 / 7, // was 3/4, less data displayed now + ), + itemBuilder: (_, i) => OrdinalCard( + walletId: widget.walletId, + ordinal: _data[i], + ), + ); + } + }, + ); + } +} diff --git a/lib/pages/send_view/confirm_transaction_view.dart b/lib/pages/send_view/confirm_transaction_view.dart index 3193e7bb7..152437c23 100644 --- a/lib/pages/send_view/confirm_transaction_view.dart +++ b/lib/pages/send_view/confirm_transaction_view.dart @@ -89,6 +89,11 @@ class _ConfirmTransactionViewState late final FocusNode _noteFocusNode; late final TextEditingController noteController; + + late final FocusNode _onChainNoteFocusNode; + late final TextEditingController onChainNoteController; + + Future<void> _attemptSend(BuildContext context) async { final manager = ref.read(walletsChangeNotifierProvider).getManager(walletId); @@ -138,6 +143,9 @@ class _ConfirmTransactionViewState txidFuture = (manager.wallet as FiroWallet) .confirmSendPublic(txData: transactionInfo); } else { + if (coin == Coin.epicCash) { + transactionInfo["onChainNote"] = onChainNoteController.text; + } txidFuture = manager.confirmSend(txData: transactionInfo); } } @@ -272,14 +280,21 @@ class _ConfirmTransactionViewState _noteFocusNode = FocusNode(); noteController = TextEditingController(); noteController.text = transactionInfo["note"] as String? ?? ""; + + _onChainNoteFocusNode = FocusNode(); + onChainNoteController = TextEditingController(); + onChainNoteController.text = transactionInfo["onChainNote"] as String? ?? ""; + super.initState(); } @override void dispose() { noteController.dispose(); + onChainNoteController.dispose(); _noteFocusNode.dispose(); + _onChainNoteFocusNode.dispose(); super.dispose(); } @@ -493,51 +508,54 @@ class _ConfirmTransactionViewState ], ), ), - if (coin == Coin.epicCash) + if (coin == Coin.epicCash && + (transactionInfo["onChainNote"] as String).isNotEmpty) const SizedBox( height: 12, ), - if (coin == Coin.epicCash) + 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), - ), - ], + 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), + ), + ], + ), ), - ), - const SizedBox( - height: 12, - ), - 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 ((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) @@ -837,8 +855,64 @@ class _ConfirmTransactionViewState mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ + 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( - "Note (optional)", + (coin == Coin.epicCash) ? "Local Note (optional)" + : "Note (optional)", style: STextStyles.desktopTextExtraSmall(context).copyWith( color: Theme.of(context) diff --git a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/install_theme_from_file_dialog.dart b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/install_theme_from_file_dialog.dart index 235bb5e95..812fcc94b 100644 --- a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/install_theme_from_file_dialog.dart +++ b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/install_theme_from_file_dialog.dart @@ -164,7 +164,7 @@ class _InstallThemeFromFileDialogState ); if (mounted) { Navigator.of(context).pop(); - if (!result) { + if (!result!) { unawaited( showDialog( context: context, diff --git a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart index 0c45a68f7..003063bde 100644 --- a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart +++ b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart @@ -72,11 +72,11 @@ class _StackThemeCardState extends ConsumerState<StackThemeCard> { } Future<void> _downloadPressed() async { - final result = await showLoading( + final result = (await showLoading( whileFuture: _downloadAndInstall(), context: context, message: "Downloading and installing theme...", - ); + ))!; if (mounted) { final message = result diff --git a/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart b/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart index 3e5d3e3e4..56b008093 100644 --- a/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart +++ b/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart @@ -196,7 +196,10 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> { case Coin.nano: case Coin.banano: - //TODO: check network/node + case Coin.stellar: + case Coin.stellarTestnet: + throw UnimplementedError(); + //TODO: check network/node } if (showFlushBar && mounted) { @@ -736,6 +739,8 @@ class _NodeFormState extends ConsumerState<NodeForm> { case Coin.nano: case Coin.banano: case Coin.eCash: + case Coin.stellar: + case Coin.stellarTestnet: return false; case Coin.ethereum: diff --git a/lib/pages/settings_views/global_settings_view/manage_nodes_views/node_details_view.dart b/lib/pages/settings_views/global_settings_view/manage_nodes_views/node_details_view.dart index fa69871cb..59ff1efad 100644 --- a/lib/pages/settings_views/global_settings_view/manage_nodes_views/node_details_view.dart +++ b/lib/pages/settings_views/global_settings_view/manage_nodes_views/node_details_view.dart @@ -172,7 +172,10 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> { case Coin.nano: case Coin.banano: - //TODO: check network/node + case Coin.stellar: + case Coin.stellarTestnet: + throw UnimplementedError(); + //TODO: check network/node } if (testPassed) { diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart index 59f443db9..fcacc60d4 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart @@ -20,11 +20,12 @@ import 'package:stackwallet/pages/home_view/home_view.dart'; import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/advanced_views/debug_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_preferences_view.dart'; -import 'package:stackwallet/pages/settings_views/global_settings_view/xpub_view.dart'; import 'package:stackwallet/pages/settings_views/sub_widgets/settings_list_button.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart'; import 'package:stackwallet/providers/global/wallets_provider.dart'; import 'package:stackwallet/providers/ui/transaction_filter_provider.dart'; import 'package:stackwallet/route_generator.dart'; @@ -231,7 +232,7 @@ class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> { .mnemonic; if (mounted) { - Navigator.push( + await Navigator.push( context, RouteGenerator.getRoute( shouldUseMaterialRoute: @@ -305,6 +306,25 @@ class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> { ); }, ), + if (coin == Coin.nano || coin == Coin.banano) + const SizedBox( + height: 8, + ), + if (coin == Coin.nano || coin == Coin.banano) + Consumer( + builder: (_, ref, __) { + return SettingsListButton( + iconAssetName: Assets.svg.eye, + title: "Change representative", + onPressed: () { + Navigator.of(context).pushNamed( + ChangeRepresentativeView.routeName, + arguments: widget.walletId, + ); + }, + ); + }, + ), const SizedBox( height: 8, ), @@ -434,18 +454,20 @@ class _EpiBoxInfoFormState extends ConsumerState<EpicBoxInfoForm> { TextButton( onPressed: () async { try { - wallet.updateEpicboxConfig( + await wallet.updateEpicboxConfig( hostController.text, int.parse(portController.text), ); - showFloatingFlushBar( - context: context, - message: "Epicbox info saved!", - type: FlushBarType.success, - ); - wallet.refresh(); + if (mounted) { + await showFloatingFlushBar( + context: context, + message: "Epicbox info saved!", + type: FlushBarType.success, + ); + } + unawaited(wallet.refresh()); } catch (e) { - showFloatingFlushBar( + await showFloatingFlushBar( context: context, message: "Failed to save epicbox info: $e", type: FlushBarType.warning, diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart new file mode 100644 index 000000000..a91b0cb4e --- /dev/null +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart @@ -0,0 +1,402 @@ +/* + * 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 'dart:async'; + +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:stackwallet/notifications/show_flush_bar.dart'; +import 'package:stackwallet/providers/global/wallets_provider.dart'; +import 'package:stackwallet/services/coins/banano/banano_wallet.dart'; +import 'package:stackwallet/services/coins/nano/nano_wallet.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/clipboard_interface.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/show_loading.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/background.dart'; +import 'package:stackwallet/widgets/conditional_parent.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/icon_widgets/x_icon.dart'; +import 'package:stackwallet/widgets/loading_indicator.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; +import 'package:stackwallet/widgets/stack_text_field.dart'; +import 'package:stackwallet/widgets/textfield_icon_button.dart'; + +class ChangeRepresentativeView extends ConsumerStatefulWidget { + const ChangeRepresentativeView({ + Key? key, + required this.walletId, + this.clipboardInterface = const ClipboardWrapper(), + }) : super(key: key); + + final String walletId; + final ClipboardInterface clipboardInterface; + + static const String routeName = "/changeRepresentative"; + + @override + ConsumerState<ChangeRepresentativeView> createState() => _XPubViewState(); +} + +class _XPubViewState extends ConsumerState<ChangeRepresentativeView> { + final _textController = TextEditingController(); + final _textFocusNode = FocusNode(); + final bool isDesktop = Util.isDesktop; + + late ClipboardInterface _clipboardInterface; + + String? representative; + + Future<String> loadRepresentative() async { + final manager = + ref.read(walletsChangeNotifierProvider).getManager(widget.walletId); + + if (manager.coin == Coin.nano) { + return (manager.wallet as NanoWallet).getCurrentRepresentative(); + } else if (manager.coin == Coin.banano) { + return (manager.wallet as BananoWallet).getCurrentRepresentative(); + } + throw Exception("Unsupported wallet attempted to show representative!"); + } + + Future<void> _save() async { + final manager = + ref.read(walletsChangeNotifierProvider).getManager(widget.walletId); + + final changeFuture = manager.coin == Coin.nano + ? (manager.wallet as NanoWallet).changeRepresentative + : (manager.wallet as BananoWallet).changeRepresentative; + + final result = await showLoading( + whileFuture: changeFuture(_textController.text), + context: context, + message: "Updating representative...", + isDesktop: Util.isDesktop, + onException: (ex) { + String msg = ex.toString(); + while (msg.isNotEmpty && msg.startsWith("Exception:")) { + msg = msg.substring(10).trim(); + } + showFloatingFlushBar( + type: FlushBarType.warning, + message: msg, + context: context, + ); + }); + + if (mounted) { + if (result != null && result) { + setState(() { + representative = _textController.text; + _textController.text = ""; + }); + await showFloatingFlushBar( + type: FlushBarType.success, + message: "Representative changed", + context: context, + ); + } + } + } + + @override + void initState() { + _clipboardInterface = widget.clipboardInterface; + + super.initState(); + } + + @override + void dispose() { + _textController.dispose(); + _textFocusNode.dispose(); + super.dispose(); + } + + Future<void> _copy() async { + await _clipboardInterface + .setData(ClipboardData(text: representative ?? "")); + if (mounted) { + unawaited(showFloatingFlushBar( + type: FlushBarType.info, + message: "Copied to clipboard", + iconAsset: Assets.svg.copy, + context: context, + )); + } + } + + @override + Widget build(BuildContext context) { + return ConditionalParent( + condition: !isDesktop, + builder: (child) => Background( + child: SafeArea( + child: Scaffold( + backgroundColor: + Theme.of(context).extension<StackColors>()!.background, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () async { + Navigator.of(context).pop(); + }, + ), + title: Text( + "Wallet representative", + style: STextStyles.navBarTitle(context), + ), + actions: [ + Padding( + padding: const EdgeInsets.all(10), + child: AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + color: Theme.of(context) + .extension<StackColors>()! + .background, + shadows: const [], + icon: SvgPicture.asset( + Assets.svg.copy, + width: 24, + height: 24, + color: Theme.of(context) + .extension<StackColors>()! + .topNavIconPrimary, + ), + onPressed: () { + if (representative != null) { + _copy(); + } + }, + ), + ), + ), + ], + ), + body: Padding( + padding: const EdgeInsets.only( + top: 12, + left: 16, + right: 16, + ), + child: child, + ), + ), + ), + ), + child: ConditionalParent( + condition: isDesktop, + builder: (child) => DesktopDialog( + maxWidth: 600, + maxHeight: double.infinity, + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only( + left: 32, + ), + child: Text( + "Change representative", + style: STextStyles.desktopH2(context), + ), + ), + DesktopDialogCloseButton( + onPressedOverride: Navigator.of( + context, + rootNavigator: true, + ).pop, + ), + ], + ), + AnimatedSize( + duration: const Duration( + milliseconds: 150, + ), + child: Padding( + padding: const EdgeInsets.fromLTRB(32, 0, 32, 32), + child: child, + ), + ), + ], + ), + ), + child: Column( + children: [ + if (isDesktop) const SizedBox(height: 24), + ConditionalParent( + condition: !isDesktop, + builder: (child) => Expanded( + child: child, + ), + child: FutureBuilder( + future: loadRepresentative(), + builder: (context, AsyncSnapshot<String> snapshot) { + if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData) { + representative = snapshot.data!; + } + + const height = 600.0; + Widget child; + if (representative == null) { + child = const SizedBox( + key: Key("loadingRepresentative"), + height: height, + child: Center( + child: LoadingIndicator( + width: 100, + ), + ), + ); + } else { + child = Column( + children: [ + ConditionalParent( + condition: !isDesktop, + builder: (child) => RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + child, + ], + ), + ), + child: ConditionalParent( + condition: isDesktop, + builder: (child) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Current representative", + style: STextStyles.desktopTextExtraExtraSmall( + context), + ), + const SizedBox( + height: 4, + ), + Row( + children: [ + child, + ], + ), + ], + ), + child: SelectableText( + representative!, + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context) + .copyWith( + color: Theme.of(context) + .extension<StackColors>()! + .textDark, + ) + : STextStyles.itemSubtitle12(context), + ), + ), + ), + const SizedBox( + height: 24, + ), + ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + autocorrect: Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop ? false : true, + controller: _textController, + style: isDesktop + ? STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension<StackColors>()! + .textFieldActiveText, + height: 1.8, + ) + : STextStyles.field(context), + focusNode: _textFocusNode, + decoration: standardInputDecoration( + "Enter new representative", + _textFocusNode, + context, + desktopMed: isDesktop, + ).copyWith( + contentPadding: isDesktop + ? const EdgeInsets.only( + left: 16, + top: 11, + bottom: 12, + right: 5, + ) + : null, + suffixIcon: _textController.text.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + _textController.text = ""; + }); + }, + ), + ], + ), + ), + ) + : null, + ), + ), + ), + if (isDesktop) const SizedBox(height: 60), + if (!isDesktop) const Spacer(), + PrimaryButton( + label: "Save", + onPressed: _save, + ), + if (!isDesktop) + const SizedBox( + height: 16, + ), + ], + ); + } + + return AnimatedSwitcher( + duration: const Duration( + milliseconds: 200, + ), + child: child, + ); + }, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/settings_views/global_settings_view/xpub_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart similarity index 100% rename from lib/pages/settings_views/global_settings_view/xpub_view.dart rename to lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart diff --git a/lib/pages/special/firo_rescan_recovery_error_dialog.dart b/lib/pages/special/firo_rescan_recovery_error_dialog.dart new file mode 100644 index 000000000..a543eb6bc --- /dev/null +++ b/lib/pages/special/firo_rescan_recovery_error_dialog.dart @@ -0,0 +1,307 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_warning_view.dart'; +import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_delete_wallet_dialog.dart'; +import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart'; +import 'package:stackwallet/providers/global/wallets_provider.dart'; +import 'package:stackwallet/route_generator.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/background.dart'; +import 'package:stackwallet/widgets/conditional_parent.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/desktop/desktop_app_bar.dart'; +import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +import 'package:stackwallet/widgets/stack_dialog.dart'; +import 'package:tuple/tuple.dart'; + +enum FiroRescanRecoveryErrorViewOption { + retry, + showMnemonic, + deleteWallet; +} + +class FiroRescanRecoveryErrorView extends ConsumerStatefulWidget { + const FiroRescanRecoveryErrorView({ + super.key, + required this.walletId, + }); + + static const String routeName = "/firoRescanRecoveryErrorView"; + + final String walletId; + + @override + ConsumerState<FiroRescanRecoveryErrorView> createState() => + _FiroRescanRecoveryErrorViewState(); +} + +class _FiroRescanRecoveryErrorViewState + extends ConsumerState<FiroRescanRecoveryErrorView> { + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async => false, + child: ConditionalParent( + condition: Util.isDesktop, + builder: (child) { + return DesktopScaffold( + appBar: DesktopAppBar( + background: Theme.of(context).extension<StackColors>()!.popupBG, + isCompactHeight: true, + // useSpacers: false, + trailing: Padding( + padding: const EdgeInsets.only(right: 16), + child: CustomTextButton( + text: "Delete wallet", + onTap: () async { + final result = await showDialog<bool?>( + context: context, + barrierDismissible: false, + builder: (context) => Navigator( + initialRoute: DesktopDeleteWalletDialog.routeName, + onGenerateRoute: RouteGenerator.generateRoute, + onGenerateInitialRoutes: (_, __) { + return [ + RouteGenerator.generateRoute( + RouteSettings( + name: DesktopDeleteWalletDialog.routeName, + arguments: widget.walletId, + ), + ), + ]; + }, + ), + ); + + if (result == true) { + if (context.mounted) { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + } + } + }, + ), + ), + ), + body: SizedBox(width: 328, child: child), + ); + }, + child: ConditionalParent( + condition: !Util.isDesktop, + builder: (child) { + return Background( + child: Scaffold( + backgroundColor: + Theme.of(context).extension<StackColors>()!.background, + appBar: AppBar( + automaticallyImplyLeading: false, + actions: [ + Padding( + padding: const EdgeInsets.only( + top: 10, + bottom: 10, + right: 10, + ), + child: AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + semanticsLabel: "Delete wallet button. " + "Start process of deleting current wallet.", + key: const Key("walletViewRadioButton"), + size: 36, + shadows: const [], + color: Theme.of(context) + .extension<StackColors>()! + .background, + icon: SvgPicture.asset( + Assets.svg.trash, + width: 20, + height: 20, + color: Theme.of(context) + .extension<StackColors>()! + .topNavIconPrimary, + ), + onPressed: () async { + await showDialog<void>( + barrierDismissible: true, + context: context, + builder: (_) => StackDialog( + title: + "Do you want to delete ${ref.read(walletsChangeNotifierProvider).getManager(widget.walletId).walletName}?", + leftButton: TextButton( + style: Theme.of(context) + .extension<StackColors>()! + .getSecondaryEnabledButtonStyle(context), + onPressed: () { + Navigator.pop(context); + }, + child: Text( + "Cancel", + style: STextStyles.button(context).copyWith( + color: Theme.of(context) + .extension<StackColors>()! + .accentColorDark), + ), + ), + rightButton: TextButton( + style: Theme.of(context) + .extension<StackColors>()! + .getPrimaryEnabledButtonStyle(context), + onPressed: () { + Navigator.pop(context); + Navigator.push( + context, + RouteGenerator.getRoute( + shouldUseMaterialRoute: + RouteGenerator.useMaterialPageRoute, + builder: (_) => LockscreenView( + routeOnSuccessArguments: + widget.walletId, + showBackButton: true, + routeOnSuccess: + DeleteWalletWarningView.routeName, + biometricsCancelButtonString: + "CANCEL", + biometricsLocalizedReason: + "Authenticate to delete wallet", + biometricsAuthenticationTitle: + "Delete wallet", + ), + settings: const RouteSettings( + name: "/deleteWalletLockscreen"), + ), + ); + }, + child: Text( + "Delete", + style: STextStyles.button(context), + ), + ), + ), + ); + }, + ), + ), + ), + ], + ), + body: Padding( + padding: const EdgeInsets.all(16), + child: child, + ), + ), + ); + }, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (!Util.isDesktop) const Spacer(), + Text( + "Failed to rescan firo wallet", + style: STextStyles.pageTitleH2(context), + ), + Util.isDesktop + ? const SizedBox( + height: 60, + ) + : const Spacer(), + BranchedParent( + condition: Util.isDesktop, + conditionBranchBuilder: (children) => Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: children, + ), + otherBranchBuilder: (children) => Row( + children: [ + Expanded(child: children[0]), + children[1], + Expanded(child: children[2]), + ], + ), + children: [ + SecondaryButton( + label: "Show mnemonic", + buttonHeight: Util.isDesktop ? ButtonHeight.l : null, + onPressed: () async { + if (Util.isDesktop) { + await showDialog<void>( + context: context, + barrierDismissible: false, + builder: (context) => Navigator( + initialRoute: UnlockWalletKeysDesktop.routeName, + onGenerateRoute: RouteGenerator.generateRoute, + onGenerateInitialRoutes: (_, __) { + return [ + RouteGenerator.generateRoute( + RouteSettings( + name: UnlockWalletKeysDesktop.routeName, + arguments: widget.walletId, + ), + ) + ]; + }, + ), + ); + } else { + final mnemonic = await ref + .read(walletsChangeNotifierProvider) + .getManager(widget.walletId) + .mnemonic; + + if (mounted) { + await Navigator.push( + context, + RouteGenerator.getRoute( + shouldUseMaterialRoute: + RouteGenerator.useMaterialPageRoute, + builder: (_) => LockscreenView( + routeOnSuccessArguments: + Tuple2(widget.walletId, mnemonic), + showBackButton: true, + routeOnSuccess: WalletBackupView.routeName, + biometricsCancelButtonString: "CANCEL", + biometricsLocalizedReason: + "Authenticate to view recovery phrase", + biometricsAuthenticationTitle: + "View recovery phrase", + ), + settings: const RouteSettings( + name: "/viewRecoverPhraseLockscreen"), + ), + ); + } + } + }, + ), + const SizedBox( + width: 16, + height: 16, + ), + PrimaryButton( + label: "Retry", + buttonHeight: Util.isDesktop ? ButtonHeight.l : null, + onPressed: () { + Navigator.of(context).pop( + true, + ); + }, + ), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/token_view/sub_widgets/my_token_select_item.dart b/lib/pages/token_view/sub_widgets/my_token_select_item.dart index 31dc2b085..323ddcf8b 100644 --- a/lib/pages/token_view/sub_widgets/my_token_select_item.dart +++ b/lib/pages/token_view/sub_widgets/my_token_select_item.dart @@ -100,7 +100,7 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> { message: "Loading ${widget.token.name}", ); - if (!success) { + if (!success!) { return; } diff --git a/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart b/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart index 4b2ee7a3b..029cd7aa3 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart @@ -121,7 +121,8 @@ class WalletBalanceToggleSheet extends ConsumerWidget { height: 24, ), BalanceSelector( - title: "Available balance", + title: + "Available${balanceSecondary != null ? " public" : ""} balance", coin: coin, balance: balance.spendable, onPressed: () { @@ -141,6 +142,31 @@ class WalletBalanceToggleSheet extends ConsumerWidget { value: _BalanceType.available, groupValue: _bal, ), + const SizedBox( + height: 12, + ), + BalanceSelector( + title: + "Full${balanceSecondary != null ? " public" : ""} balance", + coin: coin, + balance: balance.total, + onPressed: () { + ref.read(walletBalanceToggleStateProvider.state).state = + WalletBalanceToggleState.full; + ref.read(publicPrivateBalanceStateProvider.state).state = + "Public"; + Navigator.of(context).pop(); + }, + onChanged: (_) { + ref.read(walletBalanceToggleStateProvider.state).state = + WalletBalanceToggleState.full; + ref.read(publicPrivateBalanceStateProvider.state).state = + "Public"; + Navigator.of(context).pop(); + }, + value: _BalanceType.full, + groupValue: _bal, + ), if (balanceSecondary != null) const SizedBox( height: 12, @@ -167,30 +193,6 @@ class WalletBalanceToggleSheet extends ConsumerWidget { value: _BalanceType.privateAvailable, groupValue: _bal, ), - const SizedBox( - height: 12, - ), - BalanceSelector( - title: "Full balance", - coin: coin, - balance: balance.total, - onPressed: () { - ref.read(walletBalanceToggleStateProvider.state).state = - WalletBalanceToggleState.full; - ref.read(publicPrivateBalanceStateProvider.state).state = - "Public"; - Navigator.of(context).pop(); - }, - onChanged: (_) { - ref.read(walletBalanceToggleStateProvider.state).state = - WalletBalanceToggleState.full; - ref.read(publicPrivateBalanceStateProvider.state).state = - "Public"; - Navigator.of(context).pop(); - }, - value: _BalanceType.full, - groupValue: _bal, - ), if (balanceSecondary != null) const SizedBox( height: 12, diff --git a/lib/pages/wallet_view/sub_widgets/wallet_summary.dart b/lib/pages/wallet_view/sub_widgets/wallet_summary.dart index a82145817..08f5f22c8 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_summary.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_summary.dart @@ -52,6 +52,7 @@ class WalletSummary extends StatelessWidget { walletId: walletId, width: constraints.maxWidth, height: constraints.maxHeight, + isFavorite: false, ), Positioned.fill( child: Padding( diff --git a/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart b/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart index a4716d190..0c4e6fb08 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart @@ -10,6 +10,7 @@ import 'dart:async'; import 'dart:io'; +import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -19,6 +20,7 @@ import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_refresh_button. import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart'; import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart'; +import 'package:stackwallet/services/coins/banano/banano_wallet.dart'; import 'package:stackwallet/services/coins/firo/firo_wallet.dart'; import 'package:stackwallet/services/event_bus/events/global/balance_refreshed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; @@ -31,6 +33,7 @@ import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart'; import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/conditional_parent.dart'; class WalletSummaryInfo extends ConsumerStatefulWidget { const WalletSummaryInfo({ @@ -49,6 +52,8 @@ class WalletSummaryInfo extends ConsumerStatefulWidget { class _WalletSummaryInfoState extends ConsumerState<WalletSummaryInfo> { late StreamSubscription<BalanceRefreshedEvent> _balanceUpdated; + String receivingAddress = ""; + void showSheet() { showModalBottomSheet<dynamic>( backgroundColor: Colors.transparent, @@ -72,6 +77,17 @@ class _WalletSummaryInfoState extends ConsumerState<WalletSummaryInfo> { } }, ); + + // managerProvider = widget.managerProvider; + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + final address = await ref + .read(walletsChangeNotifierProvider) + .getManager(widget.walletId) + .currentReceivingAddress; + setState(() { + receivingAddress = address; + }); + }); super.initState(); } @@ -85,10 +101,14 @@ class _WalletSummaryInfoState extends ConsumerState<WalletSummaryInfo> { Widget build(BuildContext context) { debugPrint("BUILD: $runtimeType"); + bool isMonkey = true; + + final manager = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(widget.walletId))); + final externalCalls = ref.watch( prefsChangeNotifierProvider.select((value) => value.externalCalls)); - final coin = ref.watch(walletsChangeNotifierProvider - .select((value) => value.getManager(widget.walletId).coin)); + final coin = manager.coin; final balance = ref.watch(walletsChangeNotifierProvider .select((value) => value.getManager(widget.walletId).balance)); @@ -125,84 +145,104 @@ class _WalletSummaryInfoState extends ConsumerState<WalletSummaryInfo> { title = _showAvailable ? "Available balance" : "Full balance"; } - return Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - GestureDetector( - onTap: showSheet, - child: Row( - children: [ - Text( - title, - style: STextStyles.subtitle500(context).copyWith( + List<int>? imageBytes; + + if (coin == Coin.banano) { + imageBytes = (manager.wallet as BananoWallet).getMonkeyImageBytes(); + } + + return ConditionalParent( + condition: imageBytes != null, + builder: (child) => Stack( + children: [ + Positioned.fill( + left: 150.0, + child: SvgPicture.memory( + Uint8List.fromList(imageBytes!), + ), + ), + child, + ], + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: showSheet, + child: Row( + children: [ + Text( + title, + style: STextStyles.subtitle500(context).copyWith( + color: Theme.of(context) + .extension<StackColors>()! + .textFavoriteCard, + ), + ), + const SizedBox( + width: 4, + ), + SvgPicture.asset( + Assets.svg.chevronDown, color: Theme.of(context) .extension<StackColors>()! .textFavoriteCard, + width: 8, + height: 4, ), - ), - const SizedBox( - width: 4, - ), - SvgPicture.asset( - Assets.svg.chevronDown, + ], + ), + ), + const Spacer(), + FittedBox( + fit: BoxFit.scaleDown, + child: SelectableText( + ref.watch(pAmountFormatter(coin)).format(balanceToShow), + style: STextStyles.pageTitleH1(context).copyWith( + fontSize: 24, color: Theme.of(context) .extension<StackColors>()! .textFavoriteCard, - width: 8, - height: 4, ), - ], + ), ), + if (externalCalls) + Text( + "${(priceTuple.item1 * balanceToShow.decimal).toAmount( + fractionDigits: 2, + ).fiatString( + locale: locale, + )} $baseCurrency", + style: STextStyles.subtitle500(context).copyWith( + color: Theme.of(context) + .extension<StackColors>()! + .textFavoriteCard, + ), + ), + ], + ), + ), + Column( + children: [ + SvgPicture.file( + File( + ref.watch(coinIconProvider(coin)), + ), + width: 24, + height: 24, ), const Spacer(), - FittedBox( - fit: BoxFit.scaleDown, - child: SelectableText( - ref.watch(pAmountFormatter(coin)).format(balanceToShow), - style: STextStyles.pageTitleH1(context).copyWith( - fontSize: 24, - color: Theme.of(context) - .extension<StackColors>()! - .textFavoriteCard, - ), - ), + WalletRefreshButton( + walletId: widget.walletId, + initialSyncStatus: widget.initialSyncStatus, ), - if (externalCalls) - Text( - "${(priceTuple.item1 * balanceToShow.decimal).toAmount( - fractionDigits: 2, - ).fiatString( - locale: locale, - )} $baseCurrency", - style: STextStyles.subtitle500(context).copyWith( - color: Theme.of(context) - .extension<StackColors>()! - .textFavoriteCard, - ), - ), ], - ), - ), - Column( - children: [ - SvgPicture.file( - File( - ref.watch(coinIconProvider(coin)), - ), - width: 24, - height: 24, - ), - const Spacer(), - WalletRefreshButton( - walletId: widget.walletId, - initialSyncStatus: widget.initialSyncStatus, - ), - ], - ) - ], + ) + ], + ), ); } } diff --git a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart index e8ccf98de..2b1ba62c4 100644 --- a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart +++ b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart @@ -358,7 +358,6 @@ class _TransactionDetailsViewState final currentHeight = ref.watch(walletsChangeNotifierProvider .select((value) => value.getManager(walletId).currentHeight)); - print("THIS TRANSACTION IS $_transaction"); return ConditionalParent( @@ -474,7 +473,9 @@ class _TransactionDetailsViewState ), SelectableText( _transaction.isCancelled - ? "Cancelled" + ? coin == Coin.ethereum + ? "Failed" + : "Cancelled" : whatIsIt( _transaction, currentHeight, @@ -585,7 +586,9 @@ class _TransactionDetailsViewState // child: SelectableText( _transaction.isCancelled - ? "Cancelled" + ? coin == Coin.ethereum + ? "Failed" + : "Cancelled" : whatIsIt( _transaction, currentHeight, @@ -781,8 +784,8 @@ class _TransactionDetailsViewState isDesktop ? const _Divider() : const SizedBox( - height: 12, - ), + height: 12, + ), if (coin == Coin.epicCash) RoundedWhiteContainer( padding: isDesktop @@ -790,22 +793,22 @@ class _TransactionDetailsViewState : const EdgeInsets.all(12), child: Row( mainAxisAlignment: - MainAxisAlignment.spaceBetween, + MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( crossAxisAlignment: - CrossAxisAlignment.start, + CrossAxisAlignment.start, children: [ Text( "On chain note", style: isDesktop ? STextStyles - .desktopTextExtraExtraSmall( - context) + .desktopTextExtraExtraSmall( + context) : STextStyles.itemSubtitle( - context), + context), ), const SizedBox( height: 8, @@ -814,18 +817,16 @@ class _TransactionDetailsViewState _transaction.otherData ?? "", style: isDesktop ? STextStyles - .desktopTextExtraExtraSmall( - context) - .copyWith( - color: Theme.of( - context) - .extension< - StackColors>()! - .textDark, - ) - : STextStyles - .itemSubtitle12( - context), + .desktopTextExtraExtraSmall( + context) + .copyWith( + color: Theme.of(context) + .extension< + StackColors>()! + .textDark, + ) + : STextStyles.itemSubtitle12( + context), ), ], ), @@ -854,7 +855,9 @@ class _TransactionDetailsViewState MainAxisAlignment.spaceBetween, children: [ Text( - (coin == Coin.epicCash) ? "Local Note" : "Note ", + (coin == Coin.epicCash) + ? "Local Note" + : "Note ", style: isDesktop ? STextStyles .desktopTextExtraExtraSmall( @@ -923,7 +926,9 @@ class _TransactionDetailsViewState notesServiceChangeNotifierProvider( walletId) .select((value) => value.getNoteFor( - txid: (coin == Coin.epicCash)? _transaction.slateId! : _transaction.txid ))), + txid: (coin == Coin.epicCash) + ? _transaction.slateId! + : _transaction.txid))), builder: (builderContext, AsyncSnapshot<String> snapshot) { if (snapshot.connectionState == diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index 3e7fb37fa..c9e391320 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -22,13 +22,16 @@ import 'package:stackwallet/pages/buy_view/buy_in_wallet_view.dart'; import 'package:stackwallet/pages/coin_control/coin_control_view.dart'; import 'package:stackwallet/pages/exchange_view/wallet_initiated_exchange_view.dart'; import 'package:stackwallet/pages/home_view/home_view.dart'; +import 'package:stackwallet/pages/monkey/monkey_view.dart'; import 'package:stackwallet/pages/notification_views/notifications_view.dart'; +import 'package:stackwallet/pages/ordinals/ordinals_view.dart'; import 'package:stackwallet/pages/paynym/paynym_claim_view.dart'; import 'package:stackwallet/pages/paynym/paynym_home_view.dart'; import 'package:stackwallet/pages/receive_view/receive_view.dart'; import 'package:stackwallet/pages/send_view/send_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_view.dart'; +import 'package:stackwallet/pages/special/firo_rescan_recovery_error_dialog.dart'; import 'package:stackwallet/pages/token_view/my_tokens_view.dart'; import 'package:stackwallet/pages/wallet_view/sub_widgets/transactions_list.dart'; import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_summary.dart'; @@ -72,6 +75,7 @@ import 'package:stackwallet/widgets/stack_dialog.dart'; import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/buy_nav_icon.dart'; import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/coin_control_nav_icon.dart'; import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/exchange_nav_icon.dart'; +import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/ordinals_nav_icon.dart'; import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/paynym_nav_icon.dart'; import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/receive_nav_icon.dart'; import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/send_nav_icon.dart'; @@ -116,6 +120,36 @@ class _WalletViewState extends ConsumerState<WalletView> { late StreamSubscription<dynamic> _nodeStatusSubscription; bool _rescanningOnOpen = false; + bool _lelantusRescanRecovery = false; + + Future<void> _firoRescanRecovery() async { + final success = await (ref.read(managerProvider).wallet as FiroWallet) + .firoRescanRecovery(); + + if (success) { + // go into wallet + WidgetsBinding.instance.addPostFrameCallback( + (_) => setState(() { + _rescanningOnOpen = false; + _lelantusRescanRecovery = false; + }), + ); + } else { + // show error message dialog w/ options + if (mounted) { + final shouldRetry = await Navigator.of(context).pushNamed( + FiroRescanRecoveryErrorView.routeName, + arguments: walletId, + ); + + if (shouldRetry is bool && shouldRetry) { + await _firoRescanRecovery(); + } + } else { + return await _firoRescanRecovery(); + } + } + } @override void initState() { @@ -131,7 +165,14 @@ class _WalletViewState extends ConsumerState<WalletView> { _shouldDisableAutoSyncOnLogOut = false; } - if (ref.read(managerProvider).rescanOnOpenVersion == Constants.rescanV1) { + if (ref.read(managerProvider).coin == Coin.firo && + (ref.read(managerProvider).wallet as FiroWallet) + .lelantusCoinIsarRescanRequired) { + _rescanningOnOpen = true; + _lelantusRescanRecovery = true; + _firoRescanRecovery(); + } else if (ref.read(managerProvider).rescanOnOpenVersion == + Constants.rescanV1) { _rescanningOnOpen = true; ref.read(managerProvider).fullRescan(20, 1000).then( (_) => ref.read(managerProvider).resetRescanOnOpen().then( @@ -209,6 +250,10 @@ class _WalletViewState extends ConsumerState<WalletView> { DateTime? _cachedTime; Future<bool> _onWillPop() async { + if (_rescanningOnOpen || _lelantusRescanRecovery) { + return false; + } + final now = DateTime.now(); const timeout = Duration(milliseconds: 1500); if (_cachedTime == null || now.difference(_cachedTime!) > timeout) { @@ -431,33 +476,37 @@ class _WalletViewState extends ConsumerState<WalletView> { eventBus: null, textColor: Theme.of(context).extension<StackColors>()!.textDark, - actionButton: SecondaryButton( - label: "Cancel", - onPressed: () async { - await showDialog<void>( - context: context, - builder: (context) => StackDialog( - title: "Warning!", - message: "Skipping this process can completely" - " break your wallet. It is only meant to be done in" - " emergency situations where the migration fails" - " and will not let you continue. Still skip?", - leftButton: SecondaryButton( - label: "Cancel", - onPressed: - Navigator.of(context, rootNavigator: true).pop, - ), - rightButton: SecondaryButton( - label: "Ok", - onPressed: () { - Navigator.of(context, rootNavigator: true).pop(); - setState(() => _rescanningOnOpen = false); - }, - ), + actionButton: _lelantusRescanRecovery + ? null + : SecondaryButton( + label: "Cancel", + onPressed: () async { + await showDialog<void>( + context: context, + builder: (context) => StackDialog( + title: "Warning!", + message: "Skipping this process can completely" + " break your wallet. It is only meant to be done in" + " emergency situations where the migration fails" + " and will not let you continue. Still skip?", + leftButton: SecondaryButton( + label: "Cancel", + onPressed: + Navigator.of(context, rootNavigator: true) + .pop, + ), + rightButton: SecondaryButton( + label: "Ok", + onPressed: () { + Navigator.of(context, rootNavigator: true) + .pop(); + setState(() => _rescanningOnOpen = false); + }, + ), + ), + ); + }, ), - ); - }, - ), ), ) ], @@ -924,6 +973,23 @@ class _WalletViewState extends ConsumerState<WalletView> { ); }, ), + if (coin == Coin.banano) + WalletNavigationBarItemData( + icon: SvgPicture.asset( + Assets.svg.monkey, + height: 20, + width: 20, + color: Theme.of(context) + .extension<StackColors>()! + .bottomNavIconIcon, + ), + label: "MonKey", + onTap: () { + Navigator.of(context).pushNamed( + MonkeyView.routeName, + arguments: widget.walletId, + ); + }), if (ref.watch( walletsChangeNotifierProvider.select( (value) => value @@ -1007,6 +1073,22 @@ class _WalletViewState extends ConsumerState<WalletView> { } }, ), + if (ref.watch( + walletsChangeNotifierProvider.select( + (value) => + value.getManager(widget.walletId).hasOrdinalsSupport, + ), + )) + WalletNavigationBarItemData( + label: "Ordinals", + icon: const OrdinalsNavIcon(), + onTap: () { + Navigator.of(context).pushNamed( + OrdinalsView.routeName, + arguments: widget.walletId, + ); + }, + ), ], ), ], diff --git a/lib/pages/wallets_view/sub_widgets/favorite_card.dart b/lib/pages/wallets_view/sub_widgets/favorite_card.dart index b3f5e97e5..107b323e0 100644 --- a/lib/pages/wallets_view/sub_widgets/favorite_card.dart +++ b/lib/pages/wallets_view/sub_widgets/favorite_card.dart @@ -149,6 +149,7 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> { walletId: widget.walletId, width: widget.width, height: widget.height, + isFavorite: true, ), child: Padding( padding: const EdgeInsets.all(12.0), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart index 003c735b2..0df83088b 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart @@ -10,12 +10,14 @@ import 'dart:async'; import 'dart:io'; +import 'dart:typed_data'; import 'package:event_bus/event_bus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart'; +import 'package:stackwallet/pages/special/firo_rescan_recovery_error_dialog.dart'; import 'package:stackwallet/pages/token_view/my_tokens_view.dart'; import 'package:stackwallet/pages/wallet_view/sub_widgets/transactions_list.dart'; import 'package:stackwallet/pages/wallet_view/transaction_views/all_transactions_view.dart'; @@ -28,6 +30,8 @@ import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub import 'package:stackwallet/providers/global/auto_swb_service_provider.dart'; import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/ui/transaction_filter_provider.dart'; +import 'package:stackwallet/services/coins/banano/banano_wallet.dart'; +import 'package:stackwallet/services/coins/firo/firo_wallet.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart'; import 'package:stackwallet/themes/coin_icon_provider.dart'; @@ -76,6 +80,7 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> { late final bool _shouldDisableAutoSyncOnLogOut; bool _rescanningOnOpen = false; + bool _lelantusRescanRecovery = false; Future<void> onBackPressed() async { await _logout(); @@ -101,6 +106,38 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> { ref.read(managerProvider.notifier).isActiveWallet = false; } + Future<void> _firoRescanRecovery() async { + final success = await (ref + .read(walletsChangeNotifierProvider) + .getManager(widget.walletId) + .wallet as FiroWallet) + .firoRescanRecovery(); + + if (success) { + // go into wallet + WidgetsBinding.instance.addPostFrameCallback( + (_) => setState(() { + _rescanningOnOpen = false; + _lelantusRescanRecovery = false; + }), + ); + } else { + // show error message dialog w/ options + if (mounted) { + final shouldRetry = await Navigator.of(context).pushNamed( + FiroRescanRecoveryErrorView.routeName, + arguments: widget.walletId, + ); + + if (shouldRetry is bool && shouldRetry) { + await _firoRescanRecovery(); + } + } else { + return await _firoRescanRecovery(); + } + } + } + @override void initState() { controller = TextEditingController(); @@ -122,7 +159,13 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> { _shouldDisableAutoSyncOnLogOut = false; } - if (ref.read(managerProvider).coin != Coin.ethereum && + if (ref.read(managerProvider).coin == Coin.firo && + (ref.read(managerProvider).wallet as FiroWallet) + .lelantusCoinIsarRescanRequired) { + _rescanningOnOpen = true; + _lelantusRescanRecovery = true; + _firoRescanRecovery(); + } else if (ref.read(managerProvider).coin != Coin.ethereum && ref.read(managerProvider).rescanOnOpenVersion == Constants.rescanV1) { _rescanningOnOpen = true; ref.read(managerProvider).fullRescan(20, 1000).then( @@ -153,6 +196,10 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> { final managerProvider = ref.watch(walletsChangeNotifierProvider .select((value) => value.getManagerProvider(widget.walletId))); + final monke = coin == Coin.banano + ? (manager.wallet as BananoWallet).getMonkeyImageBytes() + : null; + return ConditionalParent( condition: _rescanningOnOpen, builder: (child) { @@ -166,83 +213,86 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> { subMessage: "This only needs to run once per wallet", eventBus: null, textColor: Theme.of(context).extension<StackColors>()!.textDark, - actionButton: SecondaryButton( - label: "Skip", - buttonHeight: ButtonHeight.l, - onPressed: () async { - await showDialog<void>( - context: context, - builder: (context) => DesktopDialog( - maxWidth: 500, - maxHeight: double.infinity, - child: Column( - children: [ - Padding( - padding: const EdgeInsets.only(left: 32), - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, + actionButton: _lelantusRescanRecovery + ? null + : SecondaryButton( + label: "Skip", + buttonHeight: ButtonHeight.l, + onPressed: () async { + await showDialog<void>( + context: context, + builder: (context) => DesktopDialog( + maxWidth: 500, + maxHeight: double.infinity, + child: Column( children: [ - Text( - "Warning!", - style: STextStyles.desktopH3(context), + Padding( + padding: const EdgeInsets.only(left: 32), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + "Warning!", + style: STextStyles.desktopH3(context), + ), + const DesktopDialogCloseButton(), + ], + ), ), - const DesktopDialogCloseButton(), - ], - ), - ), - Padding( - padding: - const EdgeInsets.symmetric(horizontal: 32), - child: Text( - "Skipping this process can completely" - " break your wallet. It is only meant to be done in" - " emergency situations where the migration fails" - " and will not let you continue. Still skip?", - style: STextStyles.desktopTextSmall(context), - ), - ), - const SizedBox( - height: 32, - ), - Padding( - padding: const EdgeInsets.all(32), - child: Row( - children: [ - Expanded( - child: SecondaryButton( - label: "Cancel", - buttonHeight: ButtonHeight.l, - onPressed: Navigator.of(context, - rootNavigator: true) - .pop, + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 32), + child: Text( + "Skipping this process can completely" + " break your wallet. It is only meant to be done in" + " emergency situations where the migration fails" + " and will not let you continue. Still skip?", + style: + STextStyles.desktopTextSmall(context), ), ), const SizedBox( - width: 16, + height: 32, ), - Expanded( - child: PrimaryButton( - label: "Ok", - buttonHeight: ButtonHeight.l, - onPressed: () { - Navigator.of(context, - rootNavigator: true) - .pop(); - setState( - () => _rescanningOnOpen = false); - }, + Padding( + padding: const EdgeInsets.all(32), + child: Row( + children: [ + Expanded( + child: SecondaryButton( + label: "Cancel", + buttonHeight: ButtonHeight.l, + onPressed: Navigator.of(context, + rootNavigator: true) + .pop, + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: PrimaryButton( + label: "Ok", + buttonHeight: ButtonHeight.l, + onPressed: () { + Navigator.of(context, + rootNavigator: true) + .pop(); + setState(() => + _rescanningOnOpen = false); + }, + ), + ), + ], ), - ), + ) ], ), - ) - ], - ), + ), + ); + }, ), - ); - }, - ), ), ) ], @@ -334,13 +384,20 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> { padding: const EdgeInsets.all(20), child: Row( children: [ - SvgPicture.file( - File( - ref.watch(coinIconProvider(coin)), + if (monke != null) + SvgPicture.memory( + Uint8List.fromList(monke!), + width: 60, + height: 60, + ), + if (monke == null) + SvgPicture.file( + File( + ref.watch(coinIconProvider(coin)), + ), + width: 40, + height: 40, ), - width: 40, - height: 40, - ), const SizedBox( width: 10, ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart index 0488358a7..3d65eb6e2 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart @@ -105,6 +105,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> { final _baseFocus = FocusNode(); String? _note; + String? _onChainNote; Amount? _amountToSend; Amount? _cachedAmountToSend; @@ -354,6 +355,9 @@ class _DesktopSendState extends ConsumerState<DesktopSend> { } else { txData["address"] = _address; txData["note"] = _note ?? ""; + if (coin == Coin.epicCash) { + txData['onChainNote'] = _onChainNote ?? ""; + } } // pop building dialog Navigator.of( diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart index aa5922959..7679956f3 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart @@ -16,12 +16,14 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; +import 'package:stackwallet/pages/monkey/monkey_view.dart'; import 'package:stackwallet/pages/paynym/paynym_claim_view.dart'; import 'package:stackwallet/pages/paynym/paynym_home_view.dart'; import 'package:stackwallet/pages_desktop_specific/coin_control/desktop_coin_control_view.dart'; import 'package:stackwallet/pages_desktop_specific/desktop_menu.dart'; import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart'; import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart'; +import 'package:stackwallet/pages_desktop_specific/ordinals/desktop_ordinals_view.dart'; import 'package:stackwallet/providers/desktop/current_desktop_menu_item.dart'; import 'package:stackwallet/providers/global/paynym_api_provider.dart'; import 'package:stackwallet/providers/providers.dart'; @@ -80,6 +82,8 @@ class _DesktopWalletFeaturesState extends ConsumerState<DesktopWalletFeatures> { onCoinControlPressed: _onCoinControlPressed, onAnonymizeAllPressed: _onAnonymizeAllPressed, onWhirlpoolPressed: _onWhirlpoolPressed, + onOrdinalsPressed: _onOrdinalsPressed, + onMonkeyPressed: _onMonkeyPressed, ), ); } @@ -313,6 +317,24 @@ class _DesktopWalletFeaturesState extends ConsumerState<DesktopWalletFeatures> { } } + Future<void> _onMonkeyPressed() async { + Navigator.of(context, rootNavigator: true).pop(); + + await (Navigator.of(context).pushNamed( + MonkeyView.routeName, + arguments: widget.walletId, + )); + } + + void _onOrdinalsPressed() { + Navigator.of(context, rootNavigator: true).pop(); + + Navigator.of(context).pushNamed( + DesktopOrdinalsView.routeName, + arguments: widget.walletId, + ); + } + @override Widget build(BuildContext context) { final manager = ref.watch( @@ -330,8 +352,9 @@ class _DesktopWalletFeaturesState extends ConsumerState<DesktopWalletFeatures> { )) || manager.coin == Coin.firo || manager.coin == Coin.firoTestNet || - manager.hasWhirlpoolSupport; - + manager.hasWhirlpoolSupport || + manager.coin == Coin.banano || + manager.hasOrdinalsSupport; return Row( children: [ if (Constants.enableExchange) diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart index 4c8f47c25..09c400324 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart @@ -29,6 +29,8 @@ class MoreFeaturesDialog extends ConsumerStatefulWidget { required this.onCoinControlPressed, required this.onAnonymizeAllPressed, required this.onWhirlpoolPressed, + required this.onOrdinalsPressed, + required this.onMonkeyPressed, }) : super(key: key); final String walletId; @@ -36,6 +38,8 @@ class MoreFeaturesDialog extends ConsumerStatefulWidget { final VoidCallback? onCoinControlPressed; final VoidCallback? onAnonymizeAllPressed; final VoidCallback? onWhirlpoolPressed; + final VoidCallback? onOrdinalsPressed; + final VoidCallback? onMonkeyPressed; @override ConsumerState<MoreFeaturesDialog> createState() => _MoreFeaturesDialogState(); @@ -103,6 +107,20 @@ class _MoreFeaturesDialogState extends ConsumerState<MoreFeaturesDialog> { iconAsset: Assets.svg.robotHead, onPressed: () => widget.onPaynymPressed?.call(), ), + if (manager.hasOrdinalsSupport) + _MoreFeaturesItem( + label: "Ordinals", + detail: "View and control your ordinals in Stack", + iconAsset: Assets.svg.ordinal, + onPressed: () => widget.onOrdinalsPressed?.call(), + ), + if (manager.coin == Coin.banano) + _MoreFeaturesItem( + label: "MonKey", + detail: "Generate Banano MonKey", + iconAsset: Assets.svg.monkey, + onPressed: () => widget.onMonkeyPressed?.call(), + ), const SizedBox( height: 28, ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart index 68fe09aa8..61c61a807 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart @@ -13,7 +13,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:stackwallet/pages/settings_views/global_settings_view/xpub_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart'; import 'package:stackwallet/pages_desktop_specific/addresses/desktop_wallet_addresses_view.dart'; import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_delete_wallet_dialog.dart'; import 'package:stackwallet/providers/providers.dart'; @@ -21,11 +22,13 @@ import 'package:stackwallet/route_generator.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/text_styles.dart'; enum _WalletOptions { addressList, deleteWallet, + changeRepresentative, showXpub; String get prettyName { @@ -34,6 +37,8 @@ enum _WalletOptions { return "Address list"; case _WalletOptions.deleteWallet: return "Delete wallet"; + case _WalletOptions.changeRepresentative: + return "Change representative"; case _WalletOptions.showXpub: return "Show xPub"; } @@ -70,6 +75,9 @@ class WalletOptionsButton extends StatelessWidget { onAddressListPressed: () async { Navigator.of(context).pop(_WalletOptions.addressList); }, + onChangeRepPressed: () async { + Navigator.of(context).pop(_WalletOptions.changeRepresentative); + }, onShowXpubPressed: () async { Navigator.of(context).pop(_WalletOptions.showXpub); }, @@ -134,6 +142,32 @@ class WalletOptionsButton extends StatelessWidget { ), ); + if (result == true) { + if (context.mounted) { + Navigator.of(context).pop(); + } + } + break; + case _WalletOptions.changeRepresentative: + final result = await showDialog<bool?>( + context: context, + barrierDismissible: false, + builder: (context) => Navigator( + initialRoute: ChangeRepresentativeView.routeName, + onGenerateRoute: RouteGenerator.generateRoute, + onGenerateInitialRoutes: (_, __) { + return [ + RouteGenerator.generateRoute( + RouteSettings( + name: ChangeRepresentativeView.routeName, + arguments: walletId, + ), + ), + ]; + }, + ), + ); + if (result == true) { if (context.mounted) { Navigator.of(context).pop(); @@ -171,18 +205,24 @@ class WalletOptionsPopupMenu extends ConsumerWidget { required this.onDeletePressed, required this.onAddressListPressed, required this.onShowXpubPressed, + required this.onChangeRepPressed, required this.walletId, }) : super(key: key); final VoidCallback onDeletePressed; final VoidCallback onAddressListPressed; final VoidCallback onShowXpubPressed; + final VoidCallback onChangeRepPressed; final String walletId; @override Widget build(BuildContext context, WidgetRef ref) { - final bool xpubEnabled = ref.watch(walletsChangeNotifierProvider - .select((value) => value.getManager(walletId).hasXPub)); + final manager = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(walletId))); + final bool xpubEnabled = manager.hasXPub; + + final bool canChangeRep = + manager.coin == Coin.nano || manager.coin == Coin.banano; return Stack( children: [ @@ -237,6 +277,43 @@ class WalletOptionsPopupMenu extends ConsumerWidget { ), ), ), + if (canChangeRep) + const SizedBox( + height: 8, + ), + if (canChangeRep) + TransparentButton( + onPressed: onChangeRepPressed, + child: Padding( + padding: const EdgeInsets.all(8), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SvgPicture.asset( + Assets.svg.eye, + width: 20, + height: 20, + color: Theme.of(context) + .extension<StackColors>()! + .textFieldActiveSearchIconLeft, + ), + const SizedBox(width: 14), + Expanded( + child: Text( + _WalletOptions.changeRepresentative.prettyName, + style: STextStyles.desktopTextExtraExtraSmall( + context) + .copyWith( + color: Theme.of(context) + .extension<StackColors>()! + .textDark, + ), + ), + ), + ], + ), + ), + ), if (xpubEnabled) const SizedBox( height: 8, diff --git a/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart new file mode 100644 index 000000000..b0225f286 --- /dev/null +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart @@ -0,0 +1,372 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:http/http.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; +import 'package:stackwallet/models/isar/ordinal.dart'; +import 'package:stackwallet/notifications/show_flush_bar.dart'; +import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart'; +import 'package:stackwallet/providers/db/main_db_provider.dart'; +import 'package:stackwallet/providers/global/wallets_provider.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/assets.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/show_loading.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart'; +import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +class DesktopOrdinalDetailsView extends ConsumerStatefulWidget { + const DesktopOrdinalDetailsView({ + Key? key, + required this.walletId, + required this.ordinal, + }) : super(key: key); + + final String walletId; + final Ordinal ordinal; + + static const routeName = "/desktopOrdinalDetailsView"; + + @override + ConsumerState<DesktopOrdinalDetailsView> createState() => + _DesktopOrdinalDetailsViewState(); +} + +class _DesktopOrdinalDetailsViewState + extends ConsumerState<DesktopOrdinalDetailsView> { + static const _spacing = 12.0; + + late final UTXO? utxo; + + Future<String> _savePngToFile() async { + final response = await get(Uri.parse(widget.ordinal.content)); + + if (response.statusCode != 200) { + throw Exception( + "statusCode=${response.statusCode} body=${response.bodyBytes}"); + } + + final bytes = response.bodyBytes; + + if (Platform.isAndroid) { + await Permission.storage.request(); + } + + final dir = Platform.isAndroid + ? Directory("/storage/emulated/0/Documents") + : await getApplicationDocumentsDirectory(); + + final docPath = dir.path; + final filePath = "$docPath/ordinal_${widget.ordinal.inscriptionNumber}.png"; + + File imgFile = File(filePath); + + if (imgFile.existsSync()) { + throw Exception("File already exists"); + } + + await imgFile.writeAsBytes(bytes); + return filePath; + } + + @override + void initState() { + utxo = widget.ordinal.getUTXO(ref.read(mainDBProvider)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + final coin = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(widget.walletId).coin)); + + return DesktopScaffold( + appBar: DesktopAppBar( + background: Theme.of(context).extension<StackColors>()!.popupBG, + leading: Expanded( + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox( + width: 32, + ), + AppBarIconButton( + size: 32, + color: Theme.of(context) + .extension<StackColors>()! + .textFieldDefaultBG, + shadows: const [], + icon: SvgPicture.asset( + Assets.svg.arrowLeft, + width: 18, + height: 18, + color: Theme.of(context) + .extension<StackColors>()! + .topNavIconPrimary, + ), + onPressed: Navigator.of(context).pop, + ), + const SizedBox( + width: 18, + ), + Text( + "Ordinal details", + style: STextStyles.desktopH3(context), + ), + ], + ), + ), + useSpacers: false, + isCompactHeight: true, + ), + body: Padding( + padding: const EdgeInsets.only( + left: 24, + top: 24, + right: 24, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 300, + height: 300, + child: ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: Image.network( + widget.ordinal + .content, // Use the preview URL as the image source + fit: BoxFit.cover, + filterQuality: + FilterQuality.none, // Set the filter mode to nearest + ), + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + children: [ + RoundedWhiteContainer( + padding: const EdgeInsets.all(16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SelectableText( + "INSC. ${widget.ordinal.inscriptionNumber}", + style: STextStyles.w600_20(context), + ), + ], + ), + ), + const SizedBox( + width: 16, + ), + // PrimaryButton( + // width: 150, + // label: "Send", + // icon: SvgPicture.asset( + // Assets.svg.send, + // width: 18, + // height: 18, + // color: Theme.of(context) + // .extension<StackColors>()! + // .buttonTextPrimary, + // ), + // buttonHeight: ButtonHeight.l, + // iconSpacing: 8, + // onPressed: () async { + // final response = await showDialog<String?>( + // context: context, + // builder: (_) => + // const SendOrdinalUnfreezeDialog(), + // ); + // if (response == "unfreeze") { + // // TODO: unfreeze and go to send ord screen + // } + // }, + // ), + // const SizedBox( + // width: 16, + // ), + SecondaryButton( + width: 150, + label: "Download", + icon: SvgPicture.asset( + Assets.svg.arrowDown, + width: 13, + height: 18, + color: Theme.of(context) + .extension<StackColors>()! + .buttonTextSecondary, + ), + buttonHeight: ButtonHeight.l, + iconSpacing: 8, + onPressed: () async { + bool didError = false; + final path = await showLoading<String>( + whileFuture: _savePngToFile(), + context: context, + isDesktop: true, + message: "Saving ordinal image", + onException: (e) { + didError = true; + String msg = e.toString(); + while (msg.isNotEmpty && + msg.startsWith("Exception:")) { + msg = msg.substring(10).trim(); + } + showFloatingFlushBar( + type: FlushBarType.warning, + message: msg, + context: context, + ); + }, + ); + + if (!didError && mounted) { + await showFloatingFlushBar( + type: FlushBarType.success, + message: "Image saved to $path", + context: context, + ); + } + }, + ), + ], + ), + ), + const SizedBox( + height: 16, + ), + RoundedWhiteContainer( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + _DetailsItemWCopy( + title: "Inscription number", + data: widget.ordinal.inscriptionNumber.toString(), + ), + const _Divider(), + _DetailsItemWCopy( + title: "Inscription ID", + data: widget.ordinal.inscriptionId, + ), + // const SizedBox( + // height: _spacing, + // ), + // // todo: add utxo status + const _Divider(), + _DetailsItemWCopy( + title: "Amount", + data: utxo == null + ? "ERROR" + : ref.watch(pAmountFormatter(coin)).format( + Amount( + rawValue: BigInt.from(utxo!.value), + fractionDigits: coin.decimals, + ), + ), + ), + const _Divider(), + _DetailsItemWCopy( + title: "Owner address", + data: utxo?.address ?? "ERROR", + ), + const _Divider(), + _DetailsItemWCopy( + title: "Transaction ID", + data: widget.ordinal.utxoTXID, + ), + ], + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ); + } +} + +class _Divider extends StatelessWidget { + const _Divider({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric( + vertical: 16, + ), + child: Container( + height: 1, + color: Theme.of(context).extension<StackColors>()!.backgroundAppBar, + ), + ); + } +} + +class _DetailsItemWCopy extends StatelessWidget { + const _DetailsItemWCopy({ + Key? key, + required this.title, + required this.data, + }) : super(key: key); + + final String title; + final String data; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: STextStyles.itemSubtitle(context), + ), + IconCopyButton( + data: data, + ), + ], + ), + const SizedBox( + height: 4, + ), + SelectableText( + data, + style: STextStyles.itemSubtitle12(context), + ), + ], + ); + } +} diff --git a/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart new file mode 100644 index 000000000..e919ff299 --- /dev/null +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart @@ -0,0 +1,243 @@ +/* + * 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:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/pages/ordinals/widgets/ordinals_list.dart'; +import 'package:stackwallet/providers/providers.dart'; +import 'package:stackwallet/services/mixins/ordinals_interface.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/show_loading.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart'; +import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; + +class DesktopOrdinalsView extends ConsumerStatefulWidget { + const DesktopOrdinalsView({ + super.key, + required this.walletId, + }); + + static const String routeName = "/desktopOrdinalsView"; + + final String walletId; + + @override + ConsumerState<DesktopOrdinalsView> createState() => _DesktopOrdinals(); +} + +class _DesktopOrdinals extends ConsumerState<DesktopOrdinalsView> { + late final TextEditingController searchController; + late final FocusNode searchFocusNode; + + String _searchTerm = ""; + + @override + void initState() { + searchController = TextEditingController(); + searchFocusNode = FocusNode(); + + super.initState(); + } + + @override + void dispose() { + searchController.dispose(); + searchFocusNode.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + debugPrint("BUILD: $runtimeType"); + + return DesktopScaffold( + appBar: DesktopAppBar( + background: Theme.of(context).extension<StackColors>()!.popupBG, + isCompactHeight: true, + useSpacers: false, + leading: Expanded( + child: Row( + children: [ + const SizedBox( + width: 32, + ), + AppBarIconButton( + size: 32, + color: Theme.of(context) + .extension<StackColors>()! + .textFieldDefaultBG, + shadows: const [], + icon: SvgPicture.asset( + Assets.svg.arrowLeft, + width: 18, + height: 18, + color: Theme.of(context) + .extension<StackColors>()! + .topNavIconPrimary, + ), + onPressed: Navigator.of(context).pop, + ), + const SizedBox( + width: 15, + ), + SvgPicture.asset( + Assets.svg.ordinal, + width: 32, + height: 32, + color: + Theme.of(context).extension<StackColors>()!.textSubtitle1, + ), + const SizedBox( + width: 12, + ), + Text( + "Ordinals", + style: STextStyles.desktopH3(context), + ) + ], + ), + ), + ), + body: Padding( + padding: const EdgeInsets.all(24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Spacer(), + // Expanded( + // child: ClipRRect( + // borderRadius: BorderRadius.circular( + // Constants.size.circularBorderRadius, + // ), + // child: TextField( + // autocorrect: Util.isDesktop ? false : true, + // enableSuggestions: Util.isDesktop ? false : true, + // controller: searchController, + // focusNode: searchFocusNode, + // onChanged: (value) { + // setState(() { + // _searchTerm = value; + // }); + // }, + // style: STextStyles.field(context), + // decoration: standardInputDecoration( + // "Search", + // searchFocusNode, + // context, + // ).copyWith( + // prefixIcon: Padding( + // padding: const EdgeInsets.symmetric( + // horizontal: 10, + // vertical: 20, + // ), + // child: SvgPicture.asset( + // Assets.svg.search, + // width: 16, + // height: 16, + // ), + // ), + // suffixIcon: searchController.text.isNotEmpty + // ? Padding( + // padding: const EdgeInsets.only(right: 0), + // child: UnconstrainedBox( + // child: Row( + // children: [ + // TextFieldIconButton( + // child: const XIcon(), + // onTap: () async { + // setState(() { + // searchController.text = ""; + // _searchTerm = ""; + // }); + // }, + // ), + // ], + // ), + // ), + // ) + // : null, + // ), + // ), + // ), + // ), + // const SizedBox( + // width: 16, + // ), + // SecondaryButton( + // width: 184, + // label: "Filter", + // buttonHeight: ButtonHeight.l, + // icon: SvgPicture.asset( + // Assets.svg.filter, + // color: Theme.of(context) + // .extension<StackColors>()! + // .buttonTextSecondary, + // ), + // onPressed: () { + // Navigator.of(context).pushNamed( + // OrdinalsFilterView.routeName, + // ); + // }, + // ), + const SizedBox( + width: 16, + ), + SecondaryButton( + width: 184, + label: "Update", + buttonHeight: ButtonHeight.l, + icon: SvgPicture.asset( + Assets.svg.arrowRotate, + color: Theme.of(context) + .extension<StackColors>()! + .buttonTextSecondary, + ), + onPressed: () async { + // show loading for a minimum of 2 seconds on refreshing + await showLoading( + isDesktop: true, + whileFuture: Future.wait<void>([ + Future.delayed(const Duration(seconds: 2)), + (ref + .read(walletsChangeNotifierProvider) + .getManager(widget.walletId) + .wallet as OrdinalsInterface) + .refreshInscriptions() + ]), + context: context, + message: "Refreshing..."); + }, + ), + ], + ), + const SizedBox( + height: 16, + ), + Expanded( + child: SingleChildScrollView( + child: OrdinalsList( + walletId: widget.walletId, + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages_desktop_specific/password/create_password_view.dart b/lib/pages_desktop_specific/password/create_password_view.dart index 68205d077..2986cd0da 100644 --- a/lib/pages_desktop_specific/password/create_password_view.dart +++ b/lib/pages_desktop_specific/password/create_password_view.dart @@ -125,7 +125,7 @@ class _CreatePasswordViewState extends ConsumerState<CreatePasswordView> { } } - if (!widget.restoreFromSWB) { + if (!widget.restoreFromSWB && mounted) { unawaited(showFloatingFlushBar( type: FlushBarType.success, message: "Your password is set up", diff --git a/lib/pages_desktop_specific/password/desktop_login_view.dart b/lib/pages_desktop_specific/password/desktop_login_view.dart index 10e1477b4..471da2763 100644 --- a/lib/pages_desktop_specific/password/desktop_login_view.dart +++ b/lib/pages_desktop_specific/password/desktop_login_view.dart @@ -84,10 +84,10 @@ class _DesktopLoginViewState extends ConsumerState<DesktopLoginView> { unawaited( showDialog( context: context, - builder: (context) => Column( + builder: (context) => const Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, - children: const [ + children: [ LoadingIndicator( width: 200, height: 200, diff --git a/lib/providers/exchange/exchange_form_state_provider.dart b/lib/providers/exchange/exchange_form_state_provider.dart index 07447f169..b4a923b78 100644 --- a/lib/providers/exchange/exchange_form_state_provider.dart +++ b/lib/providers/exchange/exchange_form_state_provider.dart @@ -13,8 +13,12 @@ import 'package:flutter_riverpod/flutter_riverpod.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/range.dart'; +import 'package:stackwallet/providers/global/locale_provider.dart'; import 'package:stackwallet/services/exchange/exchange.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:tuple/tuple.dart'; @@ -44,7 +48,22 @@ final efSendAmountStringProvider = StateProvider<String>((ref) { if (refreshing && reversed) { return "-"; } 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) { @@ -54,7 +73,22 @@ final efReceiveAmountStringProvider = StateProvider<String>((ref) { if (refreshing && reversed == false) { return "-"; } 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; } }); diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 7ccc378c9..2b7aa662d 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -17,7 +17,9 @@ import 'package:stackwallet/models/add_wallet_list_entity/sub_classes/eth_token_ import 'package:stackwallet/models/buy/response_objects/quote.dart'; import 'package:stackwallet/models/exchange/incomplete_exchange.dart'; import 'package:stackwallet/models/exchange/response_objects/trade.dart'; +import 'package:stackwallet/models/isar/models/contact_entry.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; +import 'package:stackwallet/models/isar/ordinal.dart'; import 'package:stackwallet/models/paynym/paynym_account_lite.dart'; import 'package:stackwallet/models/send_view_auto_fill_data.dart'; import 'package:stackwallet/pages/add_wallet_views/add_token_view/add_custom_token_view.dart'; @@ -56,7 +58,11 @@ import 'package:stackwallet/pages/generic/single_field_edit_view.dart'; import 'package:stackwallet/pages/home_view/home_view.dart'; import 'package:stackwallet/pages/intro_view.dart'; import 'package:stackwallet/pages/manage_favorites_view/manage_favorites_view.dart'; +import 'package:stackwallet/pages/monkey/monkey_view.dart'; import 'package:stackwallet/pages/notification_views/notifications_view.dart'; +import 'package:stackwallet/pages/ordinals/ordinal_details_view.dart'; +import 'package:stackwallet/pages/ordinals/ordinals_filter_view.dart'; +import 'package:stackwallet/pages/ordinals/ordinals_view.dart'; import 'package:stackwallet/pages/paynym/add_new_paynym_follow_view.dart'; import 'package:stackwallet/pages/paynym/paynym_claim_view.dart'; import 'package:stackwallet/pages/paynym/paynym_home_view.dart'; @@ -103,14 +109,16 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/support_vi import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_options_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_preferences_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/wallet_syncing_options_view.dart'; -import 'package:stackwallet/pages/settings_views/global_settings_view/xpub_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_recovery_phrase_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_warning_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/rename_wallet_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart'; +import 'package:stackwallet/pages/special/firo_rescan_recovery_error_dialog.dart'; import 'package:stackwallet/pages/stack_privacy_calls.dart'; import 'package:stackwallet/pages/token_view/my_tokens_view.dart'; import 'package:stackwallet/pages/token_view/token_contract_details_view.dart'; @@ -140,6 +148,8 @@ import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart'; import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart'; import 'package:stackwallet/pages_desktop_specific/notifications/desktop_notifications_view.dart'; +import 'package:stackwallet/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart'; +import 'package:stackwallet/pages_desktop_specific/ordinals/desktop_ordinals_view.dart'; import 'package:stackwallet/pages_desktop_specific/password/create_password_view.dart'; import 'package:stackwallet/pages_desktop_specific/password/delete_password_warning_view.dart'; import 'package:stackwallet/pages_desktop_specific/password/forgot_password_desktop_view.dart'; @@ -164,8 +174,6 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/widgets/choose_coin_view.dart'; import 'package:tuple/tuple.dart'; -import 'models/isar/models/contact_entry.dart'; - /* * This file contains all the routes for the app. * To add a new route, add it to the switch statement in the generateRoute method. @@ -257,6 +265,20 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case FiroRescanRecoveryErrorView.routeName: + if (args is String) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => FiroRescanRecoveryErrorView( + walletId: args, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + case WalletsView.routeName: return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, @@ -375,6 +397,35 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case MonkeyView.routeName: + if (args is String) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => MonkeyView( + walletId: args, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + + // case MonkeyLoadedView.routeName: + // if (args is Tuple2<String, ChangeNotifierProvider<Manager>>) { + // return getRoute( + // shouldUseMaterialRoute: useMaterialPageRoute, + // builder: (_) => MonkeyLoadedView( + // walletId: args.item1, + // managerProvider: args.item2, + // ), + // settings: RouteSettings( + // name: settings.name, + // ), + // ); + // } + // return _routeError("${settings.name} invalid args: ${args.toString()}"); + case CoinControlView.routeName: if (args is Tuple2<String, CoinControlViewType>) { return getRoute( @@ -404,6 +455,70 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case OrdinalsView.routeName: + if (args is String) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => OrdinalsView( + walletId: args, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + + case DesktopOrdinalsView.routeName: + if (args is String) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => DesktopOrdinalsView( + walletId: args, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + + case OrdinalDetailsView.routeName: + if (args is ({Ordinal ordinal, String walletId})) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => OrdinalDetailsView( + walletId: args.walletId, + ordinal: args.ordinal, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + + case DesktopOrdinalDetailsView.routeName: + if (args is ({Ordinal ordinal, String walletId})) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => DesktopOrdinalDetailsView( + walletId: args.walletId, + ordinal: args.ordinal, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + + case OrdinalsFilterView.routeName: + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => const OrdinalsFilterView(), + settings: RouteSettings(name: settings.name)); + case UtxoDetailsView.routeName: if (args is Tuple2<Id, String>) { return getRoute( @@ -564,6 +679,20 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case ChangeRepresentativeView.routeName: + if (args is String) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => ChangeRepresentativeView( + walletId: args, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + case AppearanceSettingsView.routeName: return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, diff --git a/lib/services/coins/banano/banano_wallet.dart b/lib/services/coins/banano/banano_wallet.dart index 8511f5599..e2032aa09 100644 --- a/lib/services/coins/banano/banano_wallet.dart +++ b/lib/services/coins/banano/banano_wallet.dart @@ -4,6 +4,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:isar/isar.dart'; import 'package:nanodart/nanodart.dart'; +import 'package:stackwallet/db/hive/db.dart'; import 'package:stackwallet/db/isar/main_db.dart'; import 'package:stackwallet/models/balance.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; @@ -14,9 +15,9 @@ import 'package:stackwallet/services/event_bus/events/global/node_connection_sta import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart'; -import 'package:stackwallet/services/mixins/coin_control_interface.dart'; import 'package:stackwallet/services/mixins/wallet_cache.dart'; import 'package:stackwallet/services/mixins/wallet_db.dart'; +import 'package:stackwallet/services/nano_api.dart'; import 'package:stackwallet/services/node_service.dart'; import 'package:stackwallet/services/transaction_notification_tracker.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; @@ -32,8 +33,7 @@ const int MINIMUM_CONFIRMATIONS = 1; const String DEFAULT_REPRESENTATIVE = "ban_1ka1ium4pfue3uxtntqsrib8mumxgazsjf58gidh1xeo5te3whsq8z476goo"; -class BananoWallet extends CoinServiceAPI - with WalletCache, WalletDB, CoinControlInterface { +class BananoWallet extends CoinServiceAPI with WalletCache, WalletDB { BananoWallet({ required String walletId, required String walletName, @@ -925,4 +925,66 @@ class BananoWallet extends CoinServiceAPI ); await updateCachedChainHeight(height ?? 0); } + + Future<void> updateMonkeyImageBytes(List<int> bytes) async { + await DB.instance.put<dynamic>( + boxName: _walletId, + key: "monkeyImageBytesKey", + value: bytes, + ); + } + + List<int>? getMonkeyImageBytes() { + return DB.instance.get<dynamic>( + boxName: _walletId, + key: "monkeyImageBytesKey", + ) as List<int>?; + } + + Future<String> getCurrentRepresentative() async { + final serverURI = Uri.parse(getCurrentNode().host); + final address = await currentReceivingAddress; + + final response = await NanoAPI.getAccountInfo( + server: serverURI, + representative: true, + account: address, + ); + + return response.accountInfo?.representative ?? DEFAULT_REPRESENTATIVE; + } + + Future<bool> changeRepresentative(String newRepresentative) async { + try { + final serverURI = Uri.parse(getCurrentNode().host); + final balance = this.balance.spendable.raw.toString(); + final String privateKey = await getPrivateKeyFromMnemonic(); + final address = await currentReceivingAddress; + + final response = await NanoAPI.getAccountInfo( + server: serverURI, + representative: true, + account: address, + ); + + if (response.accountInfo == null) { + throw response.exception ?? Exception("Failed to get account info"); + } + + final work = await requestWork(response.accountInfo!.frontier); + + return await NanoAPI.changeRepresentative( + server: serverURI, + accountType: NanoAccountType.BANANO, + account: address, + newRepresentative: newRepresentative, + previousBlock: response.accountInfo!.frontier, + balance: balance, + privateKey: privateKey, + work: work!, + ); + } catch (_) { + rethrow; + } + } } diff --git a/lib/services/coins/coin_service.dart b/lib/services/coins/coin_service.dart index 00a52eba5..5aa6e382d 100644 --- a/lib/services/coins/coin_service.dart +++ b/lib/services/coins/coin_service.dart @@ -27,6 +27,7 @@ import 'package:stackwallet/services/coins/monero/monero_wallet.dart'; import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart'; import 'package:stackwallet/services/coins/nano/nano_wallet.dart'; import 'package:stackwallet/services/coins/particl/particl_wallet.dart'; +import 'package:stackwallet/services/coins/stellar/stellar_wallet.dart'; import 'package:stackwallet/services/coins/wownero/wownero_wallet.dart'; import 'package:stackwallet/services/transaction_notification_tracker.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; @@ -218,6 +219,15 @@ abstract class CoinServiceAPI { cachedClient: cachedClient, tracker: tracker); + case Coin.stellar: + return StellarWallet( + walletId: walletId, + walletName: walletName, + coin: coin, + secureStore: secureStorageInterface, + tracker: tracker, + ); + case Coin.wownero: return WowneroWallet( walletId: walletId, @@ -275,6 +285,15 @@ abstract class CoinServiceAPI { cachedClient: cachedClient, tracker: tracker, ); + + case Coin.stellarTestnet: + return StellarWallet( + walletId: walletId, + walletName: walletName, + coin: coin, + secureStore: secureStorageInterface, + tracker: tracker, + ); } } diff --git a/lib/services/coins/ethereum/ethereum_wallet.dart b/lib/services/coins/ethereum/ethereum_wallet.dart index 9990406d9..62f3020e0 100644 --- a/lib/services/coins/ethereum/ethereum_wallet.dart +++ b/lib/services/coins/ethereum/ethereum_wallet.dart @@ -1023,6 +1023,7 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB { final response = await EthereumAPI.getEthTransactions( address: thisAddress, firstBlock: isRescan ? 0 : firstBlock, + includeTokens: true, ); if (response.value == null) { @@ -1057,8 +1058,10 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB { txFailed = true; } isIncoming = false; - } else { + } else if (checksumEthereumAddress(element.to) == thisAddress) { isIncoming = true; + } else { + continue; } //Calculate fees (GasLimit * gasPrice) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index e62b7d069..a4cea6393 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -21,12 +21,12 @@ import 'package:decimal/decimal.dart'; import 'package:flutter/foundation.dart'; import 'package:isar/isar.dart'; import 'package:lelantus/lelantus.dart'; +import 'package:stackwallet/db/hive/db.dart'; import 'package:stackwallet/db/isar/main_db.dart'; import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart'; import 'package:stackwallet/electrumx_rpc/electrumx.dart'; import 'package:stackwallet/models/balance.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models; -import 'package:stackwallet/models/lelantus_coin.dart'; import 'package:stackwallet/models/lelantus_fee_data.dart'; import 'package:stackwallet/models/paymint/fee_object_model.dart'; import 'package:stackwallet/models/signing_data.dart'; @@ -36,7 +36,6 @@ import 'package:stackwallet/services/event_bus/events/global/refresh_percent_cha import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart'; -import 'package:stackwallet/services/mixins/firo_hive.dart'; import 'package:stackwallet/services/mixins/wallet_cache.dart'; import 'package:stackwallet/services/mixins/wallet_db.dart'; import 'package:stackwallet/services/mixins/xpubable.dart'; @@ -60,8 +59,8 @@ import 'package:uuid/uuid.dart'; const DUST_LIMIT = 1000; const MINIMUM_CONFIRMATIONS = 1; -const MINT_LIMIT = 100100000000; -const int LELANTUS_VALUE_SPEND_LIMIT_PER_TRANSACTION = 5001 * 100000000; +const MINT_LIMIT = 5001 * 100000000; +const MINT_LIMIT_TESTNET = 1001 * 100000000; const JMINT_INDEX = 5; const MINT_INDEX = 2; @@ -161,6 +160,7 @@ Future<void> executeNative(Map<String, dynamic> arguments) async { final mnemonicPassphrase = arguments['mnemonicPassphrase'] as String; final coin = arguments['coin'] as Coin; final network = arguments['network'] as NetworkType; + final walletId = arguments['walletId'] as String; final restoreData = await isolateRestore( mnemonic, @@ -170,6 +170,7 @@ Future<void> executeNative(Map<String, dynamic> arguments) async { setDataMap, usedSerialNumbers, network, + walletId, ); sendPort.send(restoreData); return; @@ -206,13 +207,14 @@ Future<Map<String, dynamic>> isolateRestore( Map<dynamic, dynamic> _setDataMap, List<String> _usedSerialNumbers, NetworkType network, + String walletId, ) async { List<int> jindexes = []; - List<Map<dynamic, LelantusCoin>> lelantusCoins = []; + List<isar_models.LelantusCoin> lelantusCoins = []; final List<String> spendTxIds = []; - var lastFoundIndex = 0; - var currentIndex = 0; + int lastFoundIndex = 0; + int currentIndex = 0; try { Set<String> usedSerialNumbersSet = _usedSerialNumbers.toSet(); @@ -239,7 +241,7 @@ Future<Map<String, dynamic>> isolateRestore( isTestnet: coin == Coin.firoTestNet, ); - for (var setId = 1; setId <= _latestSetId; setId++) { + for (int setId = 1; setId <= _latestSetId; setId++) { final setData = _setDataMap[setId] as Map; final foundCoin = (setData["coins"] as List).firstWhere( (e) => e[1] == mintTag, @@ -264,32 +266,25 @@ Future<Map<String, dynamic>> isolateRestore( isTestnet: coin == Coin.firoTestNet, ); final bool isUsed = usedSerialNumbersSet.contains(serialNumber); - final duplicateCoin = lelantusCoins.firstWhere( - (element) { - final coin = element.values.first; - return coin.txId == txId && - coin.index == currentIndex && - coin.anonymitySetId != setId; - }, - orElse: () => {}, + + lelantusCoins.removeWhere((e) => + e.txid == txId && + e.mintIndex == currentIndex && + e.anonymitySetId != setId); + + lelantusCoins.add( + isar_models.LelantusCoin( + walletId: walletId, + mintIndex: currentIndex, + value: amount.toString(), + txid: txId, + anonymitySetId: setId, + isUsed: isUsed, + isJMint: false, + otherData: + publicCoin, // not really needed but saved just in case + ), ); - if (duplicateCoin.isNotEmpty) { - Logging.instance.log( - "Firo isolateRestore removing duplicate coin: $duplicateCoin", - level: LogLevel.Info, - ); - lelantusCoins.remove(duplicateCoin); - } - lelantusCoins.add({ - txId: LelantusCoin( - currentIndex, - amount, - publicCoin, - txId, - setId, - isUsed, - ) - }); Logging.instance.log( "amount $amount used $isUsed", level: LogLevel.Info, @@ -322,32 +317,24 @@ Future<Map<String, dynamic>> isolateRestore( isTestnet: coin == Coin.firoTestNet, ); bool isUsed = usedSerialNumbersSet.contains(serialNumber); - final duplicateCoin = lelantusCoins.firstWhere( - (element) { - final coin = element.values.first; - return coin.txId == txId && - coin.index == currentIndex && - coin.anonymitySetId != setId; - }, - orElse: () => {}, + lelantusCoins.removeWhere((e) => + e.txid == txId && + e.mintIndex == currentIndex && + e.anonymitySetId != setId); + + lelantusCoins.add( + isar_models.LelantusCoin( + walletId: walletId, + mintIndex: currentIndex, + value: amount.toString(), + txid: txId, + anonymitySetId: setId, + isUsed: isUsed, + isJMint: true, + otherData: + publicCoin, // not really needed but saved just in case + ), ); - if (duplicateCoin.isNotEmpty) { - Logging.instance.log( - "Firo isolateRestore removing duplicate coin: $duplicateCoin", - level: LogLevel.Info, - ); - lelantusCoins.remove(duplicateCoin); - } - lelantusCoins.add({ - txId: LelantusCoin( - currentIndex, - amount, - publicCoin, - txId, - setId, - isUsed, - ) - }); jindexes.add(currentIndex); spendTxIds.add(txId); @@ -363,11 +350,6 @@ Future<Map<String, dynamic>> isolateRestore( level: LogLevel.Warning, ); } - } else { - Logging.instance.log( - "Coin not found in data with the mint tag: $mintTag", - level: LogLevel.Warning, - ); } } @@ -384,8 +366,6 @@ Future<Map<String, dynamic>> isolateRestore( // Logging.instance.log("jmints $spendTxIds", addToDebugMessagesDB: false); result['_lelantus_coins'] = lelantusCoins; - result['mintIndex'] = lastFoundIndex + 1; - result['jindex'] = jindexes; result['spendTxIds'] = spendTxIds; return result; @@ -396,73 +376,74 @@ Future<Map<dynamic, dynamic>> staticProcessRestore( Map<dynamic, dynamic> result, int currentHeight, ) async { - List<dynamic>? _l = result['_lelantus_coins'] as List?; - final List<Map<dynamic, LelantusCoin>> lelantusCoins = []; - for (var el in _l ?? []) { - lelantusCoins.add({el.keys.first: el.values.first as LelantusCoin}); - } + List<isar_models.LelantusCoin> lelantusCoins = + result['_lelantus_coins'] as List<isar_models.LelantusCoin>; // Edit the receive transactions with the mint fees. - Map<String, isar_models.Transaction> editedTransactions = - <String, isar_models.Transaction>{}; - for (var item in lelantusCoins) { - item.forEach((key, value) { - String txid = value.txId; - isar_models.Transaction? tx; + List<isar_models.Transaction> editedTransactions = []; + + for (final coin in lelantusCoins) { + String txid = coin.txid; + isar_models.Transaction? tx; + try { + tx = txns.firstWhere((e) => e.txid == txid); + } catch (_) { + tx = null; + } + + if (tx == null || tx.subType == isar_models.TransactionSubType.join) { + // This is a jmint. + continue; + } + + List<isar_models.Transaction> inputTxns = []; + for (final input in tx.inputs) { + isar_models.Transaction? inputTx; try { - tx = txns.firstWhere((e) => e.txid == txid); + inputTx = txns.firstWhere((e) => e.txid == input.txid); } catch (_) { - tx = null; + inputTx = null; } + if (inputTx != null) { + inputTxns.add(inputTx); + } + } + if (inputTxns.isEmpty) { + //some error. + Logging.instance.log( + "cryptic \"//some error\" occurred in staticProcessRestore on lelantus coin: $coin", + level: LogLevel.Error, + ); + continue; + } - if (tx == null || tx.subType == isar_models.TransactionSubType.join) { - // This is a jmint. - return; - } - List<isar_models.Transaction> inputs = []; - for (var element in tx.inputs) { - isar_models.Transaction? input; - try { - input = txns.firstWhere((e) => e.txid == element.txid); - } catch (_) { - input = null; - } - if (input != null) { - inputs.add(input); - } - } - if (inputs.isEmpty) { - //some error. - return; - } - - int mintFee = tx.fee; - int sharedFee = mintFee ~/ inputs.length; - for (var element in inputs) { - editedTransactions[element.txid] = isar_models.Transaction( - walletId: element.walletId, - txid: element.txid, - timestamp: element.timestamp, - type: element.type, - subType: isar_models.TransactionSubType.mint, - amount: element.amount, - amountString: Amount( - rawValue: BigInt.from(element.amount), - fractionDigits: Coin.firo.decimals, - ).toJsonString(), - fee: sharedFee, - height: element.height, - isCancelled: false, - isLelantus: true, - slateId: null, - otherData: txid, - nonce: null, - inputs: element.inputs, - outputs: element.outputs, - numberOfMessages: null, - )..address.value = element.address.value; - } - }); + int mintFee = tx.fee; + int sharedFee = mintFee ~/ inputTxns.length; + for (final inputTx in inputTxns) { + final edited = isar_models.Transaction( + walletId: inputTx.walletId, + txid: inputTx.txid, + timestamp: inputTx.timestamp, + type: inputTx.type, + subType: isar_models.TransactionSubType.mint, + amount: inputTx.amount, + amountString: Amount( + rawValue: BigInt.from(inputTx.amount), + fractionDigits: Coin.firo.decimals, + ).toJsonString(), + fee: sharedFee, + height: inputTx.height, + isCancelled: false, + isLelantus: true, + slateId: null, + otherData: txid, + nonce: null, + inputs: inputTx.inputs, + outputs: inputTx.outputs, + numberOfMessages: null, + )..address.value = inputTx.address.value; + editedTransactions.add(edited); + } } // Logging.instance.log(editedTransactions, addToDebugMessagesDB: false); @@ -472,12 +453,13 @@ Future<Map<dynamic, dynamic>> staticProcessRestore( } // Logging.instance.log(transactionMap, addToDebugMessagesDB: false); - editedTransactions.forEach((key, value) { - transactionMap.update(key, (_value) => value); - }); + // update with edited transactions + for (final tx in editedTransactions) { + transactionMap[tx.txid] = tx; + } transactionMap.removeWhere((key, value) => - lelantusCoins.any((element) => element.containsKey(key)) || + lelantusCoins.any((element) => element.txid == key) || ((value.height == -1 || value.height == null) && !value.isConfirmed(currentHeight, MINIMUM_CONFIRMATIONS))); @@ -694,7 +676,6 @@ Future<dynamic> isolateCreateJoinSplitTransaction( "fee": fee, "vSize": extTx.virtualSize(), "jmintValue": changeToMint, - "publicCoin": "jmintData.publicCoin", "spendCoinIndexes": spendCoinIndexes, "height": locktime, "txType": "Sent", @@ -763,7 +744,7 @@ Future<void> _setTestnetWrapper(bool isTestnet) async { /// Handles a single instance of a firo wallet class FiroWallet extends CoinServiceAPI - with WalletCache, WalletDB, FiroHive + with WalletCache, WalletDB implements XPubAble { // Constructor FiroWallet({ @@ -784,7 +765,6 @@ class FiroWallet extends CoinServiceAPI _cachedElectrumXClient = cachedClient; _secureStore = secureStore; initCache(walletId, coin); - initFiroHive(walletId); initWalletDB(mockableOverride: mockableOverride); Logging.instance.log("$walletName isolates length: ${isolates.length}", @@ -1151,7 +1131,8 @@ class FiroWallet extends CoinServiceAPI } } } catch (e, s) { - Logging.instance.log("Exception rethrown from prepareSend(): $e\n$s", + Logging.instance.log( + "Exception rethrown from prepareSendPublic(): $e\n$s", level: LogLevel.Error); rethrow; } @@ -1159,7 +1140,8 @@ class FiroWallet extends CoinServiceAPI throw ArgumentError("Invalid fee rate argument provided!"); } } catch (e, s) { - Logging.instance.log("Exception rethrown from prepareSend(): $e\n$s", + Logging.instance.log( + "Exception rethrown from prepareSendPublic(): $e\n$s", level: LogLevel.Error); rethrow; } @@ -1188,6 +1170,11 @@ class FiroWallet extends CoinServiceAPI required Amount amount, Map<String, dynamic>? args, }) async { + if (amount.raw > BigInt.from(MINT_LIMIT)) { + throw Exception( + "Lelantus sends of more than 5001 are currently disabled"); + } + try { // check for send all bool isSendAll = false; @@ -1237,18 +1224,6 @@ class FiroWallet extends CoinServiceAPI try { final txid = txData["txid"] as String; - // temporarily update apdate available balance until a full refresh is done - - // TODO: something here causes an exception to be thrown giving user false info that the tx failed - // Decimal sendTotal = - // Format.satoshisToAmount(txData["value"] as int, coin: coin); - // sendTotal += Decimal.parse(txData["fees"].toString()); - - // TODO: is this needed? - // final bals = await balances; - // bals[0] -= sendTotal; - // _balances = Future(() => bals); - return txid; } catch (e, s) { //todo: come back to this @@ -1264,53 +1239,6 @@ class FiroWallet extends CoinServiceAPI } } - // /// returns txid on successful send - // /// - // /// can throw - // @override - // Future<String> send({ - // required String toAddress, - // required int amount, - // Map<String, String> args = const {}, - // }) async { - // try { - // dynamic txHexOrError = - // await _createJoinSplitTransaction(amount, toAddress, false); - // Logging.instance.log("txHexOrError $txHexOrError", level: LogLevel.Error); - // if (txHexOrError is int) { - // // Here, we assume that transaction crafting returned an error - // switch (txHexOrError) { - // case 1: - // throw Exception("Insufficient balance!"); - // default: - // throw Exception("Error Creating Transaction!"); - // } - // } else { - // if (await _submitLelantusToNetwork( - // txHexOrError as Map<String, dynamic>)) { - // final txid = txHexOrError["txid"] as String; - // - // // temporarily update apdate available balance until a full refresh is done - // Decimal sendTotal = - // Format.satoshisToAmount(txHexOrError["value"] as int, coin: coin); - // sendTotal += Decimal.parse(txHexOrError["fees"].toString()); - // final bals = await balances; - // bals[0] -= sendTotal; - // _balances = Future(() => bals); - // - // return txid; - // } else { - // //TODO provide more info - // throw Exception("Transaction failed."); - // } - // } - // } catch (e, s) { - // Logging.instance.log("Exception rethrown in firo send(): $e\n$s", - // level: LogLevel.Error); - // rethrow; - // } - // } - Future<List<String>> _getMnemonicList() async { final _mnemonicString = await mnemonicString; if (_mnemonicString == null) { @@ -1442,16 +1370,32 @@ class FiroWallet extends CoinServiceAPI feeRatePerKB: selectedTxFeeRate, ); - if (feeForOneOutput < vSizeForOneOutput + 1) { - feeForOneOutput = vSizeForOneOutput + 1; - } - - final int amount = satoshiAmountToSend - feeForOneOutput; + int amount = satoshiAmountToSend - feeForOneOutput; dynamic txn = await buildTransaction( utxoSigningData: utxoSigningData, recipients: recipientsArray, satoshiAmounts: [amount], ); + + int count = 0; + int fee = feeForOneOutput; + int vsize = txn["vSize"] as int; + + while (fee < vsize && count < 10) { + // 10 being some reasonable max + count++; + fee += count; + amount = satoshiAmountToSend - fee; + + txn = await buildTransaction( + utxoSigningData: utxoSigningData, + recipients: recipientsArray, + satoshiAmounts: [amount], + ); + + vsize = txn["vSize"] as int; + } + Map<String, dynamic> transactionObject = { "hex": txn["hex"], "recipient": recipientsArray[0], @@ -1952,14 +1896,44 @@ class FiroWallet extends CoinServiceAPI await Future.wait([ updateCachedId(walletId), updateCachedIsFavorite(false), + setLelantusCoinIsarRescanRequiredDone(), ]); } + static const String _lelantusCoinIsarRescanRequired = + "lelantusCoinIsarRescanRequired"; + + Future<void> setLelantusCoinIsarRescanRequiredDone() async { + await DB.instance.put<dynamic>( + boxName: walletId, + key: _lelantusCoinIsarRescanRequired, + value: false, + ); + } + + bool get lelantusCoinIsarRescanRequired => + DB.instance.get( + boxName: walletId, + key: _lelantusCoinIsarRescanRequired, + ) as bool? ?? + true; + + Future<bool> firoRescanRecovery() async { + try { + await fullRescan(50, 1000); + await setLelantusCoinIsarRescanRequiredDone(); + return true; + } catch (_) { + return false; + } + } + @override Future<void> initializeExisting() async { Logging.instance.log( - "initializeExisting() $_walletId ${coin.prettyName} wallet.", - level: LogLevel.Info); + "initializeExisting() $_walletId ${coin.prettyName} wallet.", + level: LogLevel.Info, + ); if (getCachedId() == null) { throw Exception( @@ -2192,7 +2166,6 @@ class FiroWallet extends CoinServiceAPI value: "", ); - await firoUpdateJIndex(<dynamic>[]); // Generate and add addresses to relevant arrays final initialReceivingAddress = await _generateAddressForChain(0, 0); final initialChangeAddress = await _generateAddressForChain(1, 0); @@ -2248,9 +2221,9 @@ class FiroWallet extends CoinServiceAPI _feeObject = Future(() => feeObj); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.60, walletId)); - final lelantusCoins = getLelantusCoinMap(); - Logging.instance.log("_lelantus_coins at refresh: $lelantusCoins", - level: LogLevel.Warning, printFullLength: true); + // final lelantusCoins = getLelantusCoinMap(); + // Logging.instance.log("_lelantus_coins at refresh: $lelantusCoins", + // level: LogLevel.Warning, printFullLength: true); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.70, walletId)); await _refreshLelantusData(); @@ -2327,7 +2300,8 @@ class FiroWallet extends CoinServiceAPI level: LogLevel.Error); } - final List<LelantusCoin> lelantusCoins = await _getUnspentCoins(); + final List<isar_models.LelantusCoin> lelantusCoins = + await _getUnspentCoins(); final root = await Bip32Utils.getBip32Root( _mnemonic!, @@ -2339,7 +2313,7 @@ class FiroWallet extends CoinServiceAPI final derivePath = constructDerivePath( networkWIF: _network.wif, chain: MINT_INDEX, - index: coin.index, + index: coin.mintIndex, ); final keyPair = await Bip32Utils.getBip32NodeFromRoot(root, derivePath); @@ -2349,76 +2323,34 @@ class FiroWallet extends CoinServiceAPI } final String privateKey = Format.uint8listToString(keyPair.privateKey!); return DartLelantusEntry(coin.isUsed ? 1 : 0, 0, coin.anonymitySetId, - coin.value, coin.index, privateKey); + int.parse(coin.value), coin.mintIndex, privateKey); }).toList(); final lelantusEntries = await Future.wait(waitLelantusEntries); if (lelantusEntries.isNotEmpty) { + // should be redundant as _getUnspentCoins() should + // already remove all where value=0 lelantusEntries.removeWhere((element) => element.amount == 0); } return lelantusEntries; } - List<Map<dynamic, LelantusCoin>> getLelantusCoinMap() { - final _l = firoGetLelantusCoins(); - final List<Map<dynamic, LelantusCoin>> lelantusCoins = []; - for (var el in _l ?? []) { - lelantusCoins.add({el.keys.first: el.values.first as LelantusCoin}); - } - return lelantusCoins; - } + Future<List<isar_models.LelantusCoin>> _getUnspentCoins() async { + final lelantusCoinsList = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .isUsedEqualTo(false) + .not() + .group((q) => q + .valueEqualTo("0") + .or() + .anonymitySetIdEqualTo(ANONYMITY_SET_EMPTY_ID)) + .findAll(); - Future<List<LelantusCoin>> _getUnspentCoins() async { - final List<Map<dynamic, LelantusCoin>> lelantusCoins = getLelantusCoinMap(); - if (lelantusCoins.isNotEmpty) { - lelantusCoins.removeWhere((element) => - element.values.any((elementCoin) => elementCoin.value == 0)); - } - final jindexes = firoGetJIndex(); - - List<LelantusCoin> coins = []; - - List<LelantusCoin> lelantusCoinsList = - lelantusCoins.fold(<LelantusCoin>[], (previousValue, element) { - previousValue.add(element.values.first); - return previousValue; - }); - - final currentChainHeight = await chainHeight; - - for (int i = 0; i < lelantusCoinsList.length; i++) { - // Logging.instance.log("lelantusCoinsList[$i]: ${lelantusCoinsList[i]}"); - final txid = lelantusCoinsList[i].txId; - final txn = await cachedElectrumXClient.getTransaction( - txHash: txid, - verbose: true, - coin: coin, - ); - final confirmations = txn["confirmations"]; - bool isUnconfirmed = confirmations is int && confirmations < 1; - - final tx = await db.getTransaction(walletId, txid); - - if (!jindexes!.contains(lelantusCoinsList[i].index) && - tx != null && - tx.isLelantus == true && - !(tx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS))) { - isUnconfirmed = true; - } - - if (tx != null && - !tx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - continue; - } - if (!lelantusCoinsList[i].isUsed && - lelantusCoinsList[i].anonymitySetId != ANONYMITY_SET_EMPTY_ID && - !isUnconfirmed) { - coins.add(lelantusCoinsList[i]); - } - } - return coins; + return lelantusCoinsList; } // index 0 and 1 for the funds available to spend. @@ -2427,62 +2359,48 @@ class FiroWallet extends CoinServiceAPI Future<void> _refreshBalance() async { try { final utxosUpdateFuture = _refreshUTXOs(); - final List<Map<dynamic, LelantusCoin>> lelantusCoins = - getLelantusCoinMap(); - if (lelantusCoins.isNotEmpty) { - lelantusCoins.removeWhere((element) => - element.values.any((elementCoin) => elementCoin.value == 0)); - } + final lelantusCoins = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .isUsedEqualTo(false) + .not() + .valueEqualTo(0.toString()) + .findAll(); final currentChainHeight = await chainHeight; - final jindexes = firoGetJIndex(); int intLelantusBalance = 0; int unconfirmedLelantusBalance = 0; - for (final element in lelantusCoins) { - element.forEach((key, lelantusCoin) { - isar_models.Transaction? txn = db.isar.transactions - .where() - .txidWalletIdEqualTo( - lelantusCoin.txId, - walletId, - ) - .findFirstSync(); + for (final lelantusCoin in lelantusCoins) { + isar_models.Transaction? txn = db.isar.transactions + .where() + .txidWalletIdEqualTo( + lelantusCoin.txid, + walletId, + ) + .findFirstSync(); - if (txn == null) { - // TODO: ?????????????????????????????????????? - } else { - bool isLelantus = txn.isLelantus == true; - if (!jindexes!.contains(lelantusCoin.index) && isLelantus) { - if (!lelantusCoin.isUsed && - txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - // mint tx, add value to balance - intLelantusBalance += lelantusCoin.value; - } /* else { - // This coin is not confirmed and may be replaced - }*/ - } else if (jindexes.contains(lelantusCoin.index) && - isLelantus && - !lelantusCoin.isUsed && - !txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - unconfirmedLelantusBalance += lelantusCoin.value; - } else if (jindexes.contains(lelantusCoin.index) && - !lelantusCoin.isUsed) { - intLelantusBalance += lelantusCoin.value; - } else if (!lelantusCoin.isUsed && - (txn.isLelantus == true - ? true - : txn.isConfirmed( - currentChainHeight, MINIMUM_CONFIRMATIONS) != - false)) { - intLelantusBalance += lelantusCoin.value; - } else if (!isLelantus && - txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS) == - false) { - unconfirmedLelantusBalance += lelantusCoin.value; - } + if (txn == null) { + Logging.instance.log( + "Transaction not found in DB for lelantus coin: $lelantusCoin", + level: LogLevel.Fatal, + ); + } else { + if (txn.isLelantus != true) { + Logging.instance.log( + "Bad database state found in $walletName $walletId for _refreshBalance lelantus", + level: LogLevel.Fatal, + ); } - }); + + if (txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { + // mint tx, add value to balance + intLelantusBalance += int.parse(lelantusCoin.value); + } else { + unconfirmedLelantusBalance += int.parse(lelantusCoin.value); + } + } } _balancePrivate = Balance( @@ -2551,17 +2469,19 @@ class FiroWallet extends CoinServiceAPI } } - final List<Map<dynamic, LelantusCoin>> lelantusCoins = getLelantusCoinMap(); - if (lelantusCoins.isNotEmpty) { - lelantusCoins.removeWhere((element) => - element.values.any((elementCoin) => elementCoin.value == 0)); - } + final lelantusCoins = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .not() + .valueEqualTo(0.toString()) + .findAll(); + final data = await _txnData; for (final value in data) { if (value.inputs.isNotEmpty) { for (var element in value.inputs) { - if (lelantusCoins - .any((element) => element.keys.contains(value.txid)) && + if (lelantusCoins.any((e) => e.txid == value.txid) && spendableOutputs.firstWhere( (output) => output?.txid == element.txid, orElse: () => null) != @@ -2626,24 +2546,93 @@ class FiroWallet extends CoinServiceAPI } Future<List<Map<String, dynamic>>> createMintsFromAmount(int total) async { - var tmpTotal = total; - var index = 1; - var mints = <Map<String, dynamic>>[]; - final nextFreeMintIndex = firoGetMintIndex(); + if (total > MINT_LIMIT) { + throw Exception( + "Lelantus mints of more than 5001 are currently disabled"); + } + + int tmpTotal = total; + int counter = 0; + final lastUsedIndex = await db.getHighestUsedMintIndex(walletId: walletId); + final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; + + final root = await Bip32Utils.getBip32Root( + (await mnemonic).join(" "), + (await mnemonicPassphrase)!, + _network, + ); + + final mints = <Map<String, dynamic>>[]; while (tmpTotal > 0) { - final mintValue = min(tmpTotal, MINT_LIMIT); - final mint = await _getMintHex( - mintValue, - nextFreeMintIndex + index, + final index = nextFreeMintIndex + counter; + + final bip32.BIP32 mintKeyPair = await Bip32Utils.getBip32NodeFromRoot( + root, + constructDerivePath( + networkWIF: _network.wif, + chain: MINT_INDEX, + index: index, + ), ); - mints.add({ - "value": mintValue, - "script": mint, - "index": nextFreeMintIndex + index, - "publicCoin": "", - }); - tmpTotal = tmpTotal - MINT_LIMIT; - index++; + + final String mintTag = CreateTag( + Format.uint8listToString(mintKeyPair.privateKey!), + index, + Format.uint8listToString(mintKeyPair.identifier), + isTestnet: coin == Coin.firoTestNet, + ); + final List<Map<String, dynamic>> anonymitySets; + try { + anonymitySets = await fetchAnonymitySets(); + } catch (e, s) { + Logging.instance.log( + "Firo needs better internet to create mints: $e\n$s", + level: LogLevel.Fatal, + ); + rethrow; + } + + bool isUsedMintTag = false; + + // stupid dynamic maps + for (final set in anonymitySets) { + final setCoins = set["coins"] as List; + for (final coin in setCoins) { + if (coin[1] == mintTag) { + isUsedMintTag = true; + break; + } + } + if (isUsedMintTag) { + break; + } + } + + if (isUsedMintTag) { + Logging.instance.log( + "Found used index when minting", + level: LogLevel.Warning, + ); + } + + if (!isUsedMintTag) { + final mintValue = min(tmpTotal, + (coin == Coin.firoTestNet ? MINT_LIMIT_TESTNET : MINT_LIMIT)); + final mint = await _getMintHex( + mintValue, + index, + ); + + mints.add({ + "value": mintValue, + "script": mint, + "index": index, + }); + tmpTotal = tmpTotal - + (coin == Coin.firoTestNet ? MINT_LIMIT_TESTNET : MINT_LIMIT); + } + + counter++; } return mints; } @@ -2669,8 +2658,6 @@ class FiroWallet extends CoinServiceAPI int satoshisPerRecipient, List<Map<String, dynamic>> mintsMap, ) async { - //todo: check if print needed - // debugPrint(utxosToUse.toString()); List<String> addressStringsToGet = []; // Populating the addresses to derive @@ -2794,9 +2781,6 @@ class FiroWallet extends CoinServiceAPI amount += utxosToUse[i].value; } - final index = firoGetMintIndex(); - Logging.instance.log("index of mint $index", level: LogLevel.Info); - for (var mintsElement in mintsMap) { Logging.instance.log("using $mintsElement", level: LogLevel.Info); Uint8List mintu8 = @@ -2829,7 +2813,6 @@ class FiroWallet extends CoinServiceAPI rawValue: BigInt.from(fee), fractionDigits: coin.decimals, ).decimal.toDouble(), - "publicCoin": "", "height": height, "txType": "Sent", "confirmed_status": false, @@ -2843,146 +2826,75 @@ class FiroWallet extends CoinServiceAPI }; } + // TODO: verify this function does what we think it does Future<void> _refreshLelantusData() async { - final List<Map<dynamic, LelantusCoin>> lelantusCoins = getLelantusCoinMap(); - final jindexes = firoGetJIndex(); - - // Get all joinsplit transaction ids - - final listLelantusTxData = await db - .getTransactions(walletId) + final lelantusCoins = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) .filter() - .isLelantusEqualTo(true) + .isUsedEqualTo(false) + .not() + .valueEqualTo(0.toString()) .findAll(); - List<String> joinsplits = []; - for (final tx in listLelantusTxData) { - if (tx.subType == isar_models.TransactionSubType.join) { - joinsplits.add(tx.txid); - } - } - for (final coin - in lelantusCoins.fold(<LelantusCoin>[], (previousValue, element) { - (previousValue as List<LelantusCoin>).add(element.values.first); - return previousValue; - })) { - if (jindexes != null) { - if (jindexes.contains(coin.index) && !joinsplits.contains(coin.txId)) { - joinsplits.add(coin.txId); - } - } - } - Map<String, Tuple2<isar_models.Address?, isar_models.Transaction>> data = - {}; - for (final entry in listLelantusTxData) { - data[entry.txid] = Tuple2(entry.address.value, entry); - } + final List<isar_models.LelantusCoin> updatedCoins = []; - // Grab the most recent information on all the joinsplits + final usedSerialNumbersSet = (await getUsedCoinSerials()).toSet(); - final updatedJSplit = await getJMintTransactions( - cachedElectrumXClient, - joinsplits, - coin, + final root = await Bip32Utils.getBip32Root( + (await mnemonic).join(" "), + (await mnemonicPassphrase)!, + _network, ); - final currentChainHeight = await chainHeight; + for (final coin in lelantusCoins) { + final _derivePath = constructDerivePath( + networkWIF: _network.wif, + chain: MINT_INDEX, + index: coin.mintIndex, + ); + final bip32.BIP32 mintKeyPair = await Bip32Utils.getBip32NodeFromRoot( + root, + _derivePath, + ); - // update all of joinsplits that are now confirmed. - for (final tx in updatedJSplit.entries) { - isar_models.Transaction? currentTx; + final String serialNumber = GetSerialNumber( + int.parse(coin.value), + Format.uint8listToString(mintKeyPair.privateKey!), + coin.mintIndex, + isTestnet: this.coin == Coin.firoTestNet, + ); + final bool isUsed = usedSerialNumbersSet.contains(serialNumber); + if (isUsed) { + updatedCoins.add(coin.copyWith(isUsed: isUsed)); + } + + final tx = await db.getTransaction(walletId, coin.txid); + if (tx == null) { + print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + } + } + + if (updatedCoins.isNotEmpty) { try { - currentTx = - listLelantusTxData.firstWhere((e) => e.txid == tx.value.txid); - } catch (_) { - currentTx = null; - } - - if (currentTx == null) { - // this send was accidentally not included in the list - tx.value.isLelantus = true; - data[tx.value.txid] = - Tuple2(tx.value.address.value ?? tx.key, tx.value); - - continue; - } - if (currentTx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS) != - tx.value.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - tx.value.isLelantus = true; - data[tx.value.txid] = - Tuple2(tx.value.address.value ?? tx.key, tx.value); + await db.isar.writeTxn(() async { + for (final c in updatedCoins) { + await db.isar.lelantusCoins.deleteByMintIndexWalletId( + c.mintIndex, + c.walletId, + ); + } + await db.isar.lelantusCoins.putAll(updatedCoins); + }); + } catch (e, s) { + Logging.instance.log( + "$e\n$s", + level: LogLevel.Fatal, + ); + rethrow; } } - - // Logging.instance.log(txData.txChunks); - final listTxData = await _txnData; - for (final value in listTxData) { - // ignore change addresses - // bool hasAtLeastOneReceive = false; - // int howManyReceiveInputs = 0; - // for (var element in value.inputs) { - // if (listLelantusTxData.containsKey(element.txid) && - // listLelantusTxData[element.txid]!.txType == "Received" - // // && - // // listLelantusTxData[element.txid].subType != "mint" - // ) { - // // hasAtLeastOneReceive = true; - // // howManyReceiveInputs++; - // } - // } - - if (value.type == isar_models.TransactionType.incoming && - value.subType != isar_models.TransactionSubType.mint) { - // Every receive other than a mint should be shown. Mints will be collected and shown from the send side - value.isLelantus = true; - data[value.txid] = Tuple2(value.address.value, value); - } else if (value.type == isar_models.TransactionType.outgoing) { - // all sends should be shown, mints will be displayed correctly in the ui - value.isLelantus = true; - data[value.txid] = Tuple2(value.address.value, value); - } - } - - // TODO: optimize this whole lelantus process - - final List<Tuple2<isar_models.Transaction, isar_models.Address?>> txnsData = - []; - - for (final value in data.values) { - // allow possible null address on mints as we don't display address - // this should normally never be null anyways but old (dbVersion up to 4) - // migrated transactions may not have had an address (full rescan should - // fix this) - isar_models.Address? transactionAddress; - try { - transactionAddress = - value.item2.subType == isar_models.TransactionSubType.mint - ? value.item1 - : value.item1!; - } catch (_) { - Logging.instance - .log("_refreshLelantusData value: $value", level: LogLevel.Fatal); - } - final outs = - value.item2.outputs.where((_) => true).toList(growable: false); - final ins = value.item2.inputs.where((_) => true).toList(growable: false); - - txnsData.add(Tuple2( - value.item2.copyWith(inputs: ins, outputs: outs).item1, - transactionAddress)); - } - - await db.addNewTransactionData(txnsData, walletId); - - // // update the _lelantusTransactionData - // final models.TransactionData newTxData = - // models.TransactionData.fromMap(listLelantusTxData); - // // Logging.instance.log(newTxData.txChunks); - // _lelantusTransactionData = Future(() => newTxData); - // await DB.instance.put<dynamic>( - // boxName: walletId, key: 'latest_lelantus_tx_model', value: newTxData); - // return newTxData; } Future<String> _getMintHex(int amount, int index) async { @@ -3030,51 +2942,63 @@ class FiroWallet extends CoinServiceAPI Logging.instance.log( "_submitLelantusToNetwork txid: ${transactionInfo['txid']}", level: LogLevel.Info); + if (txid == transactionInfo['txid']) { - final index = firoGetMintIndex(); - final List<Map<dynamic, LelantusCoin>> lelantusCoins = - getLelantusCoinMap(); - List<Map<dynamic, LelantusCoin>> coins; - if (lelantusCoins.isEmpty) { - coins = []; - } else { - coins = [...lelantusCoins]; - } + final lastUsedIndex = + await db.getHighestUsedMintIndex(walletId: walletId); + final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; if (transactionInfo['spendCoinIndexes'] != null) { // This is a joinsplit + final spentCoinIndexes = + transactionInfo['spendCoinIndexes'] as List<int>; + final List<isar_models.LelantusCoin> updatedCoins = []; + // Update all of the coins that have been spent. - for (final lCoinMap in coins) { - final lCoin = lCoinMap.values.first; - if ((transactionInfo['spendCoinIndexes'] as List<int>) - .contains(lCoin.index)) { - lCoinMap[lCoinMap.keys.first] = LelantusCoin( - lCoin.index, - lCoin.value, - lCoin.publicCoin, - lCoin.txId, - lCoin.anonymitySetId, - true); + + for (final index in spentCoinIndexes) { + final possibleCoin = await db.isar.lelantusCoins + .where() + .mintIndexWalletIdEqualTo(index, walletId) + .findFirst(); + + if (possibleCoin != null) { + updatedCoins.add(possibleCoin.copyWith(isUsed: true)); } } // if a jmint was made add it to the unspent coin index - LelantusCoin jmint = LelantusCoin( - index, - transactionInfo['jmintValue'] as int? ?? 0, - transactionInfo['publicCoin'] as String, - transactionInfo['txid'] as String, - latestSetId, - false); - if (jmint.value > 0) { - coins.add({jmint.txId: jmint}); - final jindexes = firoGetJIndex()!; - jindexes.add(index); - await firoUpdateJIndex(jindexes); - await firoUpdateMintIndex(index + 1); + final jmint = isar_models.LelantusCoin( + walletId: walletId, + mintIndex: nextFreeMintIndex, + value: (transactionInfo['jmintValue'] as int? ?? 0).toString(), + txid: transactionInfo['txid'] as String, + anonymitySetId: latestSetId, + isUsed: false, + isJMint: true, + otherData: null, + ); + + try { + await db.isar.writeTxn(() async { + for (final c in updatedCoins) { + await db.isar.lelantusCoins.deleteByMintIndexWalletId( + c.mintIndex, + c.walletId, + ); + } + await db.isar.lelantusCoins.putAll(updatedCoins); + + await db.isar.lelantusCoins.put(jmint); + }); + } catch (e, s) { + Logging.instance.log( + "$e\n$s", + level: LogLevel.Fatal, + ); + rethrow; } - await firoUpdateLelantusCoins(coins); final amount = Amount.fromDecimal( Decimal.parse(transactionInfo["amount"].toString()), @@ -3087,14 +3011,8 @@ class FiroWallet extends CoinServiceAPI txid: transactionInfo['txid'] as String, timestamp: transactionInfo['timestamp'] as int? ?? (DateTime.now().millisecondsSinceEpoch ~/ 1000), - type: transactionInfo['txType'] == "Received" - ? isar_models.TransactionType.incoming - : isar_models.TransactionType.outgoing, - subType: transactionInfo["subType"] == "mint" - ? isar_models.TransactionSubType.mint - : transactionInfo["subType"] == "join" - ? isar_models.TransactionSubType.join - : isar_models.TransactionSubType.none, + type: isar_models.TransactionType.outgoing, + subType: isar_models.TransactionSubType.join, amount: amount.raw.toInt(), amountString: amount.toJsonString(), fee: Amount.fromDecimal( @@ -3133,40 +3051,40 @@ class FiroWallet extends CoinServiceAPI txnsData.add(Tuple2(transaction, transactionAddress)); await db.addNewTransactionData(txnsData, walletId); - - // final models.TransactionData newTxData = - // models.TransactionData.fromMap(transactions); - // await DB.instance.put<dynamic>( - // boxName: walletId, - // key: 'latest_lelantus_tx_model', - // value: newTxData); - // final ldata = DB.instance.get<dynamic>( - // boxName: walletId, - // key: 'latest_lelantus_tx_model') as models.TransactionData; - // _lelantusTransactionData = Future(() => ldata); } else { // This is a mint Logging.instance.log("this is a mint", level: LogLevel.Info); - // TODO: transactionInfo['mintsMap'] + final List<isar_models.LelantusCoin> updatedCoins = []; + for (final mintMap in transactionInfo['mintsMap'] as List<Map<String, dynamic>>) { - final index = mintMap['index'] as int?; - LelantusCoin mint = LelantusCoin( - index!, - mintMap['value'] as int, - mintMap['publicCoin'] as String, - transactionInfo['txid'] as String, - latestSetId, - false, + final index = mintMap['index'] as int; + final mint = isar_models.LelantusCoin( + walletId: walletId, + mintIndex: index, + value: (mintMap['value'] as int).toString(), + txid: transactionInfo['txid'] as String, + anonymitySetId: latestSetId, + isUsed: false, + isJMint: false, + otherData: null, ); - if (mint.value > 0) { - coins.add({mint.txId: mint}); - await firoUpdateMintIndex(index + 1); - } + + updatedCoins.add(mint); } // Logging.instance.log(coins); - await firoUpdateLelantusCoins(coins); + try { + await db.isar.writeTxn(() async { + await db.isar.lelantusCoins.putAll(updatedCoins); + }); + } catch (e, s) { + Logging.instance.log( + "$e\n$s", + level: LogLevel.Fatal, + ); + rethrow; + } } return true; } else { @@ -3204,20 +3122,6 @@ class FiroWallet extends CoinServiceAPI Logging.instance.log("fetched fees: $feeObject", level: LogLevel.Info); return feeObject; - - // final result = await electrumXClient.getFeeRate(); - // - // final locale = await Devicelocale.currentLocale; - // final String fee = - // Format.satoshiAmountToPrettyString(result["rate"] as int, locale!); - // - // final fees = { - // "fast": fee, - // "average": fee, - // "slow": fee, - // }; - // final FeeObject feeObject = FeeObject.fromJson(fees); - // return feeObject; } catch (e) { Logging.instance .log("Exception rethrown from _getFees(): $e", level: LogLevel.Error); @@ -3666,11 +3570,21 @@ class FiroWallet extends CoinServiceAPI ), ); } + final txid = txObject["txid"] as String; const subType = isar_models.TransactionSubType.join; + final type = nonWalletAddressFoundInOutputs ? isar_models.TransactionType.outgoing - : isar_models.TransactionType.incoming; + : (await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .txidEqualTo(txid) + .findFirst()) == + null + ? isar_models.TransactionType.incoming + : isar_models.TransactionType.sentToSelf; final amount = nonWalletAddressFoundInOutputs ? totalOutputValue @@ -3697,7 +3611,7 @@ class FiroWallet extends CoinServiceAPI final tx = isar_models.Transaction( walletId: walletId, - txid: txObject["txid"] as String, + txid: txid, timestamp: txObject["blocktime"] as int? ?? (DateTime.now().millisecondsSinceEpoch ~/ 1000), type: type, @@ -3718,6 +3632,81 @@ class FiroWallet extends CoinServiceAPI txnsData.add(Tuple2(tx, transactionAddress)); + // Master node payment ===================================== + } else if (inputList.length == 1 && + inputList.first["coinbase"] is String) { + List<isar_models.Input> ins = [ + isar_models.Input( + txid: inputList.first["coinbase"] as String, + vout: -1, + scriptSig: null, + scriptSigAsm: null, + isCoinbase: true, + sequence: inputList.first['sequence'] as int?, + innerRedeemScriptAsm: null, + ), + ]; + + // parse outputs + List<isar_models.Output> outs = []; + for (final output in outputList) { + // get value + final value = Amount.fromDecimal( + Decimal.parse(output["value"].toString()), + fractionDigits: coin.decimals, + ); + + // get output address + final address = output["scriptPubKey"]?["addresses"]?[0] as String? ?? + output["scriptPubKey"]?["address"] as String?; + if (address != null) { + outputAddresses.add(address); + + // if output was to my wallet, add value to amount received + if (receivingAddresses.contains(address)) { + amountReceivedInWallet += value; + } + } + + outs.add( + isar_models.Output( + scriptPubKey: output['scriptPubKey']?['hex'] as String?, + scriptPubKeyAsm: output['scriptPubKey']?['asm'] as String?, + scriptPubKeyType: output['scriptPubKey']?['type'] as String?, + scriptPubKeyAddress: address ?? "", + value: value.raw.toInt(), + ), + ); + } + + // this is the address initially used to fetch the txid + isar_models.Address transactionAddress = + txObject["address"] as isar_models.Address; + + final tx = isar_models.Transaction( + walletId: walletId, + txid: txObject["txid"] as String, + timestamp: txObject["blocktime"] as int? ?? + (DateTime.now().millisecondsSinceEpoch ~/ 1000), + type: isar_models.TransactionType.incoming, + subType: isar_models.TransactionSubType.none, + // amount may overflow. Deprecated. Use amountString + amount: amountReceivedInWallet.raw.toInt(), + amountString: amountReceivedInWallet.toJsonString(), + fee: 0, + height: txObject["height"] as int?, + isCancelled: false, + isLelantus: false, + slateId: null, + otherData: null, + nonce: null, + inputs: ins, + outputs: outs, + numberOfMessages: null, + ); + + txnsData.add(Tuple2(tx, transactionAddress)); + // Assume non lelantus transaction ===================================== } else { // parse inputs @@ -3947,7 +3936,6 @@ class FiroWallet extends CoinServiceAPI coin: coin, ); - // todo check here if we should mark as blocked final utxo = isar_models.UTXO( walletId: walletId, txid: txn["txid"] as String, @@ -3985,7 +3973,6 @@ class FiroWallet extends CoinServiceAPI Logging.instance .log('Outputs fetched: $outputArray', level: LogLevel.Info); - // TODO move this out of here and into IDB await db.isar.writeTxn(() async { await db.isar.utxos.where().walletIdEqualTo(walletId).deleteAll(); await db.isar.utxos.putAll(outputArray); @@ -4072,45 +4059,6 @@ class FiroWallet extends CoinServiceAPI ); } - // /// Takes in a list of isar_models.UTXOs and adds a name (dependent on object index within list) - // /// and checks for the txid associated with the utxo being blocked and marks it accordingly. - // /// Now also checks for output labeling. - // Future<void> _sortOutputs(List<isar_models.UTXO> utxos) async { - // final blockedHashArray = - // DB.instance.get<dynamic>(boxName: walletId, key: 'blocked_tx_hashes') - // as List<dynamic>?; - // final List<String> lst = []; - // if (blockedHashArray != null) { - // for (var hash in blockedHashArray) { - // lst.add(hash as String); - // } - // } - // final labels = - // DB.instance.get<dynamic>(boxName: walletId, key: 'labels') as Map? ?? - // {}; - // - // _outputsList = []; - // - // for (var i = 0; i < utxos.length; i++) { - // if (labels[utxos[i].txid] != null) { - // utxos[i].txName = labels[utxos[i].txid] as String? ?? ""; - // } else { - // utxos[i].txName = 'Output #$i'; - // } - // - // if (utxos[i].status.confirmed == false) { - // _outputsList.add(utxos[i]); - // } else { - // if (lst.contains(utxos[i].txid)) { - // utxos[i].blocked = true; - // _outputsList.add(utxos[i]); - // } else if (!lst.contains(utxos[i].txid)) { - // _outputsList.add(utxos[i]); - // } - // } - // } - // } - @override Future<void> fullRescan( int maxUnusedAddressGap, @@ -4256,6 +4204,7 @@ class FiroWallet extends CoinServiceAPI maxNumberOfIndexesToCheck, false, ); + await setLelantusCoinIsarRescanRequiredDone(); await compute( _setTestnetWrapper, @@ -4620,6 +4569,7 @@ class FiroWallet extends CoinServiceAPI "setDataMap": setDataMap, "usedSerialNumbers": usedSerialNumbers, "network": _network, + "walletId": walletId, }); await Future.wait([dataFuture]); @@ -4638,11 +4588,20 @@ class FiroWallet extends CoinServiceAPI await chainHeight, ); - await Future.wait([ - firoUpdateMintIndex(message['mintIndex'] as int), - firoUpdateLelantusCoins(message['_lelantus_coins'] as List), - firoUpdateJIndex(message['jindex'] as List), - ]); + final coins = message['_lelantus_coins'] as List<isar_models.LelantusCoin>; + + try { + await db.isar.writeTxn(() async { + await db.isar.lelantusCoins.putAll(coins); + }); + } catch (e, s) { + Logging.instance.log( + "$e\n$s", + level: LogLevel.Fatal, + ); + // don't just rethrow since isar likes to strip stack traces for some reason + throw Exception("e=$e & s=$s"); + } final transactionMap = message["newTxMap"] as Map<String, isar_models.Transaction>; @@ -4726,7 +4685,8 @@ class FiroWallet extends CoinServiceAPI int spendAmount, String address, bool subtractFeeFromAmount) async { final _mnemonic = await mnemonicString; final _mnemonicPassphrase = await mnemonicPassphrase; - final index = firoGetMintIndex(); + final lastUsedIndex = await db.getHighestUsedMintIndex(walletId: walletId); + final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; final lelantusEntry = await _getLelantusEntry(); final anonymitySets = await fetchAnonymitySets(); final locktime = await getBlockHead(electrumXClient); @@ -4740,7 +4700,7 @@ class FiroWallet extends CoinServiceAPI "subtractFeeFromAmount": subtractFeeFromAmount, "mnemonic": _mnemonic, "mnemonicPassphrase": _mnemonicPassphrase, - "index": index, + "index": nextFreeMintIndex, // "price": price, "lelantusEntries": lelantusEntry, "locktime": locktime, @@ -4828,75 +4788,6 @@ class FiroWallet extends CoinServiceAPI this.isActive = isActive; }; - Future<dynamic> getCoinsToJoinSplit( - int required, - ) async { - List<DartLelantusEntry> coins = await _getLelantusEntry(); - if (required > LELANTUS_VALUE_SPEND_LIMIT_PER_TRANSACTION) { - return false; - } - - int availableBalance = coins.fold( - 0, (previousValue, element) => previousValue + element.amount); - - if (required > availableBalance) { - return false; - } - - // sort by biggest amount. if it is same amount we will prefer the older block - coins.sort((a, b) => - (a.amount != b.amount ? a.amount > b.amount : a.height < b.height) - ? -1 - : 1); - int spendVal = 0; - - List<DartLelantusEntry> coinsToSpend = []; - - while (spendVal < required) { - if (coins.isEmpty) { - break; - } - - DartLelantusEntry? chosen; - int need = required - spendVal; - - var itr = coins.first; - if (need >= itr.amount) { - chosen = itr; - coins.remove(itr); - } else { - for (int index = coins.length - 1; index != 0; index--) { - var coinIt = coins[index]; - var nextItr = coins[index - 1]; - - if (coinIt.amount >= need && - (index - 1 == 0 || nextItr.amount != coinIt.amount)) { - chosen = coinIt; - coins.remove(chosen); - break; - } - } - } - - // TODO: investigate the bug here where chosen is null, conditions, given one mint - spendVal += chosen!.amount; - coinsToSpend.insert(coinsToSpend.length, chosen); - } - - // sort by group id ay ascending order. it is mandatory for creating proper joinsplit - coinsToSpend.sort((a, b) => a.anonymitySetId < b.anonymitySetId ? 1 : -1); - - int changeToMint = spendVal - required; - List<int> indices = []; - for (var l in coinsToSpend) { - indices.add(l.index); - } - List<DartLelantusEntry> coinsToBeSpentOut = []; - coinsToBeSpentOut.addAll(coinsToSpend); - - return {"changeToMint": changeToMint, "coinsToSpend": coinsToBeSpentOut}; - } - Future<int> estimateJoinSplitFee( int spendAmount, ) async { @@ -4927,36 +4818,6 @@ class FiroWallet extends CoinServiceAPI Logging.instance.log('Closing estimateJoinSplit!', level: LogLevel.Info); return (message as LelantusFeeData).fee; } - // int fee; - // int size; - // - // for (fee = 0;;) { - // int currentRequired = spendAmount; - // - // TODO: investigate the bug here - // var map = await getCoinsToJoinSplit(currentRequired); - // if (map is bool && !map) { - // return 0; - // } - // - // List<DartLelantusEntry> coinsToBeSpent = - // map['coinsToSpend'] as List<DartLelantusEntry>; - // - // // 1054 is constant part, mainly Schnorr and Range proofs, 2560 is for each sigma/aux data - // // 179 other parts of tx, assuming 1 utxo and 1 jmint - // size = 1054 + 2560 * coinsToBeSpent.length + 180; - // // uint64_t feeNeeded = GetMinimumFee(size, DEFAULT_TX_CONFIRM_TARGET); - // int feeNeeded = - // size; //TODO(Levon) temporary, use real estimation methods here - // - // if (fee >= feeNeeded) { - // break; - // } - // - // fee = feeNeeded; - // } - // - // return fee; @override Future<Amount> estimateFeeFor(Amount amount, int feeRate) async { @@ -5019,7 +4880,6 @@ class FiroWallet extends CoinServiceAPI } } - // TODO: correct formula for firo? Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) { return Amount( rawValue: BigInt.from(((181 * inputCount) + (34 * outputCount) + 10) * diff --git a/lib/services/coins/litecoin/litecoin_wallet.dart b/lib/services/coins/litecoin/litecoin_wallet.dart index b84c6c6a0..703ec5cf6 100644 --- a/lib/services/coins/litecoin/litecoin_wallet.dart +++ b/lib/services/coins/litecoin/litecoin_wallet.dart @@ -36,6 +36,7 @@ import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_ import 'package:stackwallet/services/event_bus/global_event_bus.dart'; import 'package:stackwallet/services/mixins/coin_control_interface.dart'; import 'package:stackwallet/services/mixins/electrum_x_parsing.dart'; +import 'package:stackwallet/services/mixins/ordinals_interface.dart'; import 'package:stackwallet/services/mixins/wallet_cache.dart'; import 'package:stackwallet/services/mixins/wallet_db.dart'; import 'package:stackwallet/services/mixins/xpubable.dart'; @@ -109,7 +110,12 @@ String constructDerivePath({ } class LitecoinWallet extends CoinServiceAPI - with WalletCache, WalletDB, ElectrumXParsing, CoinControlInterface + with + WalletCache, + WalletDB, + ElectrumXParsing, + CoinControlInterface, + OrdinalsInterface implements XPubAble { LitecoinWallet({ required String walletId, @@ -130,6 +136,7 @@ class LitecoinWallet extends CoinServiceAPI _secureStore = secureStore; initCache(walletId, coin); initWalletDB(mockableOverride: mockableOverride); + initOrdinalsInterface(walletId: walletId, coin: coin, db: db); initCoinControlInterface( walletId: walletId, walletName: walletName, @@ -1864,14 +1871,6 @@ class LitecoinWallet extends CoinServiceAPI String? blockReason; String? label; - final utxoAmount = jsonUTXO["value"] as int; - - if (utxoAmount <= 10000) { - shouldBlock = true; - blockReason = "May contain ordinal"; - label = "Possible ordinal"; - } - final vout = jsonUTXO["tx_pos"] as int; final outputs = txn["vout"] as List; @@ -1886,6 +1885,25 @@ class LitecoinWallet extends CoinServiceAPI } } + final utxoAmount = jsonUTXO["value"] as int; + + // TODO check the specific output, not just the address in general + // TODO optimize by freezing output in OrdinalsInterface, so one ordinal API calls is made (or at least many less) + if (utxoOwnerAddress != null) { + if (await inscriptionInAddress(utxoOwnerAddress!)) { + shouldBlock = true; + blockReason = "Ordinal"; + label = "Ordinal detected at address"; + } + } else { + // TODO implement inscriptionInOutput + if (utxoAmount <= 10000) { + shouldBlock = true; + blockReason = "May contain ordinal"; + label = "Possible ordinal"; + } + } + final utxo = isar_models.UTXO( walletId: walletId, txid: txn["txid"] as String, @@ -1910,7 +1928,12 @@ class LitecoinWallet extends CoinServiceAPI level: LogLevel.Info, ); - await db.updateUTXOs(walletId, outputArray); + bool inscriptionsRefreshNeeded = + await db.updateUTXOs(walletId, outputArray); + + if (inscriptionsRefreshNeeded) { + await refreshInscriptions(); + } // finally update balance await _updateBalance(); diff --git a/lib/services/coins/manager.dart b/lib/services/coins/manager.dart index 4e3dd460e..1f5ae55f3 100644 --- a/lib/services/coins/manager.dart +++ b/lib/services/coins/manager.dart @@ -21,6 +21,7 @@ import 'package:stackwallet/services/event_bus/events/global/node_connection_sta import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart'; import 'package:stackwallet/services/mixins/coin_control_interface.dart'; +import 'package:stackwallet/services/mixins/ordinals_interface.dart'; import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart'; import 'package:stackwallet/services/mixins/xpubable.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; @@ -244,6 +245,8 @@ class Manager with ChangeNotifier { bool get hasCoinControlSupport => _currentWallet is CoinControlInterface; + bool get hasOrdinalsSupport => _currentWallet is OrdinalsInterface; + bool get hasTokenSupport => _currentWallet.coin == Coin.ethereum; bool get hasWhirlpoolSupport => false; diff --git a/lib/services/coins/nano/nano_wallet.dart b/lib/services/coins/nano/nano_wallet.dart index 1e783bf5f..391303675 100644 --- a/lib/services/coins/nano/nano_wallet.dart +++ b/lib/services/coins/nano/nano_wallet.dart @@ -24,9 +24,9 @@ import 'package:stackwallet/services/event_bus/events/global/node_connection_sta import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart'; -import 'package:stackwallet/services/mixins/coin_control_interface.dart'; import 'package:stackwallet/services/mixins/wallet_cache.dart'; import 'package:stackwallet/services/mixins/wallet_db.dart'; +import 'package:stackwallet/services/nano_api.dart'; import 'package:stackwallet/services/node_service.dart'; import 'package:stackwallet/services/transaction_notification_tracker.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; @@ -42,8 +42,7 @@ const int MINIMUM_CONFIRMATIONS = 1; const String DEFAULT_REPRESENTATIVE = "nano_38713x95zyjsqzx6nm1dsom1jmm668owkeb9913ax6nfgj15az3nu8xkx579"; -class NanoWallet extends CoinServiceAPI - with WalletCache, WalletDB, CoinControlInterface { +class NanoWallet extends CoinServiceAPI with WalletCache, WalletDB { NanoWallet({ required String walletId, required String walletName, @@ -937,4 +936,51 @@ class NanoWallet extends CoinServiceAPI ); await updateCachedChainHeight(height ?? 0); } + + Future<String> getCurrentRepresentative() async { + final serverURI = Uri.parse(getCurrentNode().host); + final address = await currentReceivingAddress; + + final response = await NanoAPI.getAccountInfo( + server: serverURI, + representative: true, + account: address, + ); + + return response.accountInfo?.representative ?? DEFAULT_REPRESENTATIVE; + } + + Future<bool> changeRepresentative(String newRepresentative) async { + try { + final serverURI = Uri.parse(getCurrentNode().host); + final balance = this.balance.spendable.raw.toString(); + final String privateKey = await getPrivateKeyFromMnemonic(); + final address = await currentReceivingAddress; + + final response = await NanoAPI.getAccountInfo( + server: serverURI, + representative: true, + account: address, + ); + + if (response.accountInfo == null) { + throw response.exception ?? Exception("Failed to get account info"); + } + + final work = await requestWork(response.accountInfo!.frontier); + + return await NanoAPI.changeRepresentative( + server: serverURI, + accountType: NanoAccountType.NANO, + account: address, + newRepresentative: newRepresentative, + previousBlock: response.accountInfo!.frontier, + balance: balance, + privateKey: privateKey, + work: work!, + ); + } catch (_) { + rethrow; + } + } } diff --git a/lib/services/coins/stellar/stellar_wallet.dart b/lib/services/coins/stellar/stellar_wallet.dart new file mode 100644 index 000000000..c4c92373f --- /dev/null +++ b/lib/services/coins/stellar/stellar_wallet.dart @@ -0,0 +1,809 @@ +import 'dart:async'; + +import 'package:http/http.dart' as http; +import 'package:isar/isar.dart'; +import 'package:stackwallet/db/isar/main_db.dart'; +import 'package:stackwallet/models/balance.dart' as SWBalance; +import 'package:stackwallet/models/isar/models/blockchain_data/address.dart' + as SWAddress; +import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart' + as SWTransaction; +import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; +import 'package:stackwallet/models/node_model.dart'; +import 'package:stackwallet/models/paymint/fee_object_model.dart'; +import 'package:stackwallet/services/coins/coin_service.dart'; +import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart'; +import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart'; +import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; +import 'package:stackwallet/services/event_bus/global_event_bus.dart'; +import 'package:stackwallet/services/mixins/wallet_cache.dart'; +import 'package:stackwallet/services/mixins/wallet_db.dart'; +import 'package:stackwallet/services/node_service.dart'; +import 'package:stackwallet/services/transaction_notification_tracker.dart'; +import 'package:stackwallet/utilities/amount/amount.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/default_nodes.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart'; +import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; +import 'package:stackwallet/utilities/logger.dart'; +import 'package:stackwallet/utilities/prefs.dart'; +import 'package:stellar_flutter_sdk/stellar_flutter_sdk.dart'; +import 'package:tuple/tuple.dart'; + +const int MINIMUM_CONFIRMATIONS = 1; + +class StellarWallet extends CoinServiceAPI with WalletCache, WalletDB { + late StellarSDK stellarSdk; + late Network stellarNetwork; + + StellarWallet({ + required String walletId, + required String walletName, + required Coin coin, + required TransactionNotificationTracker tracker, + required SecureStorageInterface secureStore, + MainDB? mockableOverride, + }) { + txTracker = tracker; + _walletId = walletId; + _walletName = walletName; + _coin = coin; + _secureStore = secureStore; + initCache(walletId, coin); + initWalletDB(mockableOverride: mockableOverride); + + if (coin.name == "stellarTestnet") { + stellarSdk = StellarSDK.TESTNET; + stellarNetwork = Network.TESTNET; + } else { + stellarSdk = StellarSDK.PUBLIC; + stellarNetwork = Network.PUBLIC; + } + } + + late final TransactionNotificationTracker txTracker; + late SecureStorageInterface _secureStore; + + // final StellarSDK stellarSdk = StellarSDK.PUBLIC; + + @override + bool get isFavorite => _isFavorite ??= getCachedIsFavorite(); + bool? _isFavorite; + + @override + set isFavorite(bool isFavorite) { + _isFavorite = isFavorite; + updateCachedIsFavorite(isFavorite); + } + + @override + bool get shouldAutoSync => _shouldAutoSync; + bool _shouldAutoSync = true; + + Timer? timer; + + final _prefs = Prefs.instance; + + @override + set shouldAutoSync(bool shouldAutoSync) { + if (_shouldAutoSync != shouldAutoSync) { + _shouldAutoSync = shouldAutoSync; + if (!shouldAutoSync) { + timer?.cancel(); + timer = null; + stopNetworkAlivePinging(); + } else { + startNetworkAlivePinging(); + refresh(); + } + } + } + + Timer? _networkAliveTimer; + + void startNetworkAlivePinging() { + // call once on start right away + _periodicPingCheck(); + + // then periodically check + _networkAliveTimer = Timer.periodic( + Constants.networkAliveTimerDuration, + (_) async { + _periodicPingCheck(); + }, + ); + } + + void stopNetworkAlivePinging() { + _networkAliveTimer?.cancel(); + _networkAliveTimer = null; + } + + void _periodicPingCheck() async { + bool hasNetwork = await testNetworkConnection(); + + if (_isConnected != hasNetwork) { + NodeConnectionStatus status = hasNetwork + ? NodeConnectionStatus.connected + : NodeConnectionStatus.disconnected; + + GlobalEventBus.instance.fire( + NodeConnectionStatusChangedEvent( + status, + walletId, + coin, + ), + ); + + _isConnected = hasNetwork; + if (hasNetwork) { + unawaited(refresh()); + } + } + } + + @override + String get walletName => _walletName; + late String _walletName; + + @override + set walletName(String name) => _walletName = name; + + @override + SWBalance.Balance get balance => _balance ??= getCachedBalance(); + SWBalance.Balance? _balance; + + @override + Coin get coin => _coin; + late Coin _coin; + + Future<bool> _accountExists(String accountId) async { + bool exists = false; + + try { + AccountResponse receiverAccount = + await stellarSdk.accounts.account(accountId); + if (receiverAccount.accountId != "") { + exists = true; + } + } catch (e, s) { + Logging.instance.log( + "Error getting account ${e.toString()} - ${s.toString()}", + level: LogLevel.Error); + } + return exists; + } + + @override + Future<String> confirmSend({required Map<String, dynamic> txData}) async { + final secretSeed = await _secureStore.read(key: '${_walletId}_secretSeed'); + KeyPair senderKeyPair = KeyPair.fromSecretSeed(secretSeed!); + AccountResponse sender = + await stellarSdk.accounts.account(senderKeyPair.accountId); + final amountToSend = txData['recipientAmt'] as Amount; + + //First check if account exists, can be skipped, but if the account does not exist, + // the transaction fee will be charged when the transaction fails. + bool validAccount = await _accountExists(txData['address'] as String); + Transaction transaction; + + if (!validAccount) { + //Fund the account, user must ensure account is correct + CreateAccountOperationBuilder createAccBuilder = + CreateAccountOperationBuilder( + txData['address'] as String, amountToSend.decimal.toString()); + transaction = TransactionBuilder(sender) + .addOperation(createAccBuilder.build()) + .build(); + } else { + transaction = TransactionBuilder(sender) + .addOperation(PaymentOperationBuilder(txData['address'] as String, + Asset.NATIVE, amountToSend.decimal.toString()) + .build()) + .build(); + } + transaction.sign(senderKeyPair, stellarNetwork); + try { + SubmitTransactionResponse response = + await stellarSdk.submitTransaction(transaction); + + if (!response.success) { + throw ("Unable to send transaction"); + } + return response.hash!; + } catch (e, s) { + Logging.instance.log("Error sending TX $e - $s", level: LogLevel.Error); + rethrow; + } + } + + Future<SWAddress.Address?> get _currentReceivingAddress => db + .getAddresses(walletId) + .filter() + .typeEqualTo(SWAddress.AddressType.unknown) + .and() + .subTypeEqualTo(SWAddress.AddressSubType.unknown) + .sortByDerivationIndexDesc() + .findFirst(); + + @override + Future<String> get currentReceivingAddress async => + (await _currentReceivingAddress)?.value ?? await getAddressSW(); + + Future<int> getBaseFee() async { + // final nodeURI = Uri.parse("${getCurrentNode().host}:${getCurrentNode().port}"); + final nodeURI = Uri.parse(getCurrentNode().host); + final httpClient = http.Client(); + FeeStatsResponse fsp = + await FeeStatsRequestBuilder(httpClient, nodeURI).execute(); + return int.parse(fsp.lastLedgerBaseFee); + } + + @override + Future<Amount> estimateFeeFor(Amount amount, int feeRate) async { + var baseFee = await getBaseFee(); + int fee = 100; + switch (feeRate) { + case 0: + fee = baseFee * 10; + case 1: + case 2: + fee = baseFee * 50; + case 3: + fee = baseFee * 100; + case 4: + fee = baseFee * 200; + default: + fee = baseFee * 50; + } + return Amount(rawValue: BigInt.from(fee), fractionDigits: coin.decimals); + } + + @override + Future<void> exit() async { + _hasCalledExit = true; + timer?.cancel(); + timer = null; + stopNetworkAlivePinging(); + } + + NodeModel? _xlmNode; + + NodeModel getCurrentNode() { + if (_xlmNode != null) { + return _xlmNode!; + } else if (NodeService(secureStorageInterface: _secureStore) + .getPrimaryNodeFor(coin: coin) != + null) { + return NodeService(secureStorageInterface: _secureStore) + .getPrimaryNodeFor(coin: coin)!; + } else { + return DefaultNodes.getNodeFor(coin); + } + } + + @override + Future<FeeObject> get fees async { + // final nodeURI = Uri.parse("${getCurrentNode().host}:${getCurrentNode().port}"); + final nodeURI = Uri.parse(getCurrentNode().host); + + final httpClient = http.Client(); + FeeStatsResponse fsp = + await FeeStatsRequestBuilder(httpClient, nodeURI).execute(); + + return FeeObject( + numberOfBlocksFast: 0, + numberOfBlocksAverage: 0, + numberOfBlocksSlow: 0, + fast: int.parse(fsp.lastLedgerBaseFee) * 100, + medium: int.parse(fsp.lastLedgerBaseFee) * 50, + slow: int.parse(fsp.lastLedgerBaseFee) * 10); + } + + @override + Future<void> fullRescan( + int maxUnusedAddressGap, int maxNumberOfIndexesToCheck) async { + await _prefs.init(); + await updateTransactions(); + await updateChainHeight(); + await updateBalance(); + } + + @override + Future<bool> generateNewAddress() { + // TODO: implement generateNewAddress + throw UnimplementedError(); + } + + @override + bool get hasCalledExit => _hasCalledExit; + bool _hasCalledExit = false; + + @override + Future<void> initializeExisting() async { + await _prefs.init(); + } + + @override + Future<void> initializeNew() async { + if ((await mnemonicString) != null || (await mnemonicPassphrase) != null) { + throw Exception( + "Attempted to overwrite mnemonic on generate new wallet!"); + } + + await _prefs.init(); + + String mnemonic = await Wallet.generate24WordsMnemonic(); + await _secureStore.write(key: '${_walletId}_mnemonic', value: mnemonic); + await _secureStore.write(key: '${_walletId}_mnemonicPassphrase', value: ""); + + Wallet wallet = await Wallet.from(mnemonic); + KeyPair keyPair = await wallet.getKeyPair(index: 0); + String address = keyPair.accountId; + String secretSeed = + keyPair.secretSeed; //This will be required for sending a tx + + await _secureStore.write(key: '${_walletId}_secretSeed', value: secretSeed); + + final swAddress = SWAddress.Address( + walletId: walletId, + value: address, + publicKey: keyPair.publicKey, + derivationIndex: 0, + derivationPath: null, + type: SWAddress.AddressType.unknown, // TODO: set type + subType: SWAddress.AddressSubType.unknown); + + await db.putAddress(swAddress); + + await Future.wait( + [updateCachedId(walletId), updateCachedIsFavorite(false)]); + } + + Future<String> getAddressSW() async { + var mnemonic = await _secureStore.read(key: '${_walletId}_mnemonic'); + + Wallet wallet = await Wallet.from(mnemonic!); + KeyPair keyPair = await wallet.getKeyPair(index: 0); + + return Future.value(keyPair.accountId); + } + + @override + bool get isConnected => _isConnected; + bool _isConnected = false; + + @override + bool get isRefreshing => refreshMutex; + bool refreshMutex = false; + + @override + // TODO: implement maxFee + Future<int> get maxFee => throw UnimplementedError(); + + @override + Future<List<String>> get mnemonic => + mnemonicString.then((value) => value!.split(" ")); + + @override + Future<String?> get mnemonicPassphrase => + _secureStore.read(key: '${_walletId}_mnemonicPassphrase'); + + @override + Future<String?> get mnemonicString => + _secureStore.read(key: '${_walletId}_mnemonic'); + + @override + Future<Map<String, dynamic>> prepareSend( + {required String address, + required Amount amount, + Map<String, dynamic>? args}) async { + try { + final feeRate = args?["feeRate"]; + var fee = 1000; + if (feeRate is FeeRateType) { + final theFees = await fees; + switch (feeRate) { + case FeeRateType.fast: + fee = theFees.fast; + case FeeRateType.slow: + fee = theFees.slow; + case FeeRateType.average: + default: + fee = theFees.medium; + } + } + Map<String, dynamic> txData = { + "fee": fee, + "address": address, + "recipientAmt": amount, + }; + + Logging.instance.log("prepare send: $txData", level: LogLevel.Info); + return txData; + } catch (e, s) { + Logging.instance.log("Error getting fees $e - $s", level: LogLevel.Error); + rethrow; + } + } + + @override + Future<void> recoverFromMnemonic( + {required String mnemonic, + String? mnemonicPassphrase, + required int maxUnusedAddressGap, + required int maxNumberOfIndexesToCheck, + required int height}) async { + if ((await mnemonicString) != null || + (await this.mnemonicPassphrase) != null) { + throw Exception("Attempted to overwrite mnemonic on restore!"); + } + + var wallet = await Wallet.from(mnemonic); + var keyPair = await wallet.getKeyPair(index: 0); + var address = keyPair.accountId; + var secretSeed = keyPair.secretSeed; + + await _secureStore.write( + key: '${_walletId}_mnemonic', value: mnemonic.trim()); + await _secureStore.write( + key: '${_walletId}_mnemonicPassphrase', + value: mnemonicPassphrase ?? "", + ); + await _secureStore.write(key: '${_walletId}_secretSeed', value: secretSeed); + + final swAddress = SWAddress.Address( + walletId: walletId, + value: address, + publicKey: keyPair.publicKey, + derivationIndex: 0, + derivationPath: null, + type: SWAddress.AddressType.unknown, // TODO: set type + subType: SWAddress.AddressSubType.unknown); + + await db.putAddress(swAddress); + + await Future.wait( + [updateCachedId(walletId), updateCachedIsFavorite(false)]); + } + + Future<void> updateChainHeight() async { + final height = await stellarSdk.ledgers + .order(RequestBuilderOrder.DESC) + .limit(1) + .execute() + .then((value) => value.records!.first.sequence) + .onError((error, stackTrace) => throw ("Error getting chain height")); + await updateCachedChainHeight(height); + } + + Future<void> updateTransactions() async { + try { + List<Tuple2<SWTransaction.Transaction, SWAddress.Address?>> + transactionList = []; + + Page<OperationResponse> payments = await stellarSdk.payments + .forAccount(await getAddressSW()) + .order(RequestBuilderOrder.DESC) + .execute() + .onError( + (error, stackTrace) => throw ("Could not fetch transactions")); + + for (OperationResponse response in payments.records!) { + // PaymentOperationResponse por; + if (response is PaymentOperationResponse) { + PaymentOperationResponse por = response; + + Logging.instance.log( + "ALL TRANSACTIONS IS ${por.transactionSuccessful}", + level: LogLevel.Info); + + Logging.instance.log("THIS TX HASH IS ${por.transactionHash}", + level: LogLevel.Info); + + SWTransaction.TransactionType type; + if (por.sourceAccount == await getAddressSW()) { + type = SWTransaction.TransactionType.outgoing; + } else { + type = SWTransaction.TransactionType.incoming; + } + final amount = Amount( + rawValue: BigInt.parse(float + .parse(por.amount!) + .toStringAsFixed(coin.decimals) + .replaceAll(".", "")), + fractionDigits: coin.decimals, + ); + int fee = 0; + int height = 0; + //Query the transaction linked to the payment, + // por.transaction returns a null sometimes + TransactionResponse tx = + await stellarSdk.transactions.transaction(por.transactionHash!); + + if (tx.hash.isNotEmpty) { + fee = tx.feeCharged!; + height = tx.ledger; + } + var theTransaction = SWTransaction.Transaction( + walletId: walletId, + txid: por.transactionHash!, + timestamp: + DateTime.parse(por.createdAt!).millisecondsSinceEpoch ~/ 1000, + type: type, + subType: SWTransaction.TransactionSubType.none, + amount: 0, + amountString: amount.toJsonString(), + fee: fee, + height: height, + isCancelled: false, + isLelantus: false, + slateId: "", + otherData: "", + inputs: [], + outputs: [], + nonce: 0, + numberOfMessages: null, + ); + SWAddress.Address? receivingAddress = await _currentReceivingAddress; + SWAddress.Address address = + type == SWTransaction.TransactionType.incoming + ? receivingAddress! + : SWAddress.Address( + walletId: walletId, + value: por.sourceAccount!, + publicKey: + KeyPair.fromAccountId(por.sourceAccount!).publicKey, + derivationIndex: 0, + derivationPath: null, + type: SWAddress.AddressType.unknown, // TODO: set type + subType: SWAddress.AddressSubType.unknown); + Tuple2<SWTransaction.Transaction, SWAddress.Address> tuple = + Tuple2(theTransaction, address); + transactionList.add(tuple); + } else if (response is CreateAccountOperationResponse) { + CreateAccountOperationResponse caor = response; + SWTransaction.TransactionType type; + if (caor.sourceAccount == await getAddressSW()) { + type = SWTransaction.TransactionType.outgoing; + } else { + type = SWTransaction.TransactionType.incoming; + } + final amount = Amount( + rawValue: BigInt.parse(float + .parse(caor.startingBalance!) + .toStringAsFixed(coin.decimals) + .replaceAll(".", "")), + fractionDigits: coin.decimals, + ); + int fee = 0; + int height = 0; + TransactionResponse tx = + await stellarSdk.transactions.transaction(caor.transactionHash!); + if (tx.hash.isNotEmpty) { + fee = tx.feeCharged!; + height = tx.ledger; + } + var theTransaction = SWTransaction.Transaction( + walletId: walletId, + txid: caor.transactionHash!, + timestamp: + DateTime.parse(caor.createdAt!).millisecondsSinceEpoch ~/ 1000, + type: type, + subType: SWTransaction.TransactionSubType.none, + amount: 0, + amountString: amount.toJsonString(), + fee: fee, + height: height, + isCancelled: false, + isLelantus: false, + slateId: "", + otherData: "", + inputs: [], + outputs: [], + nonce: 0, + numberOfMessages: null, + ); + SWAddress.Address? receivingAddress = await _currentReceivingAddress; + SWAddress.Address address = + type == SWTransaction.TransactionType.incoming + ? receivingAddress! + : SWAddress.Address( + walletId: walletId, + value: caor.sourceAccount!, + publicKey: + KeyPair.fromAccountId(caor.sourceAccount!).publicKey, + derivationIndex: 0, + derivationPath: null, + type: SWAddress.AddressType.unknown, // TODO: set type + subType: SWAddress.AddressSubType.unknown); + Tuple2<SWTransaction.Transaction, SWAddress.Address> tuple = + Tuple2(theTransaction, address); + transactionList.add(tuple); + } + } + await db.addNewTransactionData(transactionList, walletId); + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from updateTransactions(): $e\n$s", + level: LogLevel.Error); + } + } + + Future<void> updateBalance() async { + try { + AccountResponse accountResponse = + await stellarSdk.accounts.account(await getAddressSW()); + for (Balance balance in accountResponse.balances) { + switch (balance.assetType) { + case Asset.TYPE_NATIVE: + _balance = SWBalance.Balance( + total: Amount( + rawValue: BigInt.from(float.parse(balance.balance) * 10000000), + fractionDigits: coin.decimals, + ), + spendable: Amount( + rawValue: BigInt.from(float.parse(balance.balance) * 10000000), + fractionDigits: coin.decimals, + ), + blockedTotal: Amount( + rawValue: BigInt.from(0), + fractionDigits: coin.decimals, + ), + pendingSpendable: Amount( + rawValue: BigInt.from(0), + fractionDigits: coin.decimals, + ), + ); + Logging.instance.log(_balance, level: LogLevel.Info); + await updateCachedBalance(_balance!); + } + } + } catch (e, s) { + Logging.instance.log( + "ERROR GETTING BALANCE $e\n$s", + level: LogLevel.Info, + ); + } + } + + @override + Future<void> refresh() async { + if (refreshMutex) { + Logging.instance.log( + "$walletId $walletName refreshMutex denied", + level: LogLevel.Info, + ); + return; + } else { + refreshMutex = true; + } + + try { + await _prefs.init(); + + GlobalEventBus.instance.fire( + WalletSyncStatusChangedEvent( + WalletSyncStatus.syncing, + walletId, + coin, + ), + ); + + await updateChainHeight(); + await updateTransactions(); + await updateBalance(); + + GlobalEventBus.instance.fire( + WalletSyncStatusChangedEvent( + WalletSyncStatus.synced, + walletId, + coin, + ), + ); + + if (shouldAutoSync) { + timer ??= Timer.periodic(const Duration(seconds: 30), (timer) async { + Logging.instance.log( + "Periodic refresh check for $walletId $walletName in object instance: $hashCode", + level: LogLevel.Info); + + await refresh(); + GlobalEventBus.instance.fire( + UpdatedInBackgroundEvent( + "New data found in $walletId $walletName in background!", + walletId, + ), + ); + }); + } + } catch (e, s) { + Logging.instance.log( + "Failed to refresh stellar wallet $walletId: '$walletName': $e\n$s", + level: LogLevel.Warning, + ); + GlobalEventBus.instance.fire( + WalletSyncStatusChangedEvent( + WalletSyncStatus.unableToSync, + walletId, + coin, + ), + ); + } + + refreshMutex = false; + } + + @override + int get storedChainHeight => getCachedChainHeight(); + + @override + Future<bool> testNetworkConnection() { + // TODO: implement testNetworkConnection + throw UnimplementedError(); + } + + @override + Future<List<SWTransaction.Transaction>> get transactions => + db.getTransactions(walletId).findAll(); + + @override + Future<void> updateNode(bool shouldRefresh) async { + _xlmNode = NodeService(secureStorageInterface: _secureStore) + .getPrimaryNodeFor(coin: coin) ?? + DefaultNodes.getNodeFor(coin); + + if (shouldRefresh) { + unawaited(refresh()); + } + } + + @override + Future<void> updateSentCachedTxData(Map<String, dynamic> txData) async { + final transaction = SWTransaction.Transaction( + walletId: walletId, + txid: txData["txid"] as String, + timestamp: DateTime.now().millisecondsSinceEpoch ~/ 1000, + type: SWTransaction.TransactionType.outgoing, + subType: SWTransaction.TransactionSubType.none, + // precision may be lost here hence the following amountString + amount: (txData["recipientAmt"] as Amount).raw.toInt(), + amountString: (txData["recipientAmt"] as Amount).toJsonString(), + fee: txData["fee"] as int, + height: null, + isCancelled: false, + isLelantus: false, + otherData: null, + slateId: null, + nonce: null, + inputs: [], + outputs: [], + numberOfMessages: null, + ); + + final address = txData["address"] is String + ? await db.getAddress(walletId, txData["address"] as String) + : null; + + await db.addNewTransactionData( + [ + Tuple2(transaction, address), + ], + walletId, + ); + } + + @override + // TODO: implement utxos + Future<List<UTXO>> get utxos => throw UnimplementedError(); + + @override + bool validateAddress(String address) { + return RegExp(r"^[G][A-Z0-9]{55}$").hasMatch(address); + } + + @override + String get walletId => _walletId; + late String _walletId; +} diff --git a/lib/services/ethereum/ethereum_api.dart b/lib/services/ethereum/ethereum_api.dart index d30b9ea27..5e95e3aa1 100644 --- a/lib/services/ethereum/ethereum_api.dart +++ b/lib/services/ethereum/ethereum_api.dart @@ -10,7 +10,6 @@ import 'dart:convert'; -import 'package:decimal/decimal.dart'; import 'package:http/http.dart'; import 'package:stackwallet/dto/ethereum/eth_token_tx_dto.dart'; import 'package:stackwallet/dto/ethereum/eth_token_tx_extra_dto.dart'; @@ -50,6 +49,7 @@ abstract class EthereumAPI { static Future<EthereumResponse<List<EthTxDTO>>> getEthTransactions({ required String address, int firstBlock = 0, + bool includeTokens = false, }) async { try { final response = await get( @@ -67,7 +67,7 @@ abstract class EthereumAPI { for (final map in list!) { final txn = EthTxDTO.fromMap(Map<String, dynamic>.from(map as Map)); - if (txn.hasToken == 0) { + if (txn.hasToken == 0 || includeTokens) { txns.add(txn); } } @@ -76,9 +76,11 @@ abstract class EthereumAPI { null, ); } else { - throw EthApiException( - "getEthTransactions($address) response is empty but status code is " - "${response.statusCode}", + // nice that the api returns an empty body instead of being + // consistent and returning a json object with no transactions + return EthereumResponse( + [], + null, ); } } else { @@ -196,9 +198,11 @@ abstract class EthereumAPI { null, ); } else { - throw EthApiException( - "getEthTransactionNonces($txns) response is empty but status code is " - "${response.statusCode}", + // nice that the api returns an empty body instead of being + // consistent and returning a json object with no transactions + return EthereumResponse( + [], + null, ); } } else { @@ -252,13 +256,13 @@ abstract class EthereumAPI { ); } else { throw EthApiException( - "getEthTransaction($txids) response is empty but status code is " + "getEthTokenTransactionsByTxids($txids) response is empty but status code is " "${response.statusCode}", ); } } else { throw EthApiException( - "getEthTransaction($txids) failed with status code: " + "getEthTokenTransactionsByTxids($txids) failed with status code: " "${response.statusCode}", ); } @@ -269,7 +273,7 @@ abstract class EthereumAPI { ); } catch (e, s) { Logging.instance.log( - "getEthTransaction($txids): $e\n$s", + "getEthTokenTransactionsByTxids($txids): $e\n$s", level: LogLevel.Error, ); return EthereumResponse( @@ -307,9 +311,11 @@ abstract class EthereumAPI { null, ); } else { - throw EthApiException( - "getTokenTransactions($address, $tokenContractAddress) response is empty but status code is " - "${response.statusCode}", + // nice that the api returns an empty body instead of being + // consistent and returning a json object with no transactions + return EthereumResponse( + [], + null, ); } } else { @@ -424,10 +430,10 @@ abstract class EthereumAPI { final map = json["data"].first as Map; final balance = - Decimal.tryParse(map["balance"].toString()) ?? Decimal.zero; + BigInt.tryParse(map["units"].toString()) ?? BigInt.zero; return EthereumResponse( - Amount.fromDecimal(balance, fractionDigits: map["decimals"] as int), + Amount(rawValue: balance, fractionDigits: map["decimals"] as int), null, ); } else { diff --git a/lib/services/exchange/exchange_data_loading_service.dart b/lib/services/exchange/exchange_data_loading_service.dart index 6fefba289..ee0ed50a0 100644 --- a/lib/services/exchange/exchange_data_loading_service.dart +++ b/lib/services/exchange/exchange_data_loading_service.dart @@ -135,6 +135,9 @@ class ExchangeDataLoadingService { Future<void> loadAll() async { if (!_locked) { _locked = true; + if (_isar == null) { + await initDB(); + } Logging.instance.log( "ExchangeDataLoadingService.loadAll starting...", level: LogLevel.Info, diff --git a/lib/services/litescribe_api.dart b/lib/services/litescribe_api.dart new file mode 100644 index 000000000..1acc047b8 --- /dev/null +++ b/lib/services/litescribe_api.dart @@ -0,0 +1,79 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; +import 'package:stackwallet/dto/ordinals/inscription_data.dart'; +import 'package:stackwallet/dto/ordinals/litescribe_response.dart'; + +class LitescribeAPI { + static final LitescribeAPI _instance = LitescribeAPI._internal(); + + factory LitescribeAPI({required String baseUrl}) { + _instance.baseUrl = baseUrl; + return _instance; + } + + LitescribeAPI._internal(); + + late String baseUrl; + + Future<LitescribeResponse> _getResponse(String endpoint) async { + final response = await http.get(Uri.parse('$baseUrl$endpoint')); + if (response.statusCode == 200) { + return LitescribeResponse(data: _validateJson(response.body)); + } else { + throw Exception( + 'LitescribeAPI _getResponse exception: Failed to load data'); + } + } + + Map<String, dynamic> _validateJson(String responseBody) { + final parsed = jsonDecode(responseBody); + if (parsed is Map<String, dynamic>) { + return parsed; + } else { + throw const FormatException( + 'LitescribeAPI _validateJson exception: Invalid JSON format'); + } + } + + Future<List<InscriptionData>> getInscriptionsByAddress(String address, + {int cursor = 0, int size = 1000}) async { + // size param determines how many inscriptions are returned per response + // default of 1000 is used to cover most addresses (I assume) + // if the total number of inscriptions at the address exceeds the length of the list of inscriptions returned, another call with a higher size is made + final int defaultLimit = 1000; + final response = await _getResponse( + '/address/inscriptions?address=$address&cursor=$cursor&size=$size'); + + // Check if the number of returned inscriptions equals the limit + final int total = response.data['result']['total'] as int; + + int currentSize = 0; + if (total == 0) { + return <InscriptionData>[]; + } + + final list = response.data['result']!['list']; + currentSize = list.length as int; + + if (currentSize == size && currentSize < total) { + // If the number of returned inscriptions equals the limit and there are more inscriptions available, + // increment the cursor and make the next API call to fetch the remaining inscriptions. + final int newCursor = cursor + size; + return getInscriptionsByAddress(address, cursor: newCursor, size: size); + } else { + try { + // Iterate through the list and create InscriptionData objects from each element + final List<InscriptionData> inscriptions = (list as List<dynamic>) + .map((json) => + InscriptionData.fromJson(json as Map<String, dynamic>)) + .toList(); + + return inscriptions; + } catch (e) { + throw const FormatException( + 'LitescribeAPI getInscriptionsByAddress exception: AddressInscriptionResponse.fromJson failure'); + } + } + } +} diff --git a/lib/services/mixins/firo_hive.dart b/lib/services/mixins/firo_hive.dart deleted file mode 100644 index be9845453..000000000 --- a/lib/services/mixins/firo_hive.dart +++ /dev/null @@ -1,61 +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'; - -mixin FiroHive { - late final String _walletId; - - void initFiroHive(String walletId) { - _walletId = walletId; - } - - // jindex - List? firoGetJIndex() { - return DB.instance.get<dynamic>(boxName: _walletId, key: "jindex") as List?; - } - - Future<void> firoUpdateJIndex(List jIndex) async { - await DB.instance.put<dynamic>( - boxName: _walletId, - key: "jindex", - value: jIndex, - ); - } - - // _lelantus_coins - List? firoGetLelantusCoins() { - return DB.instance.get<dynamic>(boxName: _walletId, key: "_lelantus_coins") - as List?; - } - - Future<void> firoUpdateLelantusCoins(List lelantusCoins) async { - await DB.instance.put<dynamic>( - boxName: _walletId, - key: "_lelantus_coins", - value: lelantusCoins, - ); - } - - // mintIndex - int firoGetMintIndex() { - return DB.instance.get<dynamic>(boxName: _walletId, key: "mintIndex") - as int? ?? - 0; - } - - Future<void> firoUpdateMintIndex(int mintIndex) async { - await DB.instance.put<dynamic>( - boxName: _walletId, - key: "mintIndex", - value: mintIndex, - ); - } -} diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart new file mode 100644 index 000000000..5b17f3b48 --- /dev/null +++ b/lib/services/mixins/ordinals_interface.dart @@ -0,0 +1,94 @@ +import 'dart:async'; + +import 'package:isar/isar.dart'; +import 'package:stackwallet/db/isar/main_db.dart'; +import 'package:stackwallet/dto/ordinals/inscription_data.dart'; +import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; +import 'package:stackwallet/models/isar/ordinal.dart'; +import 'package:stackwallet/services/litescribe_api.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; + +mixin OrdinalsInterface { + late final String _walletId; + late final Coin _coin; + late final MainDB _db; + + void initOrdinalsInterface({ + required String walletId, + required Coin coin, + required MainDB db, + }) { + _walletId = walletId; + _coin = coin; + _db = db; + } + + final LitescribeAPI litescribeAPI = + LitescribeAPI(baseUrl: 'https://litescribe.io/api'); + + Future<void> refreshInscriptions() async { + final uniqueAddresses = await _db + .getUTXOs(_walletId) + .filter() + .addressIsNotNull() + .distinctByAddress() + .addressProperty() + .findAll(); + final inscriptions = + await _getInscriptionDataFromAddresses(uniqueAddresses.cast<String>()); + + final ords = inscriptions + .map((e) => Ordinal.fromInscriptionData(e, _walletId)) + .toList(); + + await _db.isar.writeTxn(() async { + await _db.isar.ordinals + .where() + .filter() + .walletIdEqualTo(_walletId) + .deleteAll(); + await _db.isar.ordinals.putAll(ords); + }); + } + + Future<List<InscriptionData>> _getInscriptionDataFromAddresses( + List<String> addresses) async { + List<InscriptionData> allInscriptions = []; + for (String address in addresses) { + try { + var inscriptions = + await litescribeAPI.getInscriptionsByAddress(address); + allInscriptions.addAll(inscriptions); + } catch (e) { + throw Exception("Error fetching inscriptions for address $address: $e"); + } + } + return allInscriptions; + } + + // check if an inscription is in a given <UTXO> output + Future<bool> inscriptionInOutput(UTXO output) async { + if (output.address != null) { + var inscriptions = + await litescribeAPI.getInscriptionsByAddress("${output.address}"); + if (inscriptions.isNotEmpty) { + return true; + } else { + return false; + } + } else { + throw UnimplementedError( + 'TODO look up utxo without address. utxo->txid:output->address'); + } + } + + // check if an inscription is in a given <UTXO> output + Future<bool> inscriptionInAddress(String address) async { + var inscriptions = await litescribeAPI.getInscriptionsByAddress(address); + if (inscriptions.isNotEmpty) { + return true; + } else { + return false; + } + } +} diff --git a/lib/services/monkey_service.dart b/lib/services/monkey_service.dart new file mode 100644 index 000000000..46dfbb0ef --- /dev/null +++ b/lib/services/monkey_service.dart @@ -0,0 +1,40 @@ +import 'dart:typed_data'; + +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:http/http.dart' as http; +import 'package:stackwallet/utilities/logger.dart'; + +final pMonKeyService = Provider((ref) => MonKeyService()); + +class MonKeyService { + static const baseURL = "https://monkey.banano.cc/api/v1/monkey/"; + + Future<Uint8List> fetchMonKey({ + required String address, + bool png = false, + }) async { + try { + String url = "https://monkey.banano.cc/api/v1/monkey/$address"; + + if (png) { + url += '?format=png&size=512&background=false'; + } + + final response = await http.get(Uri.parse(url)); + + if (response.statusCode == 200) { + return response.bodyBytes; + } else { + throw Exception( + "statusCode=${response.statusCode} body=${response.body}", + ); + } + } catch (e, s) { + Logging.instance.log( + "Failed fetchMonKey($address): $e\n$s", + level: LogLevel.Error, + ); + rethrow; + } + } +} diff --git a/lib/services/nano_api.dart b/lib/services/nano_api.dart new file mode 100644 index 000000000..3173bfbe6 --- /dev/null +++ b/lib/services/nano_api.dart @@ -0,0 +1,130 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; +import 'package:nanodart/nanodart.dart'; + +class NanoAPI { + static Future< + ({ + NAccountInfo? accountInfo, + Exception? exception, + })> getAccountInfo({ + required Uri server, + required bool representative, + required String account, + }) async { + NAccountInfo? accountInfo; + Exception? exception; + + try { + final response = await http.post( + server, + headers: { + "Content-Type": "application/json", + }, + body: jsonEncode({ + "action": "account_info", + "representative": "true", + "account": account, + }), + ); + + final map = jsonDecode(response.body); + + if (map is Map && map["error"] != null) { + throw Exception(map["error"].toString()); + } + + accountInfo = NAccountInfo( + frontier: map["frontier"] as String, + representative: map["representative"] as String, + ); + } on Exception catch (e) { + exception = e; + } catch (e) { + exception = Exception(e.toString()); + } + + return (accountInfo: accountInfo, exception: exception); + } + + static Future<bool> changeRepresentative({ + required Uri server, + required int accountType, + required String account, + required String newRepresentative, + required String previousBlock, + required String balance, + required String privateKey, + required String work, + }) async { + Map<String, String> block = { + "type": "state", + "account": account, + "previous": previousBlock, + "representative": newRepresentative, + "balance": balance, + "link": + "0000000000000000000000000000000000000000000000000000000000000000", + "work": work, + }; + + final String hash; + + try { + hash = NanoBlocks.computeStateHash( + accountType, + account, + previousBlock, + newRepresentative, + BigInt.parse(balance), + block["link"] as String, + ); + } catch (e) { + if (e is RangeError) { + throw Exception("Invalid representative format"); + } + rethrow; + } + + final signature = NanoSignatures.signBlock(hash, privateKey); + + block["signature"] = signature; + + final map = await postBlock(server: server, block: block); + + if (map is Map && map["error"] != null) { + throw Exception(map["error"].toString()); + } + + return map["error"] == null; + } + + // TODO: GET RID OF DYNAMIC AND USED TYPED DATA + static Future<dynamic> postBlock({ + required Uri server, + required Map<String, dynamic> block, + }) async { + final response = await http.post( + server, + headers: { + "Content-Type": "application/json", + }, + body: jsonEncode({ + "action": "process", + "json_block": "true", + "subtype": "change", + "block": block, + }), + ); + + return jsonDecode(response.body); + } +} + +class NAccountInfo { + final String frontier; + final String representative; + + NAccountInfo({required this.frontier, required this.representative}); +} diff --git a/lib/services/notifications_api.dart b/lib/services/notifications_api.dart index e1c42fabe..3ef97462a 100644 --- a/lib/services/notifications_api.dart +++ b/lib/services/notifications_api.dart @@ -26,6 +26,7 @@ class NotificationApi { priority: Priority.high, ticker: 'ticker'), iOS: IOSNotificationDetails(), + macOS: MacOSNotificationDetails(), ); } @@ -34,8 +35,13 @@ class NotificationApi { const iOS = IOSInitializationSettings(); const linux = LinuxInitializationSettings( defaultActionName: "temporary_stack_wallet"); - const settings = - InitializationSettings(android: android, iOS: iOS, linux: linux); + const macOS = MacOSInitializationSettings(); + const settings = InitializationSettings( + android: android, + iOS: iOS, + linux: linux, + macOS: macOS, + ); await _notifications.initialize( settings, onSelectNotification: (payload) async { @@ -71,8 +77,10 @@ class NotificationApi { final id = prefs.currentNotificationId; String confirms = ""; - if (txid != null) { - confirms = " (${confirmations!}/${requiredConfirmations!})"; + if (txid != null && + confirmations != null && + requiredConfirmations != null) { + confirms = " ($confirmations/$requiredConfirmations)"; } final NotificationModel model = NotificationModel( diff --git a/lib/services/price.dart b/lib/services/price.dart index 24d929062..66842c9d8 100644 --- a/lib/services/price.dart +++ b/lib/services/price.dart @@ -100,7 +100,7 @@ class PriceAPI { Uri.parse("https://api.coingecko.com/api/v3/coins/markets?vs_currency" "=${baseCurrency.toLowerCase()}" "&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin," - "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano" + "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano,stellar" "&order=market_cap_desc&per_page=50&page=1&sparkline=false"); final coinGeckoResponse = await client.get( diff --git a/lib/themes/coin_card_provider.dart b/lib/themes/coin_card_provider.dart index fc84faebd..b34e9e6f1 100644 --- a/lib/themes/coin_card_provider.dart +++ b/lib/themes/coin_card_provider.dart @@ -22,3 +22,14 @@ final coinCardProvider = Provider.family<String?, Coin>((ref, coin) { 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; + } +}); diff --git a/lib/themes/color_theme.dart b/lib/themes/color_theme.dart index bcfa45ac9..c78d35c11 100644 --- a/lib/themes/color_theme.dart +++ b/lib/themes/color_theme.dart @@ -28,6 +28,7 @@ class CoinThemeColorDefault { Color get namecoin => const Color(0xFF91B1E1); Color get wownero => const Color(0xFFED80C1); Color get particl => const Color(0xFF8175BD); + Color get stellar => const Color(0xFF6600FF); Color get nano => const Color(0xFF209CE9); Color get banano => const Color(0xFFFBDD11); @@ -62,6 +63,9 @@ class CoinThemeColorDefault { return wownero; case Coin.particl: return particl; + case Coin.stellar: + case Coin.stellarTestnet: + return stellar; case Coin.nano: return nano; case Coin.banano: diff --git a/lib/themes/stack_colors.dart b/lib/themes/stack_colors.dart index b9e58a5ca..5dbd55fe1 100644 --- a/lib/themes/stack_colors.dart +++ b/lib/themes/stack_colors.dart @@ -1707,6 +1707,9 @@ class StackColors extends ThemeExtension<StackColors> { return _coin.wownero; case Coin.particl: return _coin.particl; + case Coin.stellar: + case Coin.stellarTestnet: + return _coin.stellar; case Coin.nano: return _coin.nano; case Coin.banano: diff --git a/lib/themes/theme_service.dart b/lib/themes/theme_service.dart index c9a8bd978..baeac44f2 100644 --- a/lib/themes/theme_service.dart +++ b/lib/themes/theme_service.dart @@ -27,7 +27,7 @@ final pThemeService = Provider<ThemeService>((ref) { }); class ThemeService { - static const _currentDefaultThemeVersion = 3; + static const _currentDefaultThemeVersion = 4; ThemeService._(); static ThemeService? _instance; static ThemeService get instance => _instance ??= ThemeService._(); diff --git a/lib/utilities/address_utils.dart b/lib/utilities/address_utils.dart index 0093e1d00..8766d706a 100644 --- a/lib/utilities/address_utils.dart +++ b/lib/utilities/address_utils.dart @@ -105,6 +105,8 @@ class AddressUtils { return Address.validateAddress(address, namecoin, namecoin.bech32!); case Coin.particl: return Address.validateAddress(address, particl); + case Coin.stellar: + return RegExp(r"^[G][A-Z0-9]{55}$").hasMatch(address); case Coin.nano: return NanoAccounts.isValid(NanoAccountType.NANO, address); case Coin.banano: @@ -139,6 +141,8 @@ class AddressUtils { return Address.validateAddress(address, firoTestNetwork); case Coin.dogecoinTestNet: return Address.validateAddress(address, dogecointestnet); + case Coin.stellarTestnet: + return RegExp(r"^[G][A-Z0-9]{55}$").hasMatch(address); } } diff --git a/lib/utilities/amount/amount_formatter.dart b/lib/utilities/amount/amount_formatter.dart index 6743bcd53..1ae577507 100644 --- a/lib/utilities/amount/amount_formatter.dart +++ b/lib/utilities/amount/amount_formatter.dart @@ -70,6 +70,11 @@ class AmountFormatter { String string, { EthContract? ethContract, }) { - return unit.tryParse(string, locale: locale, coin: coin); + return unit.tryParse( + string, + locale: locale, + coin: coin, + tokenContract: ethContract, + ); } } diff --git a/lib/utilities/amount/amount_unit.dart b/lib/utilities/amount/amount_unit.dart index 0c0600d7a..ccdd4e095 100644 --- a/lib/utilities/amount/amount_unit.dart +++ b/lib/utilities/amount/amount_unit.dart @@ -50,6 +50,8 @@ enum AmountUnit { case Coin.dogecoin: case Coin.eCash: case Coin.epicCash: + case Coin.stellar: // TODO: check if this is correct + case Coin.stellarTestnet: return AmountUnit.values.sublist(0, 4); case Coin.monero: @@ -168,6 +170,7 @@ extension AmountUnitExt on AmountUnit { required String locale, required Coin coin, EthContract? tokenContract, + bool overrideWithDecimalPlacesFromString = false, }) { final precisionLost = value.startsWith("~"); @@ -201,7 +204,9 @@ extension AmountUnitExt on AmountUnit { return null; } - final decimalPlaces = tokenContract?.decimals ?? coin.decimals; + final decimalPlaces = overrideWithDecimalPlacesFromString + ? decimal.scale + : tokenContract?.decimals ?? coin.decimals; final realShift = math.min(shift, decimalPlaces); return decimal.shift(0 - realShift).toAmount(fractionDigits: decimalPlaces); diff --git a/lib/utilities/assets.dart b/lib/utilities/assets.dart index b8ec501e8..d1d7a55d8 100644 --- a/lib/utilities/assets.dart +++ b/lib/utilities/assets.dart @@ -92,6 +92,7 @@ class _SVG { final coinControl = const _COIN_CONTROL(); + String get monkey => "assets/svg/monkey.svg"; String get circleSliders => "assets/svg/configuration.svg"; String get circlePlus => "assets/svg/plus-circle.svg"; String get circlePlusFilled => "assets/svg/circle-plus-filled.svg"; @@ -205,6 +206,8 @@ class _SVG { String get messageQuestion => "assets/svg/message-question-1.svg"; String get list => "assets/svg/list-ul.svg"; String get unclaimedPaynym => "assets/svg/unclaimed.svg"; + String get send => "assets/svg/send.svg"; + String get ordinal => "assets/svg/ordinal.svg"; String get trocadorRatingA => "assets/svg/trocador_rating_a.svg"; String get trocadorRatingB => "assets/svg/trocador_rating_b.svg"; diff --git a/lib/utilities/block_explorers.dart b/lib/utilities/block_explorers.dart index 7ff0cf349..831332320 100644 --- a/lib/utilities/block_explorers.dart +++ b/lib/utilities/block_explorers.dart @@ -54,10 +54,14 @@ Uri getDefaultBlockExplorerUrlFor({ return Uri.parse("https://chainz.cryptoid.info/nmc/tx.dws?$txid.htm"); case Coin.particl: return Uri.parse("https://chainz.cryptoid.info/part/tx.dws?$txid.htm"); + case Coin.stellar: + return Uri.parse("https://stellarchain.io/tx/$txid"); case Coin.nano: return Uri.parse("https://www.nanolooker.com/block/$txid"); case Coin.banano: return Uri.parse("https://www.bananolooker.com/block/$txid"); + case Coin.stellarTestnet: + return Uri.parse("https://testnet.stellarchain.io/transactions/$txid"); } } diff --git a/lib/utilities/constants.dart b/lib/utilities/constants.dart index 2dd7be287..ae357c5b3 100644 --- a/lib/utilities/constants.dart +++ b/lib/utilities/constants.dart @@ -43,6 +43,7 @@ abstract class Constants { BigInt.parse("1000000000000000000000000000000"); // 1*10^30 static final BigInt _satsPerCoinBanano = BigInt.parse("100000000000000000000000000000"); // 1*10^29 + static final BigInt _satsPerCoinStellar = BigInt.from(10000000); // https://developers.stellar.org/docs/fundamentals-and-concepts/stellar-data-structures/assets#amount-precision static final BigInt _satsPerCoin = BigInt.from(100000000); static const int _decimalPlaces = 8; static const int _decimalPlacesNano = 30; @@ -51,6 +52,7 @@ abstract class Constants { static const int _decimalPlacesMonero = 12; static const int _decimalPlacesEthereum = 18; static const int _decimalPlacesECash = 2; + static const int _decimalPlacesStellar = 7; static const int notificationsMax = 0xFFFFFFFF; static const Duration networkAliveTimerDuration = Duration(seconds: 10); @@ -58,7 +60,7 @@ abstract class Constants { // Enable Logger.print statements static const bool disableLogger = false; - static const int currentDataVersion = 10; + static const int currentDataVersion = 11; static const int rescanV1 = 1; @@ -96,6 +98,10 @@ abstract class Constants { case Coin.eCash: return _satsPerCoinECash; + + case Coin.stellar: + case Coin.stellarTestnet: + return _satsPerCoinStellar; } } @@ -133,6 +139,10 @@ abstract class Constants { case Coin.eCash: return _decimalPlacesECash; + + case Coin.stellar: + case Coin.stellarTestnet: + return _decimalPlacesStellar; } } @@ -155,6 +165,8 @@ abstract class Constants { case Coin.namecoin: case Coin.particl: case Coin.nano: + case Coin.stellar: + case Coin.stellarTestnet: values.addAll([24, 12]); break; case Coin.banano: @@ -214,6 +226,10 @@ abstract class Constants { case Coin.nano: // TODO: Verify this case Coin.banano: // TODO: Verify this return 1; + + case Coin.stellar: + case Coin.stellarTestnet: + return 5; } } @@ -241,6 +257,8 @@ abstract class Constants { case Coin.nano: case Coin.banano: + case Coin.stellar: + case Coin.stellarTestnet: return 24; case Coin.monero: diff --git a/lib/utilities/db_version_migration.dart b/lib/utilities/db_version_migration.dart index 293b5d48c..f54c2b85a 100644 --- a/lib/utilities/db_version_migration.dart +++ b/lib/utilities/db_version_migration.dart @@ -180,7 +180,6 @@ class DbVersionMigrator with WalletDB { // clear possible broken firo cache await DB.instance.clearSharedTransactionCache(coin: Coin.firo); - // update version await DB.instance.put<dynamic>( boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 4); @@ -343,12 +342,85 @@ class DbVersionMigrator with WalletDB { // try to continue migrating return await migrate(10, secureStore: secureStore); + case 10: + // migrate + await _v10(secureStore); + + // update version + await DB.instance.put<dynamic>( + boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 11); + + // try to continue migrating + return await migrate(11, secureStore: secureStore); + default: // finally return return; } } + Future<void> _v10(SecureStorageInterface secureStore) async { + await Hive.openBox<dynamic>(DB.boxNameAllWalletsData); + await Hive.openBox<dynamic>(DB.boxNamePrefs); + final walletsService = WalletsService(secureStorageInterface: secureStore); + final prefs = Prefs.instance; + final walletInfoList = await walletsService.walletNames; + await prefs.init(); + await MainDB.instance.initMainDB(); + + for (final walletId in walletInfoList.keys) { + final info = walletInfoList[walletId]!; + assert(info.walletId == walletId); + + if (info.coin == Coin.firo && + MainDB.instance.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .countSync() == + 0) { + final walletBox = await Hive.openBox<dynamic>(walletId); + + final hiveLCoins = DB.instance.get<dynamic>( + boxName: walletId, + key: "_lelantus_coins", + ) as List? ?? + []; + + final jindexes = (DB.instance + .get<dynamic>(boxName: walletId, key: "jindex") as List? ?? + []) + .cast<int>(); + + final List<isar_models.LelantusCoin> coins = []; + for (final e in hiveLCoins) { + final map = e as Map; + final lcoin = map.values.first as LelantusCoin; + + final isJMint = jindexes.contains(lcoin.index); + + final coin = isar_models.LelantusCoin( + walletId: walletId, + txid: lcoin.txId, + value: lcoin.value.toString(), + mintIndex: lcoin.index, + anonymitySetId: lcoin.anonymitySetId, + isUsed: lcoin.isUsed, + isJMint: isJMint, + otherData: null, + ); + + coins.add(coin); + } + + if (coins.isNotEmpty) { + await MainDB.instance.isar.writeTxn(() async { + await MainDB.instance.isar.lelantusCoins.putAll(coins); + }); + } + } + } + } + Future<void> _v4(SecureStorageInterface secureStore) async { await Hive.openBox<dynamic>(DB.boxNameAllWalletsData); await Hive.openBox<dynamic>(DB.boxNamePrefs); diff --git a/lib/utilities/default_nodes.dart b/lib/utilities/default_nodes.dart index c8ff94120..9a6a0d914 100644 --- a/lib/utilities/default_nodes.dart +++ b/lib/utilities/default_nodes.dart @@ -34,6 +34,7 @@ abstract class DefaultNodes { bitcoincashTestnet, dogecoinTestnet, firoTestnet, + stellarTestnet, ]; static NodeModel get bitcoin => NodeModel( @@ -181,6 +182,18 @@ abstract class DefaultNodes { isFailover: true, isDown: false); + static NodeModel get stellar => NodeModel( + host: "https://horizon.stellar.org", + port: 443, + name: defaultName, + id: _nodeId(Coin.stellar), + useSSL: false, + enabled: true, + coinName: Coin.stellar.name, + isFailover: true, + isDown: false + ); + static NodeModel get nano => NodeModel( host: "https://rainstorm.city/api", port: 443, @@ -263,6 +276,18 @@ abstract class DefaultNodes { isDown: false, ); + static NodeModel get stellarTestnet => NodeModel( + host: "https://horizon-testnet.stellar.org/", + port: 50022, + name: defaultName, + id: _nodeId(Coin.stellarTestnet), + useSSL: true, + enabled: true, + coinName: Coin.stellarTestnet.name, + isFailover: true, + isDown: false, + ); + static NodeModel getNodeFor(Coin coin) { switch (coin) { case Coin.bitcoin: @@ -301,6 +326,9 @@ abstract class DefaultNodes { case Coin.particl: return particl; + case Coin.stellar: + return stellar; + case Coin.nano: return nano; @@ -321,6 +349,9 @@ abstract class DefaultNodes { case Coin.dogecoinTestNet: return dogecoinTestnet; + + case Coin.stellarTestnet: + return stellarTestnet; } } } diff --git a/lib/utilities/enums/coin_enum.dart b/lib/utilities/enums/coin_enum.dart index c656b2c62..677d474e7 100644 --- a/lib/utilities/enums/coin_enum.dart +++ b/lib/utilities/enums/coin_enum.dart @@ -27,6 +27,7 @@ import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart' import 'package:stackwallet/services/coins/nano/nano_wallet.dart' as nano; import 'package:stackwallet/services/coins/particl/particl_wallet.dart' as particl; +import 'package:stackwallet/services/coins/stellar/stellar_wallet.dart' as xlm; import 'package:stackwallet/services/coins/wownero/wownero_wallet.dart' as wow; import 'package:stackwallet/utilities/constants.dart'; @@ -44,6 +45,7 @@ enum Coin { namecoin, nano, particl, + stellar, wownero, /// @@ -56,6 +58,7 @@ enum Coin { dogecoinTestNet, firoTestNet, litecoinTestNet, + stellarTestnet, } final int kTestNetCoinCount = 4; // Util.isDesktop ? 5 : 4; @@ -84,6 +87,8 @@ extension CoinExt on Coin { return "Monero"; case Coin.particl: return "Particl"; + case Coin.stellar: + return "Stellar"; case Coin.wownero: return "Wownero"; case Coin.namecoin: @@ -102,6 +107,8 @@ extension CoinExt on Coin { return "tFiro"; case Coin.dogecoinTestNet: return "tDogecoin"; + case Coin.stellarTestnet: + return "tStellar"; } } @@ -127,6 +134,8 @@ extension CoinExt on Coin { return "XMR"; case Coin.particl: return "PART"; + case Coin.stellar: + return "XLM"; case Coin.wownero: return "WOW"; case Coin.namecoin: @@ -145,6 +154,8 @@ extension CoinExt on Coin { return "tFIRO"; case Coin.dogecoinTestNet: return "tDOGE"; + case Coin.stellarTestnet: + return "tXLM"; } } @@ -171,6 +182,8 @@ extension CoinExt on Coin { return "monero"; case Coin.particl: return "particl"; + case Coin.stellar: + return "stellar"; case Coin.wownero: return "wownero"; case Coin.namecoin: @@ -189,6 +202,8 @@ extension CoinExt on Coin { return "firo"; case Coin.dogecoinTestNet: return "dogecoin"; + case Coin.stellarTestnet: + return "stellar"; } } @@ -215,6 +230,8 @@ extension CoinExt on Coin { case Coin.wownero: case Coin.nano: case Coin.banano: + case Coin.stellar: + case Coin.stellarTestnet: return false; } } @@ -242,6 +259,8 @@ extension CoinExt on Coin { case Coin.firoTestNet: case Coin.nano: case Coin.banano: + case Coin.stellar: + case Coin.stellarTestnet: return false; } } @@ -262,6 +281,7 @@ extension CoinExt on Coin { case Coin.nano: case Coin.banano: case Coin.eCash: + case Coin.stellar: return false; case Coin.dogecoinTestNet: @@ -269,6 +289,7 @@ extension CoinExt on Coin { case Coin.litecoinTestNet: case Coin.bitcoincashTestnet: case Coin.firoTestNet: + case Coin.stellarTestnet: return true; } } @@ -289,6 +310,7 @@ extension CoinExt on Coin { case Coin.nano: case Coin.banano: case Coin.eCash: + case Coin.stellar: return this; case Coin.dogecoinTestNet: @@ -305,6 +327,9 @@ extension CoinExt on Coin { case Coin.firoTestNet: return Coin.firo; + + case Coin.stellarTestnet: + return Coin.stellar; } } @@ -345,6 +370,10 @@ extension CoinExt on Coin { case Coin.particl: return particl.MINIMUM_CONFIRMATIONS; + case Coin.stellar: + case Coin.stellarTestnet: + return xlm.MINIMUM_CONFIRMATIONS; + case Coin.wownero: return wow.MINIMUM_CONFIRMATIONS; @@ -404,6 +433,10 @@ Coin coinFromPrettyName(String name) { case "particl": return Coin.particl; + case "Stellar": + case "stellar": + return Coin.stellar; + case "Namecoin": case "namecoin": return Coin.namecoin; @@ -448,6 +481,11 @@ Coin coinFromPrettyName(String name) { case "banano": return Coin.banano; + case "Stellar Testnet": + case "stellarTestnet": + case "tStellar": + return Coin.stellarTestnet; + default: throw ArgumentError.value( name, @@ -481,6 +519,8 @@ Coin coinFromTickerCaseInsensitive(String ticker) { return Coin.namecoin; case "part": return Coin.particl; + case "xlm": + return Coin.stellar; case "tltc": return Coin.litecoinTestNet; case "tbtc": @@ -497,6 +537,8 @@ Coin coinFromTickerCaseInsensitive(String ticker) { return Coin.nano; case "ban": return Coin.banano; + case "txlm": + return Coin.stellarTestnet; default: throw ArgumentError.value( ticker, "name", "No Coin enum value with that ticker"); diff --git a/lib/utilities/enums/derive_path_type_enum.dart b/lib/utilities/enums/derive_path_type_enum.dart index f3e7f86df..07785a5ee 100644 --- a/lib/utilities/enums/derive_path_type_enum.dart +++ b/lib/utilities/enums/derive_path_type_enum.dart @@ -49,6 +49,8 @@ extension DerivePathTypeExt on DerivePathType { case Coin.wownero: case Coin.nano: case Coin.banano: + case Coin.stellar: + case Coin.stellarTestnet: throw UnsupportedError( "$coin does not use bitcoin style derivation paths"); } diff --git a/lib/utilities/show_loading.dart b/lib/utilities/show_loading.dart index db4437d6f..759eaa05c 100644 --- a/lib/utilities/show_loading.dart +++ b/lib/utilities/show_loading.dart @@ -12,15 +12,17 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/widgets/custom_loading_overlay.dart'; -Future<T> showLoading<T>({ +Future<T?> showLoading<T>({ required Future<T> whileFuture, required BuildContext context, required String message, String? subMessage, bool isDesktop = false, bool opaqueBG = false, + void Function(Exception)? onException, }) async { unawaited( showDialog<void>( @@ -43,10 +45,24 @@ Future<T> showLoading<T>({ ), ); - final result = await whileFuture; + Exception? ex; + T? result; + + try { + result = await whileFuture; + } catch (e, s) { + Logging.instance.log( + "showLoading caught: $e\n$s", + level: LogLevel.Warning, + ); + ex = e is Exception ? e : Exception(e.toString()); + } if (context.mounted) { Navigator.of(context, rootNavigator: isDesktop).pop(); + if (ex != null) { + onException?.call(ex); + } } return result; diff --git a/lib/utilities/stack_file_system.dart b/lib/utilities/stack_file_system.dart index 843bb1b71..f36fadf01 100644 --- a/lib/utilities/stack_file_system.dart +++ b/lib/utilities/stack_file_system.dart @@ -27,8 +27,8 @@ abstract class StackFileSystem { } else if (Platform.isWindows) { appDirectory = await getApplicationSupportDirectory(); } else if (Platform.isMacOS) { - // currently run in ipad mode?? - throw Exception("Unsupported platform"); + appDirectory = await getLibraryDirectory(); + appDirectory = Directory("${appDirectory.path}/stackwallet"); } else if (Platform.isIOS) { // todo: check if we need different behaviour here if (Util.isDesktop) { diff --git a/lib/utilities/text_styles.dart b/lib/utilities/text_styles.dart index 4a1ced812..fab828bef 100644 --- a/lib/utilities/text_styles.dart +++ b/lib/utilities/text_styles.dart @@ -306,6 +306,17 @@ class STextStyles { } } + static TextStyle w600_16(BuildContext context) { + switch (_theme(context).themeId) { + default: + return GoogleFonts.inter( + color: _theme(context).textDark, + fontWeight: FontWeight.w600, + fontSize: 16, + ); + } + } + static TextStyle w500_14(BuildContext context) { switch (_theme(context).themeId) { default: @@ -339,6 +350,17 @@ class STextStyles { } } + static TextStyle w500_8(BuildContext context) { + switch (_theme(context).themeId) { + default: + return GoogleFonts.inter( + color: _theme(context).textSubtitle1, + fontWeight: FontWeight.w500, + fontSize: 8, + ); + } + } + static TextStyle w600_20(BuildContext context) { switch (_theme(context).themeId) { default: diff --git a/lib/widgets/coin_card.dart b/lib/widgets/coin_card.dart index 0bebc5fd8..ebc67bb4e 100644 --- a/lib/widgets/coin_card.dart +++ b/lib/widgets/coin_card.dart @@ -25,11 +25,13 @@ class CoinCard extends ConsumerWidget { required this.walletId, required this.width, required this.height, + required this.isFavorite, }); final String walletId; final double width; final double height; + final bool isFavorite; @override Widget build(BuildContext context, WidgetRef ref) { @@ -38,7 +40,7 @@ class CoinCard extends ConsumerWidget { .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( children: [ @@ -54,7 +56,9 @@ class CoinCard extends ConsumerWidget { fit: BoxFit.cover, image: FileImage( File( - ref.watch(coinCardProvider(coin))!, + (isFavorite) + ? ref.watch(coinCardFavoritesProvider(coin))! + : ref.watch(coinCardProvider(coin))!, ), ), ), diff --git a/lib/widgets/crypto_notifications.dart b/lib/widgets/crypto_notifications.dart index f94c74ed9..b570ae4f9 100644 --- a/lib/widgets/crypto_notifications.dart +++ b/lib/widgets/crypto_notifications.dart @@ -13,6 +13,7 @@ import 'dart:async'; import 'package:event_bus/event_bus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/services/notifications_api.dart'; import 'package:stackwallet/themes/coin_icon_provider.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -84,6 +85,8 @@ class _CryptoNotificationsState extends ConsumerState<CryptoNotifications> { @override void initState() { + NotificationApi.prefs = ref.read(prefsChangeNotifierProvider); + NotificationApi.notificationsService = ref.read(notificationsProvider); _streamSubscription = CryptoNotificationsEventBus.instance .on<CryptoNotificationEvent>() .listen( diff --git a/lib/widgets/node_card.dart b/lib/widgets/node_card.dart index 9214e3e0a..57482119c 100644 --- a/lib/widgets/node_card.dart +++ b/lib/widgets/node_card.dart @@ -194,7 +194,10 @@ class _NodeCardState extends ConsumerState<NodeCard> { case Coin.nano: case Coin.banano: - //TODO: check network/node + case Coin.stellar: + case Coin.stellarTestnet: + throw UnimplementedError(); + //TODO: check network/node } if (testPassed) { diff --git a/lib/widgets/node_options_sheet.dart b/lib/widgets/node_options_sheet.dart index 16e87581c..b55f818a2 100644 --- a/lib/widgets/node_options_sheet.dart +++ b/lib/widgets/node_options_sheet.dart @@ -177,7 +177,10 @@ class NodeOptionsSheet extends ConsumerWidget { case Coin.nano: case Coin.banano: - //TODO: check network/node + case Coin.stellar: + case Coin.stellarTestnet: + throw UnimplementedError(); + //TODO: check network/node } if (testPassed) { diff --git a/lib/widgets/transaction_card.dart b/lib/widgets/transaction_card.dart index 7791ff3a0..41b81e2da 100644 --- a/lib/widgets/transaction_card.dart +++ b/lib/widgets/transaction_card.dart @@ -230,7 +230,9 @@ class _TransactionCardState extends ConsumerState<TransactionCard> { fit: BoxFit.scaleDown, child: Text( _transaction.isCancelled - ? "Cancelled" + ? coin == Coin.ethereum + ? "Failed" + : "Cancelled" : whatIsIt( _transaction.type, coin, diff --git a/lib/widgets/wallet_card.dart b/lib/widgets/wallet_card.dart index 63b3a4a0e..09dcb0e2e 100644 --- a/lib/widgets/wallet_card.dart +++ b/lib/widgets/wallet_card.dart @@ -140,7 +140,7 @@ class SimpleWalletCard extends ConsumerWidget { isDesktop: Util.isDesktop, ); - if (!success) { + if (!success!) { // TODO: show error dialog here? Logging.instance.log( "Failed to load token wallet for $contract", diff --git a/lib/widgets/wallet_navigation_bar/components/icons/ordinals_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/ordinals_nav_icon.dart new file mode 100644 index 000000000..4c23bf20a --- /dev/null +++ b/lib/widgets/wallet_navigation_bar/components/icons/ordinals_nav_icon.dart @@ -0,0 +1,28 @@ +/* + * 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:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/assets.dart'; + +class OrdinalsNavIcon extends StatelessWidget { + const OrdinalsNavIcon({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + Assets.svg.ordinal, + height: 20, + width: 20, + color: Theme.of(context).extension<StackColors>()!.bottomNavIconIcon, + ); + } +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index b741c3c0a..b044b4b00 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,6 +6,9 @@ import FlutterMacOS import Foundation import connectivity_plus +import cw_monero +import cw_shared_external +import cw_wownero import desktop_drop import device_info_plus import devicelocale @@ -13,10 +16,10 @@ import flutter_libepiccash import flutter_local_notifications import flutter_secure_storage_macos import isar_flutter_libs +import lelantus import package_info_plus import path_provider_foundation import share_plus -import shared_preferences_foundation import stack_wallet_backup import url_launcher_macos import wakelock_macos @@ -24,6 +27,9 @@ import window_size func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 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")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin")) @@ -31,10 +37,10 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin")) + LelantusPlugin.register(with: registry.registrar(forPlugin: "LelantusPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) - SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) StackWalletBackupPlugin.register(with: registry.registrar(forPlugin: "StackWalletBackupPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin")) diff --git a/macos/Podfile b/macos/Podfile index dade8dfad..c795730db 100644 --- a/macos/Podfile +++ b/macos/Podfile @@ -1,4 +1,4 @@ -platform :osx, '10.11' +platform :osx, '10.14' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -31,6 +31,9 @@ target 'Runner' do use_modular_headers! flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end end post_install do |installer| diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 82579cd8e..69c91af48 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -1,24 +1,76 @@ PODS: - - connectivity_plus_macos (0.0.1): + - connectivity_plus (0.0.1): - FlutterMacOS - ReachabilitySwift + - cw_monero (0.0.1): + - cw_monero/Boost (= 0.0.1) + - cw_monero/Monero (= 0.0.1) + - cw_monero/OpenSSL (= 0.0.1) + - cw_monero/Sodium (= 0.0.1) + - cw_monero/Unbound (= 0.0.1) + - FlutterMacOS + - cw_monero/Boost (0.0.1): + - FlutterMacOS + - cw_monero/Monero (0.0.1): + - FlutterMacOS + - cw_monero/OpenSSL (0.0.1): + - FlutterMacOS + - cw_monero/Sodium (0.0.1): + - FlutterMacOS + - cw_monero/Unbound (0.0.1): + - FlutterMacOS + - 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): - FlutterMacOS - flutter_local_notifications (0.0.1): - FlutterMacOS - - flutter_secure_storage_macos (3.3.1): + - flutter_secure_storage_macos (6.1.1): - FlutterMacOS - FlutterMacOS (1.0.0) - isar_flutter_libs (1.0.0): - FlutterMacOS - - package_info_plus_macos (0.0.1): + - lelantus (0.0.1): - FlutterMacOS - - path_provider_macos (0.0.1): + - package_info_plus (0.0.1): + - FlutterMacOS + - path_provider_foundation (0.0.1): + - Flutter - FlutterMacOS - ReachabilitySwift (5.0.0) - - share_plus_macos (0.0.1): - - FlutterMacOS - - shared_preferences_macos (0.0.1): + - share_plus (0.0.1): - FlutterMacOS - stack_wallet_backup (0.0.1): - FlutterMacOS @@ -30,16 +82,22 @@ PODS: - FlutterMacOS 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_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`) - FlutterMacOS (from `Flutter/ephemeral`) - 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`) - - path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`) - - share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`) - - shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`) + - lelantus (from `Flutter/ephemeral/.symlinks/plugins/lelantus/macos`) + - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) + - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/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`) - wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`) @@ -50,8 +108,20 @@ SPEC REPOS: - ReachabilitySwift EXTERNAL SOURCES: - connectivity_plus_macos: - :path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus_macos/macos + connectivity_plus: + :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: :path: Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos flutter_local_notifications: @@ -62,14 +132,14 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral isar_flutter_libs: :path: Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos - package_info_plus_macos: - :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos - path_provider_macos: - :path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos - share_plus_macos: - :path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos - shared_preferences_macos: - :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos + lelantus: + :path: Flutter/ephemeral/.symlinks/plugins/lelantus/macos + package_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos + path_provider_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin + share_plus: + :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos stack_wallet_backup: :path: Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos url_launcher_macos: @@ -80,22 +150,28 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/window_size/macos SPEC CHECKSUMS: - connectivity_plus_macos: f6e86fd000e971d361e54b5afcadc8c8fa773308 - flutter_libepiccash: b33f7396504712b513b8ff019a3f6f3bdae54cfb + connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747 + 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_secure_storage_macos: 6ceee8fbc7f484553ad17f79361b556259df89aa - FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811 - isar_flutter_libs: 1948109973b6c2e46d6196b1537688a36a6edeac - package_info_plus_macos: f010621b07802a241d96d01876d6705f15e77c1c - path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19 + flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea + FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 + isar_flutter_libs: 43385c99864c168fadba7c9adeddc5d38838ca6a + lelantus: 3dfbf92b1e66b3573494dfe3d6a21c4988b5361b + package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce + path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 - share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4 - shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727 + share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 stack_wallet_backup: 6ebc60b1bdcf11cf1f1cbad9aa78332e1e15778c - url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3 + url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451 wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9 window_size: 339dafa0b27a95a62a843042038fa6c3c48de195 -PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c +PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 COCOAPODS: 1.11.3 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 6506a5075..c2c2e62ad 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXAggregateTarget section */ @@ -21,15 +21,27 @@ /* End PBXAggregateTarget 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 */; }; 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - 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 */ /* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 33CC10E52044A3C60003C045 /* Project object */; @@ -46,6 +58,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + B98151822A67402A009D013C /* mobileliblelantus.framework in Bundle Framework */, ); name = "Bundle Framework"; runOnlyForDeploymentPostprocessing = 0; @@ -53,9 +66,14 @@ /* End PBXCopyFilesBuildPhase 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>"; }; 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>"; }; 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>"; }; @@ -67,26 +85,47 @@ 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>"; }; 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>"; }; - 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>"; }; - 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>"; }; - 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>"; }; + 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>"; }; + 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 */ /* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BFD0376C00E1FFD46376BB9D /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 33CC10EA2044A3C60003C045 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; 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; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = "<group>"; + }; 33BA886A226E78AF003329D5 /* Configs */ = { isa = PBXGroup; children = ( @@ -103,9 +142,10 @@ children = ( 33FAB671232836740065AC1E /* Runner */, 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, - 9000119722579F22067B9BC0 /* Pods */, + F0D4A0626F78BE1EF2A1E0D6 /* Pods */, ); sourceTree = "<group>"; }; @@ -113,6 +153,7 @@ isa = PBXGroup; children = ( 33CC10ED2044A3C60003C045 /* Stack Wallet.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, ); name = Products; sourceTree = "<group>"; @@ -152,39 +193,63 @@ path = Runner; 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 */ = { isa = PBXGroup; children = ( - 6BB87EF657A3ADFB1CE3E959 /* Pods_Runner.framework */, + B98151832A674143009D013C /* libsqlite3.0.tbd */, + B98151802A674022009D013C /* mobileliblelantus.framework */, + E6036BF01BF05EA773C76D22 /* Pods_Runner.framework */, + 9206484E84CB0AD93E3E68CA /* Pods_RunnerTests.framework */, ); name = Frameworks; 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 */ /* 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 */ = { isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - DF80A3E256A63BF2D2008937 /* [CP] Check Pods Manifest.lock */, + AAFD86C9DC38B4393BC9D8E0 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - 8D7CC24E5AE846869656D4D1 /* [CP] Embed Pods Frameworks */, + 529691D83C3BADE14E2EAC03 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -206,6 +271,10 @@ LastUpgradeCheck = 1300; ORGANIZATIONNAME = ""; TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; 33CC10EC2044A3C60003C045 = { CreatedOnToolsVersion = 9.2; LastSwiftMigration = 1100; @@ -236,12 +305,20 @@ projectRoot = ""; targets = ( 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, 33CC111A2044C6BA0003C045 /* Flutter Assemble */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 33CC10EB2044A3C60003C045 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -256,6 +333,7 @@ /* Begin PBXShellScriptBuildPhase section */ 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -291,7 +369,7 @@ shellPath = /bin/sh; 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; buildActionMask = 2147483647; files = ( @@ -308,7 +386,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - DF80A3E256A63BF2D2008937 /* [CP] Check Pods Manifest.lock */ = { + AAFD86C9DC38B4393BC9D8E0 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; 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"; 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 */ /* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 33CC10E92044A3C60003C045 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -346,6 +454,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; @@ -366,6 +479,51 @@ /* End PBXVariantGroup 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 */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; @@ -404,9 +562,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; + ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; + STRIP_INSTALLED_PRODUCT = NO; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; @@ -421,6 +581,30 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; 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; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -483,10 +667,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; + STRIP_INSTALLED_PRODUCT = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; @@ -530,9 +715,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; + ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; + STRIP_INSTALLED_PRODUCT = NO; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; @@ -547,6 +734,30 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; 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; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -567,6 +778,30 @@ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; CODE_SIGN_STYLE = Automatic; 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; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -596,6 +831,16 @@ /* End XCBuildConfiguration 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" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c9216c9be..a9d38bc3b 100644 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -37,6 +37,17 @@ </BuildableReference> </MacroExpansion> <Testables> + <TestableReference + skipped = "NO" + parallelizable = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "331C80D4294CF70F00263BE5" + BuildableName = "RunnerTests.xctest" + BlueprintName = "RunnerTests" + ReferencedContainer = "container:Runner.xcodeproj"> + </BuildableReference> + </TestableReference> </Testables> </TestAction> <LaunchAction diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png index 55efad011..65564e63b 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png index c8ba3b1a2..998750358 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png index e85f01418..7d6531e39 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png index bfb090fc6..e81fc06dc 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png index 877b87dc3..5ff3261b8 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png index f24fbe65d..6ae00a8d2 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png index b81789038..1f195abc4 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig index 6889aa0a7..391513c54 100644 --- a/macos/Runner/Configs/AppInfo.xcconfig +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -11,4 +11,4 @@ PRODUCT_NAME = Stack Wallet PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stack_wallet // The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2022 com.cypherstack. All rights reserved. +PRODUCT_COPYRIGHT = Copyright © 2023 com.cypherstack. All rights reserved. diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements index dddb8a30c..c946719a1 100644 --- a/macos/Runner/DebugProfile.entitlements +++ b/macos/Runner/DebugProfile.entitlements @@ -8,5 +8,7 @@ <true/> <key>com.apple.security.network.server</key> <true/> + <key>com.apple.security.network.client</key> + <true/> </dict> </plist> diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift index 2722837ec..3cc05eb23 100644 --- a/macos/Runner/MainFlutterWindow.swift +++ b/macos/Runner/MainFlutterWindow.swift @@ -3,7 +3,7 @@ import FlutterMacOS class MainFlutterWindow: NSWindow { override func awakeFromNib() { - let flutterViewController = FlutterViewController.init() + let flutterViewController = FlutterViewController() let windowFrame = self.frame self.contentViewController = flutterViewController self.setFrame(windowFrame, display: true) diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements index 852fa1a47..48271acc9 100644 --- a/macos/Runner/Release.entitlements +++ b/macos/Runner/Release.entitlements @@ -4,5 +4,7 @@ <dict> <key>com.apple.security.app-sandbox</key> <true/> + <key>com.apple.security.network.client</key> + <true/> </dict> </plist> diff --git a/macos/RunnerTests/RunnerTests.swift b/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 000000000..5418c9f53 --- /dev/null +++ b/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import FlutterMacOS +import Cocoa +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/pubspec.lock b/pubspec.lock index a1569c04d..914ca108c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -37,18 +37,18 @@ packages: dependency: transitive description: name: args - sha256: c372bb384f273f0c2a8aaaa226dad84dc27c8519a691b888725dec59518ad53a + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" asn1lib: dependency: transitive description: name: asn1lib - sha256: ab96a1cb3beeccf8145c52e449233fe68364c9641623acd3adad66f8184f1039 + sha256: b74e3842a52c61f8819a1ec8444b4de5419b41a7465e69d4aa681445377398b0 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" async: dependency: "direct main" description: @@ -146,10 +146,10 @@ packages: dependency: transitive description: name: build - sha256: "43865b79fbb78532e4bff7c33087aa43b1d488c4fdef014eaef568af6d8016dc" + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.1" build_config: dependency: transitive description: @@ -170,26 +170,26 @@ packages: dependency: transitive description: name: build_resolvers - sha256: db49b8609ef8c81cca2b310618c3017c00f03a92af44c04d310b907b2d692d95 + sha256: "6c4dd11d05d056e76320b828a1db0fc01ccd376922526f8e9d6c796a5adbac20" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.1" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "220ae4553e50d7c21a17c051afc7b183d28a24a420502e842f303f8e4e6edced" + sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" url: "https://pub.dev" source: hosted - version: "2.4.4" + version: "2.4.6" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "88a57f2ac99849362e73878334caa9f06ee25f31d2adced882b8337838c84e1e" + sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41" url: "https://pub.dev" source: hosted - version: "7.2.9" + version: "7.2.10" built_collection: dependency: transitive description: @@ -202,10 +202,10 @@ packages: dependency: transitive description: name: built_value - sha256: "7dd62d9faf105c434f3d829bbe9c4be02ec67f5ed94832222116122df67c5452" + sha256: "598a2a682e2a7a90f08ba39c0aaa9374c5112340f0a2e275f61b59389543d166" url: "https://pub.dev" source: hosted - version: "8.6.0" + version: "8.6.1" characters: dependency: transitive description: @@ -226,10 +226,10 @@ packages: dependency: transitive description: name: cli_util - sha256: "66f86e916d285c1a93d3b79587d94bd71984a66aac4ff74e524cfa7877f1395c" + sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 url: "https://pub.dev" source: hosted - version: "0.3.5" + version: "0.4.0" clock: dependency: transitive description: @@ -242,10 +242,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" + sha256: "4ad01d6e56db961d29661561effde45e519939fdaeb46c351275b182eac70189" url: "https://pub.dev" source: hosted - version: "4.4.0" + version: "4.5.0" collection: dependency: transitive description: @@ -298,10 +298,10 @@ packages: dependency: "direct main" description: name: crypto - sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" cryptography: dependency: transitive description: @@ -314,10 +314,10 @@ packages: dependency: transitive description: name: csslib - sha256: "831883fb353c8bdc1d71979e5b342c7d88acfbc643113c14ae51e2442ea0f20f" + sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" url: "https://pub.dev" source: hosted - version: "0.17.3" + version: "1.0.0" cw_core: dependency: "direct main" description: @@ -382,18 +382,18 @@ packages: dependency: transitive description: name: dart_style - sha256: f4f1f73ab3fd2afcbcca165ee601fe980d966af6a21b5970c6c9376955c528ad + sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" dartx: dependency: transitive description: name: dartx - sha256: "45d7176701f16c5a5e00a4798791c1964bc231491b879369c818dd9a9c764871" + sha256: "8b25435617027257d43e6508b5fe061012880ddfdaa75a71d607c3de2a13d244" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" dbus: dependency: transitive description: @@ -406,10 +406,10 @@ packages: dependency: "direct main" description: name: decimal - sha256: eece91944f523657c75a3a008a90ec7f7eb3986191153a78570c7d0ac8ef3d01 + sha256: "24a261d5d5c87e86c7651c417a5dbdf8bcd7080dd592533910e8d0505a279f21" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.3" dependency_validator: dependency: "direct dev" description: @@ -450,14 +450,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.0" + dio: + dependency: transitive + description: + name: dio + sha256: "3866d67f93523161b643187af65f5ac08bc991a5bcdaf41a2d587fe4ccb49993" + url: "https://pub.dev" + source: hosted + version: "5.3.0" dropdown_button2: dependency: "direct main" description: name: dropdown_button2 - sha256: "374f2390161bf782b4896f0b1b24cbb2b5daaa1cfb11047c3307461dcdf44e07" + sha256: "83c54a5022f898d63e3abe21240b64b937e676103207287e6705d3f9bb04d654" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.3.6" eip1559: dependency: transitive description: @@ -542,10 +550,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: "9d6e95ec73abbd31ec54d0e0df8a961017e165aba1395e462e5b31ea0c165daf" + sha256: b1729fc96627dd44012d0a901558177418818d6bd428df59dcfeb594e5f66432 url: "https://pub.dev" source: hosted - version: "5.3.1" + version: "5.3.2" fixnum: dependency: transitive description: @@ -584,10 +592,10 @@ packages: dependency: "direct dev" description: name: flutter_launcher_icons - sha256: ce0e501cfc258907842238e4ca605e74b7fd1cdf04b3b43e86c43f3e40a1592c + sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" url: "https://pub.dev" source: hosted - version: "0.11.0" + version: "0.13.1" flutter_libepiccash: dependency: "direct main" description: @@ -606,10 +614,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" flutter_local_notifications: dependency: "direct main" description: @@ -646,10 +654,10 @@ packages: dependency: "direct main" description: name: flutter_native_splash - sha256: "6777a3abb974021a39b5fdd2d46a03ca390e03903b6351f21d10e7ecc969f12d" + sha256: ba45d8cfbd778478a74696b012f33ffb6b1760c9bc531b21e2964444a4870dae url: "https://pub.dev" source: hosted - version: "2.2.16" + version: "2.3.1" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -670,10 +678,10 @@ packages: dependency: "direct main" description: name: flutter_rounded_date_picker - sha256: e7143cc5cbf3aec1536286653e38b0809abc99fb76c91bd910dbd98ae003d890 + sha256: e6aa2dc5d3b44e8bbe85ef901be69eac59ba4136427f11f4c8b2a303e1e774e7 url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.4" flutter_secure_storage: dependency: "direct main" description: @@ -821,10 +829,10 @@ packages: dependency: transitive description: name: html - sha256: "58e3491f7bf0b6a4ea5110c0c688877460d1a6366731155c4a4580e7ded773e8" + sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" url: "https://pub.dev" source: hosted - version: "0.15.3" + version: "0.15.4" http: dependency: "direct main" description: @@ -853,10 +861,10 @@ packages: dependency: transitive description: name: image - sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6" + sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "4.0.17" import_sorter: dependency: "direct dev" description: @@ -948,15 +956,15 @@ packages: path: "crypto_plugins/flutter_liblelantus" relative: true source: path - version: "0.0.1" + version: "0.0.2" lints: dependency: transitive description: name: lints - sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" local_auth: dependency: "direct main" description: @@ -977,10 +985,10 @@ packages: dependency: "direct main" description: name: lottie - sha256: "23522951540d20a57a60202ed7022e6376bed206a4eee1c347a91f58bd57eb9f" + sha256: "0793a5866062e5cc8a8b24892fa94c3095953ea914a7fdf790f550dd7537fe60" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.5.0" matcher: dependency: transitive description: @@ -1033,10 +1041,10 @@ packages: dependency: "direct dev" description: name: mockito - sha256: "8b46d7eb40abdda92d62edd01546051f0c27365e65608c284de336dccfef88cc" + sha256: "7d5b53bcd556c1bc7ffbe4e4d5a19c3e112b7e925e9e172dd7c6ad0630812616" url: "https://pub.dev" source: hosted - version: "5.4.1" + version: "5.4.2" mocktail: dependency: transitive description: @@ -1145,10 +1153,10 @@ packages: dependency: transitive description: name: path_provider_foundation - sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3" + sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.2.4" path_provider_linux: dependency: transitive description: @@ -1169,50 +1177,50 @@ packages: dependency: transitive description: name: path_provider_windows - sha256: d3f80b32e83ec208ac95253e0cd4d298e104fbc63cb29c5c69edaed43b0c69d6 + sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96" url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" permission_handler: dependency: "direct main" description: name: permission_handler - sha256: "33c6a1253d1f95fd06fa74b65b7ba907ae9811f9d5c1d3150e51417d04b8d6a8" + sha256: "63e5216aae014a72fe9579ccd027323395ce7a98271d9defa9d57320d001af81" url: "https://pub.dev" source: hosted - version: "10.2.0" + version: "10.4.3" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: d8cc6a62ded6d0f49c6eac337e080b066ee3bce4d405bd9439a61e1f1927bfe8 + sha256: "2ffaf52a21f64ac9b35fe7369bb9533edbd4f698e5604db8645b1064ff4cf221" url: "https://pub.dev" source: hosted - version: "10.2.1" + version: "10.3.3" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: ee96ac32f5a8e6f80756e25b25b9f8e535816c8e6665a96b6d70681f8c4f7e85 + sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" url: "https://pub.dev" source: hosted - version: "9.0.8" + version: "9.1.4" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: "68abbc472002b5e6dfce47fe9898c6b7d8328d58b5d2524f75e277c07a97eb84" + sha256: "7c6b1500385dd1d2ca61bb89e2488ca178e274a69144d26bbd65e33eae7c02a9" url: "https://pub.dev" source: hosted - version: "3.9.0" + version: "3.11.3" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: f67cab14b4328574938ecea2db3475dad7af7ead6afab6338772c5f88963e38b + sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 url: "https://pub.dev" source: hosted - version: "0.1.2" + version: "0.1.3" petitparser: dependency: transitive description: @@ -1221,6 +1229,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.4.0" + pinenacl: + dependency: transitive + description: + name: pinenacl + sha256: "3a5503637587d635647c93ea9a8fecf48a420cc7deebe6f1fc85c2a5637ab327" + url: "https://pub.dev" + source: hosted + version: "0.5.1" platform: dependency: transitive description: @@ -1233,10 +1249,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" pointycastle: dependency: "direct main" description: @@ -1349,62 +1365,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.1" - shared_preferences: - dependency: transitive - description: - name: shared_preferences - sha256: "16d3fb6b3692ad244a695c0183fca18cf81fd4b821664394a781de42386bf022" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - shared_preferences_android: - dependency: transitive - description: - name: shared_preferences_android - sha256: "6478c6bbbecfe9aced34c483171e90d7c078f5883558b30ec3163cf18402c749" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - shared_preferences_foundation: - dependency: transitive - description: - name: shared_preferences_foundation - sha256: e014107bb79d6d3297196f4f2d0db54b5d1f85b8ea8ff63b8e8b391a02700feb - url: "https://pub.dev" - source: hosted - version: "2.2.2" - shared_preferences_linux: - dependency: transitive - description: - name: shared_preferences_linux - sha256: "9d387433ca65717bbf1be88f4d5bb18f10508917a8fa2fb02e0fd0d7479a9afa" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - shared_preferences_platform_interface: - dependency: transitive - description: - name: shared_preferences_platform_interface - sha256: fb5cf25c0235df2d0640ac1b1174f6466bd311f621574997ac59018a6664548d - url: "https://pub.dev" - source: hosted - version: "2.2.0" - shared_preferences_web: - dependency: transitive - description: - name: shared_preferences_web - sha256: "74083203a8eae241e0de4a0d597dbedab3b8fef5563f33cf3c12d7e93c655ca5" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - shared_preferences_windows: - dependency: transitive - description: - name: shared_preferences_windows - sha256: "5e588e2efef56916a3b229c3bfe81e6a525665a454519ca51dbcc4236a274173" - url: "https://pub.dev" - source: hosted - version: "2.2.0" shelf: dependency: transitive description: @@ -1446,18 +1406,18 @@ packages: dependency: transitive description: name: source_gen - sha256: "373f96cf5a8744bc9816c1ff41cf5391bbdbe3d7a96fe98c622b6738a8a7bd33" + sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" source_helper: dependency: transitive description: name: source_helper - sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f" + sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.4" source_map_stack_trace: dependency: transitive description: @@ -1507,6 +1467,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.2+1" + stellar_flutter_sdk: + dependency: "direct main" + description: + name: stellar_flutter_sdk + sha256: "7a9b7dc76018bbd0b9c828045cf0e26e07ec44208fb1a1733273de2390205475" + url: "https://pub.dev" + source: hosted + version: "1.6.0" stream_channel: dependency: transitive description: @@ -1611,14 +1579,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" - toast: - dependency: "direct main" + toml: + dependency: transitive description: - name: toast - sha256: bedb96d37030acf9c4c06a7ac2ffd1f1f365e780cda9458c9e24e6a1e1ab6fd9 + name: toml + sha256: "69756bc12eccf279b72217a87310d217efc4b3752f722e890f672801f19ac485" url: "https://pub.dev" source: hosted - version: "0.1.5" + version: "0.13.1" tor: dependency: "direct main" description: @@ -1630,10 +1598,10 @@ packages: dependency: "direct main" description: name: tuple - sha256: "0ea99cd2f9352b2586583ab2ce6489d1f95a5f6de6fb9492faaf97ae2060f0aa" + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" typed_data: dependency: transitive description: @@ -1646,26 +1614,34 @@ packages: dependency: transitive description: name: universal_io - sha256: "06866290206d196064fd61df4c7aea1ffe9a4e7c4ccaa8fcded42dd41948005d" + sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.2" + unorm_dart: + dependency: transitive + description: + name: unorm_dart + sha256: "5b35bff83fce4d76467641438f9e867dc9bcfdb8c1694854f230579d68cd8f4b" + url: "https://pub.dev" + source: hosted + version: "0.2.0" url_launcher: dependency: "direct main" description: name: url_launcher - sha256: eb1e00ab44303d50dd487aab67ebc575456c146c6af44422f9c13889984c00f3 + sha256: "781bd58a1eb16069412365c98597726cd8810ae27435f04b3b4d3a470bacd61e" url: "https://pub.dev" source: hosted - version: "6.1.11" + version: "6.1.12" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: eed4e6a1164aa9794409325c3b707ff424d4d1c2a785e7db67f8bbda00e36e51 + sha256: "78cb6dea3e93148615109e58e42c35d1ffbf5ef66c44add673d0ab75f12ff3af" url: "https://pub.dev" source: hosted - version: "6.0.35" + version: "6.0.37" url_launcher_ios: dependency: transitive description: @@ -1686,34 +1662,34 @@ packages: dependency: transitive description: name: url_launcher_macos - sha256: "91ee3e75ea9dadf38036200c5d3743518f4a5eb77a8d13fda1ee5764373f185e" + sha256: "1c4fdc0bfea61a70792ce97157e5cc17260f61abbe4f39354513f39ec6fd73b1" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.6" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370" + sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "6bb1e5d7fe53daf02a8fee85352432a40b1f868a81880e99ec7440113d5cfcab" + sha256: cc26720eefe98c1b71d85f9dc7ef0cada5132617046369d9dc296b3ecaa5cbb4 url: "https://pub.dev" source: hosted - version: "2.0.17" + version: "2.0.18" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "254708f17f7c20a9c8c471f67d86d76d4a3f9c1591aad1e15292008aceb82771" + sha256: "7967065dd2b5fccc18c653b97958fdf839c5478c28e767c61ee879f4e7882422" url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.0.7" uuid: dependency: "direct main" description: @@ -1839,18 +1815,18 @@ packages: dependency: transitive description: name: win32 - sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" + sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee url: "https://pub.dev" source: hosted - version: "4.1.4" + version: "5.0.5" win32_registry: dependency: transitive description: name: win32_registry - sha256: "1c52f994bdccb77103a6231ad4ea331a244dbcef5d1f37d8462f713143b0bfae" + sha256: e4506d60b7244251bc59df15656a3093501c37fb5af02105a944d73eb95be4c9 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" window_size: dependency: "direct main" description: @@ -1902,4 +1878,4 @@ packages: version: "1.0.0" sdks: dart: ">=3.0.2 <4.0.0" - flutter: ">=3.10.0" + flutter: ">=3.10.3" diff --git a/pubspec.yaml b/pubspec.yaml index 2d0b502f3..9ce4aa2b4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Stack Wallet # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.7.15+181 +version: 1.7.17+185 environment: sdk: ">=3.0.2 <4.0.0" @@ -141,6 +141,7 @@ dependencies: desktop_drop: ^0.4.1 nanodart: ^2.0.0 basic_utils: ^5.5.4 + stellar_flutter_sdk: ^1.6.0 dev_dependencies: flutter_test: @@ -148,7 +149,7 @@ dev_dependencies: integration_test: sdk: flutter build_runner: ^2.1.7 - flutter_launcher_icons: ^0.11.0 + flutter_launcher_icons: ^0.13.1 hive_generator: ^2.0.0 dependency_validator: ^3.1.2 hive_test: ^1.0.1 @@ -160,7 +161,7 @@ dev_dependencies: flutter_lints: ^2.0.1 isar_generator: 3.0.5 -flutter_icons: +flutter_launcher_icons: android: true ios: true image_path: assets/icon/icon.png @@ -173,7 +174,7 @@ flutter_icons: icon_size: 48 # min:48, max:256, default: 48 macos: generate: true - image_path: assets/icon/icon.png + image_path: assets/icon/macos-icon.png flutter_native_splash: image: assets/images/splash.png @@ -338,6 +339,9 @@ flutter: - assets/svg/trocador_rating_b.svg - assets/svg/trocador_rating_c.svg - assets/svg/trocador_rating_d.svg + - assets/svg/send.svg + - assets/svg/ordinal.svg + - assets/svg/monkey.svg # coin control icons - assets/svg/coin_control/ diff --git a/scripts/macos/build_all.sh b/scripts/macos/build_all.sh new file mode 100755 index 000000000..646274593 --- /dev/null +++ b/scripts/macos/build_all.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +(cd ../../crypto_plugins/flutter_liblelantus/scripts/macos && ./build_all.sh ) & +(cd ../../crypto_plugins/flutter_libepiccash/scripts/macos && ./build_all.sh ) & +(cd ../../crypto_plugins/flutter_libmonero/scripts/macos/ && ./build_all.sh ) & + +wait +echo "Done building" \ No newline at end of file diff --git a/test/cached_electrumx_test.mocks.dart b/test/cached_electrumx_test.mocks.dart index 45cd27285..cdda4902b 100644 --- a/test/cached_electrumx_test.mocks.dart +++ b/test/cached_electrumx_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/cached_electrumx_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i4; import 'dart:ui' as _i10; @@ -28,8 +26,18 @@ import 'package:stackwallet/utilities/prefs.dart' as _i5; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeDecimal_0 extends _i1.SmartFake implements _i2.Decimal { - _FakeDecimal_0( +class _FakeDuration_0 extends _i1.SmartFake implements Duration { + _FakeDuration_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeDecimal_1 extends _i1.SmartFake implements _i2.Decimal { + _FakeDecimal_1( Object parent, Invocation parentInvocation, ) : super( @@ -68,6 +76,15 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { returnValueForMissingStub: null, ); @override + Duration get connectionTimeoutForSpecialCaseJsonRPCClients => + (super.noSuchMethod( + Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), + returnValue: _FakeDuration_0( + this, + Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), + ), + ) as Duration); + @override String get host => (super.noSuchMethod( Invocation.getter(#host), returnValue: '', @@ -86,9 +103,9 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { _i4.Future<dynamic> request({ required String? command, List<dynamic>? args = const [], - Duration? connectionTimeout = const Duration(seconds: 60), String? requestID, int? retries = 2, + Duration? requestTimeout = const Duration(seconds: 60), }) => (super.noSuchMethod( Invocation.method( @@ -97,9 +114,9 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { { #command: command, #args: args, - #connectionTimeout: connectionTimeout, #requestID: requestID, #retries: retries, + #requestTimeout: requestTimeout, }, ), returnValue: _i4.Future<dynamic>.value(), @@ -108,7 +125,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { _i4.Future<List<Map<String, dynamic>>> batchRequest({ required String? command, required Map<String, List<dynamic>>? args, - Duration? connectionTimeout = const Duration(seconds: 60), + Duration? requestTimeout = const Duration(seconds: 60), int? retries = 2, }) => (super.noSuchMethod( @@ -118,7 +135,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { { #command: command, #args: args, - #connectionTimeout: connectionTimeout, + #requestTimeout: requestTimeout, #retries: retries, }, ), @@ -359,7 +376,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { #blocks: blocks, }, ), - returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_0( + returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_1( this, Invocation.method( #estimateFee, @@ -378,7 +395,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { [], {#requestID: requestID}, ), - returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_0( + returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_1( this, Invocation.method( #relayFee, diff --git a/test/electrumx_test.dart b/test/electrumx_test.dart index 8d29f63e9..93fc77ba5 100644 --- a/test/electrumx_test.dart +++ b/test/electrumx_test.dart @@ -59,6 +59,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenAnswer( (_) async => JsonRPCResponse(data: { @@ -97,6 +98,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenAnswer( (_) async => JsonRPCResponse(data: { @@ -132,6 +134,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenThrow(Exception()); @@ -161,6 +164,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 2), ), ).thenAnswer( (_) async => JsonRPCResponse(data: { @@ -195,6 +199,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 2), ), ).thenThrow(Exception()); @@ -224,6 +229,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenAnswer( (_) async => JsonRPCResponse(data: { @@ -281,6 +287,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenThrow(Exception()); @@ -310,6 +317,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenAnswer( (_) async => JsonRPCResponse(data: { @@ -345,6 +353,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenThrow(Exception()); @@ -377,6 +386,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenAnswer( (_) async => JsonRPCResponse(data: { @@ -415,6 +425,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenThrow(Exception()); @@ -446,6 +457,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(minutes: 5), ), ).thenAnswer( (_) async => JsonRPCResponse(data: { @@ -503,6 +515,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(minutes: 5), ), ).thenThrow(Exception()); @@ -534,6 +547,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenAnswer( (_) async => JsonRPCResponse(data: { @@ -599,6 +613,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenThrow(Exception()); @@ -630,6 +645,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenAnswer( (_) async => JsonRPCResponse(data: { @@ -667,6 +683,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenThrow(Exception()); @@ -699,6 +716,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenAnswer( (_) async => JsonRPCResponse(data: { @@ -734,6 +752,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenThrow(Exception()); @@ -765,6 +784,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenAnswer( (_) async => JsonRPCResponse(data: { @@ -800,6 +820,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenThrow(Exception()); @@ -831,6 +852,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(minutes: 2), ), ).thenAnswer( (_) async => JsonRPCResponse(data: { @@ -866,6 +888,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(minutes: 2), ), ).thenThrow(Exception()); @@ -897,6 +920,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenAnswer( (_) async => JsonRPCResponse(data: { @@ -931,6 +955,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenThrow(Exception()); @@ -963,6 +988,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenAnswer( (_) async => JsonRPCResponse(data: { @@ -998,6 +1024,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenThrow(Exception()); @@ -1031,6 +1058,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenAnswer( (_) async => JsonRPCResponse(data: { @@ -1066,6 +1094,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenThrow(Exception()); @@ -1099,6 +1128,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(minutes: 2), ), ).thenAnswer( (_) async => JsonRPCResponse(data: { @@ -1134,6 +1164,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(minutes: 2), ), ).thenThrow(Exception()); @@ -1165,6 +1196,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenAnswer( (_) async => JsonRPCResponse(data: { @@ -1199,6 +1231,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenThrow(Exception()); @@ -1228,6 +1261,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenAnswer( (_) async => JsonRPCResponse(data: { @@ -1264,6 +1298,7 @@ void main() { mockClient.request( '{"jsonrpc": "2.0", "id": "some requestId",' '"method": "$command","params": $jsonArgs}', + const Duration(seconds: 60), ), ).thenThrow(Exception()); diff --git a/test/electrumx_test.mocks.dart b/test/electrumx_test.mocks.dart index 41fd2d3b3..578b1fe56 100644 --- a/test/electrumx_test.mocks.dart +++ b/test/electrumx_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/electrumx_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i3; import 'dart:ui' as _i9; @@ -80,18 +78,27 @@ class MockJsonRPC extends _i1.Mock implements _i2.JsonRPC { ), ) as Duration); @override - _i3.Future<_i2.JsonRPCResponse> request(String? jsonRpcRequest) => + _i3.Future<_i2.JsonRPCResponse> request( + String? jsonRpcRequest, + Duration? requestTimeout, + ) => (super.noSuchMethod( Invocation.method( #request, - [jsonRpcRequest], + [ + jsonRpcRequest, + requestTimeout, + ], ), returnValue: _i3.Future<_i2.JsonRPCResponse>.value(_FakeJsonRPCResponse_1( this, Invocation.method( #request, - [jsonRpcRequest], + [ + jsonRpcRequest, + requestTimeout, + ], ), )), ) as _i3.Future<_i2.JsonRPCResponse>); diff --git a/test/flutter_secure_storage_interface_test.mocks.dart b/test/flutter_secure_storage_interface_test.mocks.dart index aa9520e43..e1541d3a2 100644 --- a/test/flutter_secure_storage_interface_test.mocks.dart +++ b/test/flutter_secure_storage_interface_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/flutter_secure_storage_interface_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i3; diff --git a/test/json_rpc_test.dart b/test/json_rpc_test.dart index 1c7cc4a00..333c1bde6 100644 --- a/test/json_rpc_test.dart +++ b/test/json_rpc_test.dart @@ -15,7 +15,10 @@ void main() { const jsonRequestString = '{"jsonrpc": "2.0", "id": "some id","method": "server.ping","params": []}'; - final result = await jsonRPC.request(jsonRequestString); + final result = await jsonRPC.request( + jsonRequestString, + const Duration(seconds: 1), + ); expect(result.data, {"jsonrpc": "2.0", "result": null, "id": "some id"}); }); @@ -30,7 +33,11 @@ void main() { const jsonRequestString = '{"jsonrpc": "2.0", "id": "some id","method": "server.ping","params": []}'; - expect(() => jsonRPC.request(jsonRequestString), + expect( + () => jsonRPC.request( + jsonRequestString, + const Duration(seconds: 1), + ), throwsA(isA<SocketException>())); }); @@ -45,7 +52,11 @@ void main() { const jsonRequestString = '{"jsonrpc": "2.0", "id": "some id","method": "server.ping","params": []}'; - expect(() => jsonRPC.request(jsonRequestString), + expect( + () => jsonRPC.request( + jsonRequestString, + const Duration(seconds: 1), + ), throwsA(isA<SocketException>())); }); } diff --git a/test/models/type_adapter_tests/lelantus_coin_adapter_test.mocks.dart b/test/models/type_adapter_tests/lelantus_coin_adapter_test.mocks.dart index a77b63d95..0ca7abc8d 100644 --- a/test/models/type_adapter_tests/lelantus_coin_adapter_test.mocks.dart +++ b/test/models/type_adapter_tests/lelantus_coin_adapter_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/models/type_adapter_tests/lelantus_coin_adapter_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:convert' as _i5; import 'dart:typed_data' as _i4; diff --git a/test/models/type_adapter_tests/transactions_model_adapter_test.mocks.dart b/test/models/type_adapter_tests/transactions_model_adapter_test.mocks.dart index fce3b87a7..3d5db62f2 100644 --- a/test/models/type_adapter_tests/transactions_model_adapter_test.mocks.dart +++ b/test/models/type_adapter_tests/transactions_model_adapter_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/models/type_adapter_tests/transactions_model_adapter_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:convert' as _i5; import 'dart:typed_data' as _i4; diff --git a/test/models/type_adapter_tests/utxo_model_adapter_test.mocks.dart b/test/models/type_adapter_tests/utxo_model_adapter_test.mocks.dart index 7ac8384e6..f326e8cbc 100644 --- a/test/models/type_adapter_tests/utxo_model_adapter_test.mocks.dart +++ b/test/models/type_adapter_tests/utxo_model_adapter_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/models/type_adapter_tests/utxo_model_adapter_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:convert' as _i5; import 'dart:typed_data' as _i4; diff --git a/test/notifications/notification_card_test.mocks.dart b/test/notifications/notification_card_test.mocks.dart index 30790fd20..3f1f4b29c 100644 --- a/test/notifications/notification_card_test.mocks.dart +++ b/test/notifications/notification_card_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/notifications/notification_card_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i5; import 'dart:typed_data' as _i6; diff --git a/test/pages/send_view/send_view_test.mocks.dart b/test/pages/send_view/send_view_test.mocks.dart index 71401e8cd..c9414583d 100644 --- a/test/pages/send_view/send_view_test.mocks.dart +++ b/test/pages/send_view/send_view_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/pages/send_view/send_view_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i23; import 'dart:typed_data' as _i30; @@ -2958,6 +2956,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/price_test.dart b/test/price_test.dart index f611c5450..6741d5445 100644 --- a/test/price_test.dart +++ b/test/price_test.dart @@ -28,7 +28,7 @@ void main() { Uri.parse( "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids" "=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,bitcoin-cash" - ",namecoin,wownero,ethereum,particl,nano,banano&order=market_cap_desc&per_page=50" + ",namecoin,wownero,ethereum,particl,nano,banano,stellar&order=market_cap_desc&per_page=50" "&page=1&sparkline=false"), headers: { 'Content-Type': 'application/json' @@ -113,19 +113,21 @@ void main() { 'Coin.namecoin: [0, 0.0], ' 'Coin.nano: [0, 0.0], ' 'Coin.particl: [0, 0.0], ' + 'Coin.stellar: [0, 0.0], ' 'Coin.wownero: [0, 0.0], ' 'Coin.bitcoinTestNet: [0, 0.0], ' 'Coin.bitcoincashTestnet: [0, 0.0], ' 'Coin.dogecoinTestNet: [0, 0.0], ' 'Coin.firoTestNet: [0, 0.0], ' - 'Coin.litecoinTestNet: [0, 0.0]' + 'Coin.litecoinTestNet: [0, 0.0], ' + 'Coin.stellarTestnet: [0, 0.0]' '}', ); verify(client.get( Uri.parse( "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc" "&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin," - "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano" + "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano,stellar" "&order=market_cap_desc&per_page=50&page=1&sparkline=false", ), headers: {'Content-Type': 'application/json'})).called(1); @@ -140,7 +142,7 @@ void main() { Uri.parse( "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&" "ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin," - "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano" + "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano,stellar" "&order=market_cap_desc&per_page=50&page=1&sparkline=false"), headers: { 'Content-Type': 'application/json' @@ -219,23 +221,20 @@ void main() { '{' 'Coin.bitcoin: [1, 0.0], ' 'Coin.monero: [0.00717236, -0.77656], ' - 'Coin.banano: [0, 0.0], ' - 'Coin.bitcoincash: [0, 0.0], ' + 'Coin.banano: [0, 0.0], Coin.bitcoincash: [0, 0.0], ' 'Coin.dogecoin: [0.00000315, -2.68533], ' 'Coin.eCash: [0, 0.0], ' - 'Coin.epicCash: [0.00002803, 7.27524], ' - 'Coin.ethereum: [0, 0.0], ' + 'Coin.epicCash: [0.00002803, 7.27524], Coin.ethereum: [0, 0.0], ' 'Coin.firo: [0.0001096, -0.89304], ' 'Coin.litecoin: [0, 0.0], ' 'Coin.namecoin: [0, 0.0], ' - 'Coin.nano: [0, 0.0], ' - 'Coin.particl: [0, 0.0], ' + 'Coin.nano: [0, 0.0], Coin.particl: [0, 0.0], Coin.stellar: [0, 0.0], ' 'Coin.wownero: [0, 0.0], ' 'Coin.bitcoinTestNet: [0, 0.0], ' - 'Coin.bitcoincashTestnet: [0, 0.0], ' - 'Coin.dogecoinTestNet: [0, 0.0], ' + 'Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], ' 'Coin.firoTestNet: [0, 0.0], ' - 'Coin.litecoinTestNet: [0, 0.0]' + 'Coin.litecoinTestNet: [0, 0.0], ' + 'Coin.stellarTestnet: [0, 0.0]' '}', ); @@ -244,7 +243,7 @@ void main() { Uri.parse( "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids" "=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin," - "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano" + "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano,stellar" "&order=market_cap_desc&per_page=50&page=1&sparkline=false"), headers: {'Content-Type': 'application/json'})).called(1); @@ -258,7 +257,7 @@ void main() { Uri.parse( "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc" "&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin," - "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano" + "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano,stellar" "&order=market_cap_desc&per_page=50&page=1&sparkline=false"), headers: { 'Content-Type': 'application/json' @@ -331,8 +330,7 @@ void main() { expect( price.toString(), '{' - 'Coin.bitcoin: [0, 0.0], ' - 'Coin.monero: [0, 0.0], ' + 'Coin.bitcoin: [0, 0.0], Coin.monero: [0, 0.0], ' 'Coin.banano: [0, 0.0], ' 'Coin.bitcoincash: [0, 0.0], ' 'Coin.dogecoin: [0, 0.0], ' @@ -344,12 +342,14 @@ void main() { 'Coin.namecoin: [0, 0.0], ' 'Coin.nano: [0, 0.0], ' 'Coin.particl: [0, 0.0], ' + 'Coin.stellar: [0, 0.0], ' 'Coin.wownero: [0, 0.0], ' 'Coin.bitcoinTestNet: [0, 0.0], ' 'Coin.bitcoincashTestnet: [0, 0.0], ' 'Coin.dogecoinTestNet: [0, 0.0], ' 'Coin.firoTestNet: [0, 0.0], ' - 'Coin.litecoinTestNet: [0, 0.0]' + 'Coin.litecoinTestNet: [0, 0.0], ' + 'Coin.stellarTestnet: [0, 0.0]' '}', ); }); @@ -361,7 +361,7 @@ void main() { Uri.parse( "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc" "&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin," - "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano" + "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano,stellar" "&order=market_cap_desc&per_page=50&page=1&sparkline=false"), headers: { 'Content-Type': 'application/json' @@ -389,12 +389,14 @@ void main() { 'Coin.namecoin: [0, 0.0], ' 'Coin.nano: [0, 0.0], ' 'Coin.particl: [0, 0.0], ' + 'Coin.stellar: [0, 0.0], ' 'Coin.wownero: [0, 0.0], ' 'Coin.bitcoinTestNet: [0, 0.0], ' 'Coin.bitcoincashTestnet: [0, 0.0], ' 'Coin.dogecoinTestNet: [0, 0.0], ' 'Coin.firoTestNet: [0, 0.0], ' - 'Coin.litecoinTestNet: [0, 0.0]' + 'Coin.litecoinTestNet: [0, 0.0], ' + 'Coin.stellarTestnet: [0, 0.0]' '}', ); }); diff --git a/test/price_test.mocks.dart b/test/price_test.mocks.dart index 09b4af616..8c38f59f9 100644 --- a/test/price_test.mocks.dart +++ b/test/price_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/price_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i3; import 'dart:convert' as _i4; diff --git a/test/screen_tests/address_book_view/address_book_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/address_book_view_screen_test.mocks.dart index 5504140a9..b359426c3 100644 --- a/test/screen_tests/address_book_view/address_book_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/address_book_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/address_book_view/address_book_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i4; import 'dart:ui' as _i5; diff --git a/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart index a5f53d82b..5edc4c538 100644 --- a/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i9; import 'dart:ui' as _i11; @@ -388,6 +386,11 @@ class MockManager extends _i1.Mock implements _i12.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart index 630726884..a84bb6a51 100644 --- a/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i8; import 'dart:ui' as _i9; @@ -349,6 +347,11 @@ class MockManager extends _i1.Mock implements _i10.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart index c64135241..f44df83ca 100644 --- a/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i8; import 'dart:ui' as _i9; @@ -347,6 +345,11 @@ class MockManager extends _i1.Mock implements _i10.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/exchange/exchange_view_test.mocks.dart b/test/screen_tests/exchange/exchange_view_test.mocks.dart index bf61af30c..7b55ab9e0 100644 --- a/test/screen_tests/exchange/exchange_view_test.mocks.dart +++ b/test/screen_tests/exchange/exchange_view_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/exchange/exchange_view_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i6; import 'dart:ui' as _i9; @@ -1031,8 +1029,8 @@ class MockChangeNowAPI extends _i1.Mock implements _i13.ChangeNowAPI { [], {#apiKey: apiKey}, ), - returnValue: _i6.Future< - _i2.ExchangeResponse<List<_i20.FixedRateMarket>>>.value( + returnValue: _i6 + .Future<_i2.ExchangeResponse<List<_i20.FixedRateMarket>>>.value( _FakeExchangeResponse_0<List<_i20.FixedRateMarket>>( this, Invocation.method( @@ -1073,8 +1071,8 @@ class MockChangeNowAPI extends _i1.Mock implements _i13.ChangeNowAPI { #apiKey: apiKey, }, ), - returnValue: _i6.Future< - _i2.ExchangeResponse<_i21.ExchangeTransaction>>.value( + returnValue: _i6 + .Future<_i2.ExchangeResponse<_i21.ExchangeTransaction>>.value( _FakeExchangeResponse_0<_i21.ExchangeTransaction>( this, Invocation.method( @@ -1130,8 +1128,8 @@ class MockChangeNowAPI extends _i1.Mock implements _i13.ChangeNowAPI { #apiKey: apiKey, }, ), - returnValue: _i6.Future< - _i2.ExchangeResponse<_i21.ExchangeTransaction>>.value( + returnValue: _i6 + .Future<_i2.ExchangeResponse<_i21.ExchangeTransaction>>.value( _FakeExchangeResponse_0<_i21.ExchangeTransaction>( this, Invocation.method( @@ -1155,35 +1153,35 @@ class MockChangeNowAPI extends _i1.Mock implements _i13.ChangeNowAPI { )), ) as _i6.Future<_i2.ExchangeResponse<_i21.ExchangeTransaction>>); @override - _i6.Future<_i2.ExchangeResponse<_i22.ExchangeTransactionStatus>> - getTransactionStatus({ + _i6.Future< + _i2 + .ExchangeResponse<_i22.ExchangeTransactionStatus>> getTransactionStatus({ required String? id, String? apiKey, }) => - (super.noSuchMethod( - Invocation.method( - #getTransactionStatus, - [], - { - #id: id, - #apiKey: apiKey, - }, - ), - returnValue: _i6.Future< - _i2.ExchangeResponse<_i22.ExchangeTransactionStatus>>.value( - _FakeExchangeResponse_0<_i22.ExchangeTransactionStatus>( - this, - Invocation.method( - #getTransactionStatus, - [], - { - #id: id, - #apiKey: apiKey, - }, - ), - )), - ) as _i6 - .Future<_i2.ExchangeResponse<_i22.ExchangeTransactionStatus>>); + (super.noSuchMethod( + Invocation.method( + #getTransactionStatus, + [], + { + #id: id, + #apiKey: apiKey, + }, + ), + returnValue: _i6 + .Future<_i2.ExchangeResponse<_i22.ExchangeTransactionStatus>>.value( + _FakeExchangeResponse_0<_i22.ExchangeTransactionStatus>( + this, + Invocation.method( + #getTransactionStatus, + [], + { + #id: id, + #apiKey: apiKey, + }, + ), + )), + ) as _i6.Future<_i2.ExchangeResponse<_i22.ExchangeTransactionStatus>>); @override _i6.Future<_i2.ExchangeResponse<List<_i23.Pair>>> getAvailableFloatingRatePairs({bool? includePartners = false}) => diff --git a/test/screen_tests/lockscreen_view_screen_test.mocks.dart b/test/screen_tests/lockscreen_view_screen_test.mocks.dart index 3f7609c1f..35a1a5f20 100644 --- a/test/screen_tests/lockscreen_view_screen_test.mocks.dart +++ b/test/screen_tests/lockscreen_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/lockscreen_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i8; import 'dart:ui' as _i10; @@ -667,6 +665,11 @@ class MockManager extends _i1.Mock implements _i13.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart b/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart index 909d04a86..8526c77f3 100644 --- a/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart +++ b/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/main_view_tests/main_view_screen_testA_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i7; import 'dart:ui' as _i9; @@ -454,6 +452,11 @@ class MockManager extends _i1.Mock implements _i10.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart b/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart index 76ca1a64a..ba9350c78 100644 --- a/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart +++ b/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/main_view_tests/main_view_screen_testB_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i7; import 'dart:ui' as _i9; @@ -454,6 +452,11 @@ class MockManager extends _i1.Mock implements _i10.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart b/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart index 416090add..5f8055c5a 100644 --- a/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart +++ b/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/main_view_tests/main_view_screen_testC_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i7; import 'dart:ui' as _i9; @@ -454,6 +452,11 @@ class MockManager extends _i1.Mock implements _i10.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/onboarding/backup_key_view_screen_test.mocks.dart b/test/screen_tests/onboarding/backup_key_view_screen_test.mocks.dart index 7022de852..d621a4ae0 100644 --- a/test/screen_tests/onboarding/backup_key_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/backup_key_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/onboarding/backup_key_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i8; import 'dart:ui' as _i10; @@ -221,6 +219,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart b/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart index 4fad26d9f..6802650ab 100644 --- a/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/onboarding/backup_key_warning_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i7; import 'dart:ui' as _i9; @@ -452,6 +450,11 @@ class MockManager extends _i1.Mock implements _i10.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart b/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart index c04081c52..a9b4549a0 100644 --- a/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/onboarding/create_pin_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i8; import 'dart:ui' as _i10; @@ -667,6 +665,11 @@ class MockManager extends _i1.Mock implements _i13.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/onboarding/name_your_wallet_view_screen_test.mocks.dart b/test/screen_tests/onboarding/name_your_wallet_view_screen_test.mocks.dart index 809c10f8f..ac36cefb3 100644 --- a/test/screen_tests/onboarding/name_your_wallet_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/name_your_wallet_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/onboarding/name_your_wallet_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i3; import 'dart:ui' as _i5; diff --git a/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart b/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart index 9a351b683..a33b13e7a 100644 --- a/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/onboarding/restore_wallet_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i9; import 'dart:ui' as _i12; @@ -508,6 +506,11 @@ class MockManager extends _i1.Mock implements _i13.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/onboarding/verify_backup_key_view_screen_test.mocks.dart b/test/screen_tests/onboarding/verify_backup_key_view_screen_test.mocks.dart index 496739e8e..e372e3781 100644 --- a/test/screen_tests/onboarding/verify_backup_key_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/verify_backup_key_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/onboarding/verify_backup_key_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i8; import 'dart:ui' as _i10; @@ -221,6 +219,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/settings_view/settings_subviews/currency_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/currency_view_screen_test.mocks.dart index 5b81467df..ece50ef76 100644 --- a/test/screen_tests/settings_view/settings_subviews/currency_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/currency_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/settings_view/settings_subviews/currency_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i8; import 'dart:ui' as _i10; @@ -221,6 +219,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart index 1c5de9829..b56c08998 100644 --- a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i9; import 'dart:ui' as _i11; @@ -436,6 +434,11 @@ class MockManager extends _i1.Mock implements _i12.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart index 760b143bd..5e62fd6d4 100644 --- a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i9; import 'dart:ui' as _i11; @@ -436,6 +434,11 @@ class MockManager extends _i1.Mock implements _i12.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/settings_view/settings_subviews/network_settings_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/network_settings_view_screen_test.mocks.dart index 7aa96da8b..d364e457c 100644 --- a/test/screen_tests/settings_view/settings_subviews/network_settings_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/network_settings_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/settings_view/settings_subviews/network_settings_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i5; import 'dart:ui' as _i7; diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_backup_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_backup_view_screen_test.mocks.dart index 191e1eca7..671af0d4b 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_backup_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_backup_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/settings_view/settings_subviews/wallet_backup_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i8; import 'dart:ui' as _i10; @@ -221,6 +219,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/change_pin_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/change_pin_view_screen_test.mocks.dart index 1475c23e2..1391c64aa 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/change_pin_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/change_pin_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/change_pin_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i3; import 'dart:ui' as _i5; diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rename_wallet_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rename_wallet_view_screen_test.mocks.dart index 38c18d714..f189731e2 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rename_wallet_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rename_wallet_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rename_wallet_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i3; import 'dart:ui' as _i5; diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rescan_warning_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rescan_warning_view_screen_test.mocks.dart index df6bac383..e3a475bdd 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rescan_warning_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rescan_warning_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rescan_warning_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i8; import 'dart:ui' as _i10; @@ -221,6 +219,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart index 2ae50c123..439dd35ca 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i7; import 'dart:ui' as _i9; @@ -452,6 +450,11 @@ class MockManager extends _i1.Mock implements _i10.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart index 3548ce5a4..cc20dce98 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i8; import 'dart:ui' as _i14; @@ -688,6 +686,11 @@ class MockManager extends _i1.Mock implements _i15.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart index daee1f95e..4cfabb7bc 100644 --- a/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/settings_view/settings_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i7; import 'dart:ui' as _i9; @@ -452,6 +450,11 @@ class MockManager extends _i1.Mock implements _i10.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.mocks.dart b/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.mocks.dart index fd928ab12..95ef22442 100644 --- a/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.mocks.dart +++ b/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i4; import 'dart:ui' as _i5; diff --git a/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart b/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart index 7914d71f4..b48b433a9 100644 --- a/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart +++ b/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i8; import 'dart:ui' as _i10; @@ -223,6 +221,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.mocks.dart b/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.mocks.dart index 52b725907..b4b3ec001 100644 --- a/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.mocks.dart +++ b/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i4; import 'dart:ui' as _i5; diff --git a/test/screen_tests/wallet_view/confirm_send_view_screen_test.mocks.dart b/test/screen_tests/wallet_view/confirm_send_view_screen_test.mocks.dart index f493222d9..df0e2d613 100644 --- a/test/screen_tests/wallet_view/confirm_send_view_screen_test.mocks.dart +++ b/test/screen_tests/wallet_view/confirm_send_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/wallet_view/confirm_send_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i8; import 'dart:ui' as _i10; @@ -222,6 +220,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/wallet_view/receive_view_screen_test.mocks.dart b/test/screen_tests/wallet_view/receive_view_screen_test.mocks.dart index 6e0382474..e00ef1039 100644 --- a/test/screen_tests/wallet_view/receive_view_screen_test.mocks.dart +++ b/test/screen_tests/wallet_view/receive_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/wallet_view/receive_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i8; import 'dart:ui' as _i10; @@ -221,6 +219,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/wallet_view/send_view_screen_test.mocks.dart b/test/screen_tests/wallet_view/send_view_screen_test.mocks.dart index 97f744995..e5e86bf6e 100644 --- a/test/screen_tests/wallet_view/send_view_screen_test.mocks.dart +++ b/test/screen_tests/wallet_view/send_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/wallet_view/send_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i8; import 'dart:ui' as _i12; @@ -263,6 +261,11 @@ class MockManager extends _i1.Mock implements _i9.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart b/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart index 4a0a14f47..674ad6549 100644 --- a/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart +++ b/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/screen_tests/wallet_view/wallet_view_screen_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i8; import 'dart:ui' as _i10; @@ -223,6 +221,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/services/change_now/change_now_test.mocks.dart b/test/services/change_now/change_now_test.mocks.dart index 233216e30..21ccddff8 100644 --- a/test/services/change_now/change_now_test.mocks.dart +++ b/test/services/change_now/change_now_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/services/change_now/change_now_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i3; import 'dart:convert' as _i4; diff --git a/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart b/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart index 806152407..32719f306 100644 --- a/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart +++ b/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/services/coins/bitcoin/bitcoin_wallet_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i4; @@ -26,8 +24,8 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i6; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeDecimal_0 extends _i1.SmartFake implements _i2.Decimal { - _FakeDecimal_0( +class _FakeDuration_0 extends _i1.SmartFake implements Duration { + _FakeDuration_0( Object parent, Invocation parentInvocation, ) : super( @@ -36,8 +34,18 @@ class _FakeDecimal_0 extends _i1.SmartFake implements _i2.Decimal { ); } -class _FakeElectrumX_1 extends _i1.SmartFake implements _i3.ElectrumX { - _FakeElectrumX_1( +class _FakeDecimal_1 extends _i1.SmartFake implements _i2.Decimal { + _FakeDecimal_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeElectrumX_2 extends _i1.SmartFake implements _i3.ElectrumX { + _FakeElectrumX_2( Object parent, Invocation parentInvocation, ) : super( @@ -76,6 +84,15 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { returnValueForMissingStub: null, ); @override + Duration get connectionTimeoutForSpecialCaseJsonRPCClients => + (super.noSuchMethod( + Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), + returnValue: _FakeDuration_0( + this, + Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), + ), + ) as Duration); + @override String get host => (super.noSuchMethod( Invocation.getter(#host), returnValue: '', @@ -94,9 +111,9 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { _i4.Future<dynamic> request({ required String? command, List<dynamic>? args = const [], - Duration? connectionTimeout = const Duration(seconds: 60), String? requestID, int? retries = 2, + Duration? requestTimeout = const Duration(seconds: 60), }) => (super.noSuchMethod( Invocation.method( @@ -105,9 +122,9 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { { #command: command, #args: args, - #connectionTimeout: connectionTimeout, #requestID: requestID, #retries: retries, + #requestTimeout: requestTimeout, }, ), returnValue: _i4.Future<dynamic>.value(), @@ -116,7 +133,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { _i4.Future<List<Map<String, dynamic>>> batchRequest({ required String? command, required Map<String, List<dynamic>>? args, - Duration? connectionTimeout = const Duration(seconds: 60), + Duration? requestTimeout = const Duration(seconds: 60), int? retries = 2, }) => (super.noSuchMethod( @@ -126,7 +143,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { { #command: command, #args: args, - #connectionTimeout: connectionTimeout, + #requestTimeout: requestTimeout, #retries: retries, }, ), @@ -367,7 +384,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { #blocks: blocks, }, ), - returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_0( + returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_1( this, Invocation.method( #estimateFee, @@ -386,7 +403,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { [], {#requestID: requestID}, ), - returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_0( + returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_1( this, Invocation.method( #relayFee, @@ -408,7 +425,7 @@ class MockCachedElectrumX extends _i1.Mock implements _i5.CachedElectrumX { @override _i3.ElectrumX get electrumXClient => (super.noSuchMethod( Invocation.getter(#electrumXClient), - returnValue: _FakeElectrumX_1( + returnValue: _FakeElectrumX_2( this, Invocation.getter(#electrumXClient), ), diff --git a/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart b/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart index 992f7d49a..5599f3a29 100644 --- a/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart +++ b/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/services/coins/bitcoincash/bitcoincash_wallet_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i4; @@ -26,8 +24,8 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i6; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeDecimal_0 extends _i1.SmartFake implements _i2.Decimal { - _FakeDecimal_0( +class _FakeDuration_0 extends _i1.SmartFake implements Duration { + _FakeDuration_0( Object parent, Invocation parentInvocation, ) : super( @@ -36,8 +34,18 @@ class _FakeDecimal_0 extends _i1.SmartFake implements _i2.Decimal { ); } -class _FakeElectrumX_1 extends _i1.SmartFake implements _i3.ElectrumX { - _FakeElectrumX_1( +class _FakeDecimal_1 extends _i1.SmartFake implements _i2.Decimal { + _FakeDecimal_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeElectrumX_2 extends _i1.SmartFake implements _i3.ElectrumX { + _FakeElectrumX_2( Object parent, Invocation parentInvocation, ) : super( @@ -76,6 +84,15 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { returnValueForMissingStub: null, ); @override + Duration get connectionTimeoutForSpecialCaseJsonRPCClients => + (super.noSuchMethod( + Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), + returnValue: _FakeDuration_0( + this, + Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), + ), + ) as Duration); + @override String get host => (super.noSuchMethod( Invocation.getter(#host), returnValue: '', @@ -94,9 +111,9 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { _i4.Future<dynamic> request({ required String? command, List<dynamic>? args = const [], - Duration? connectionTimeout = const Duration(seconds: 60), String? requestID, int? retries = 2, + Duration? requestTimeout = const Duration(seconds: 60), }) => (super.noSuchMethod( Invocation.method( @@ -105,9 +122,9 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { { #command: command, #args: args, - #connectionTimeout: connectionTimeout, #requestID: requestID, #retries: retries, + #requestTimeout: requestTimeout, }, ), returnValue: _i4.Future<dynamic>.value(), @@ -116,7 +133,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { _i4.Future<List<Map<String, dynamic>>> batchRequest({ required String? command, required Map<String, List<dynamic>>? args, - Duration? connectionTimeout = const Duration(seconds: 60), + Duration? requestTimeout = const Duration(seconds: 60), int? retries = 2, }) => (super.noSuchMethod( @@ -126,7 +143,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { { #command: command, #args: args, - #connectionTimeout: connectionTimeout, + #requestTimeout: requestTimeout, #retries: retries, }, ), @@ -367,7 +384,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { #blocks: blocks, }, ), - returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_0( + returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_1( this, Invocation.method( #estimateFee, @@ -386,7 +403,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { [], {#requestID: requestID}, ), - returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_0( + returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_1( this, Invocation.method( #relayFee, @@ -408,7 +425,7 @@ class MockCachedElectrumX extends _i1.Mock implements _i5.CachedElectrumX { @override _i3.ElectrumX get electrumXClient => (super.noSuchMethod( Invocation.getter(#electrumXClient), - returnValue: _FakeElectrumX_1( + returnValue: _FakeElectrumX_2( this, Invocation.getter(#electrumXClient), ), diff --git a/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart b/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart index 7413c926a..b084875f5 100644 --- a/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart +++ b/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/services/coins/dogecoin/dogecoin_wallet_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i4; @@ -26,8 +24,8 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i6; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeDecimal_0 extends _i1.SmartFake implements _i2.Decimal { - _FakeDecimal_0( +class _FakeDuration_0 extends _i1.SmartFake implements Duration { + _FakeDuration_0( Object parent, Invocation parentInvocation, ) : super( @@ -36,8 +34,18 @@ class _FakeDecimal_0 extends _i1.SmartFake implements _i2.Decimal { ); } -class _FakeElectrumX_1 extends _i1.SmartFake implements _i3.ElectrumX { - _FakeElectrumX_1( +class _FakeDecimal_1 extends _i1.SmartFake implements _i2.Decimal { + _FakeDecimal_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeElectrumX_2 extends _i1.SmartFake implements _i3.ElectrumX { + _FakeElectrumX_2( Object parent, Invocation parentInvocation, ) : super( @@ -76,6 +84,15 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { returnValueForMissingStub: null, ); @override + Duration get connectionTimeoutForSpecialCaseJsonRPCClients => + (super.noSuchMethod( + Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), + returnValue: _FakeDuration_0( + this, + Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), + ), + ) as Duration); + @override String get host => (super.noSuchMethod( Invocation.getter(#host), returnValue: '', @@ -94,9 +111,9 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { _i4.Future<dynamic> request({ required String? command, List<dynamic>? args = const [], - Duration? connectionTimeout = const Duration(seconds: 60), String? requestID, int? retries = 2, + Duration? requestTimeout = const Duration(seconds: 60), }) => (super.noSuchMethod( Invocation.method( @@ -105,9 +122,9 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { { #command: command, #args: args, - #connectionTimeout: connectionTimeout, #requestID: requestID, #retries: retries, + #requestTimeout: requestTimeout, }, ), returnValue: _i4.Future<dynamic>.value(), @@ -116,7 +133,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { _i4.Future<List<Map<String, dynamic>>> batchRequest({ required String? command, required Map<String, List<dynamic>>? args, - Duration? connectionTimeout = const Duration(seconds: 60), + Duration? requestTimeout = const Duration(seconds: 60), int? retries = 2, }) => (super.noSuchMethod( @@ -126,7 +143,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { { #command: command, #args: args, - #connectionTimeout: connectionTimeout, + #requestTimeout: requestTimeout, #retries: retries, }, ), @@ -367,7 +384,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { #blocks: blocks, }, ), - returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_0( + returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_1( this, Invocation.method( #estimateFee, @@ -386,7 +403,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { [], {#requestID: requestID}, ), - returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_0( + returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_1( this, Invocation.method( #relayFee, @@ -408,7 +425,7 @@ class MockCachedElectrumX extends _i1.Mock implements _i5.CachedElectrumX { @override _i3.ElectrumX get electrumXClient => (super.noSuchMethod( Invocation.getter(#electrumXClient), - returnValue: _FakeElectrumX_1( + returnValue: _FakeElectrumX_2( this, Invocation.getter(#electrumXClient), ), diff --git a/test/services/coins/firo/firo_wallet_test.dart b/test/services/coins/firo/firo_wallet_test.dart index 68db15437..05b4aeeff 100644 --- a/test/services/coins/firo/firo_wallet_test.dart +++ b/test/services/coins/firo/firo_wallet_test.dart @@ -11,9 +11,7 @@ import 'package:mockito/mockito.dart'; import 'package:stackwallet/db/isar/main_db.dart'; import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart'; import 'package:stackwallet/electrumx_rpc/electrumx.dart'; -import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart'; -import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; -import 'package:stackwallet/models/lelantus_coin.dart'; +import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/lelantus_fee_data.dart'; import 'package:stackwallet/models/paymint/transactions_model.dart' as old; import 'package:stackwallet/services/coins/firo/firo_wallet.dart'; @@ -72,6 +70,7 @@ void main() { setData, List<String>.from(usedSerials), firoNetwork, + "walletId", ); const currentHeight = 100000000000; @@ -113,10 +112,7 @@ void main() { final result = await staticProcessRestore(txData, message, currentHeight); expect(result, isA<Map<String, dynamic>>()); - expect(result["mintIndex"], 8); - expect(result["jindex"], [2, 4, 6]); - expect( - result["_lelantus_coins"], isA<List<Map<dynamic, LelantusCoin>>>()); + expect(result["_lelantus_coins"], isA<List<LelantusCoin>>()); expect(result["newTxMap"], isA<Map<String, Transaction>>()); }); @@ -133,6 +129,7 @@ void main() { setData, List<String>.from(usedSerials), firoNetwork, + "walletId", ), throwsA(isA<Error>())); }); @@ -530,18 +527,10 @@ void main() { group("FiroWallet service class functions that depend on shared storage", () { const testWalletId = "testWalletID"; const testWalletName = "Test Wallet"; - bool hiveAdaptersRegistered = false; setUp(() async { await setUpTestHive(); - if (!hiveAdaptersRegistered) { - hiveAdaptersRegistered = true; - - // Registering Lelantus Model Adapters - Hive.registerAdapter(LelantusCoinAdapter()); - } - final wallets = await Hive.openBox<dynamic>('wallets'); await wallets.put('currentWalletName', testWalletName); }); @@ -1202,13 +1191,33 @@ void main() { txHash: BuildMintTxTestParams.utxoInfo["txid"] as String, coin: Coin.firo, )).thenAnswer((_) async => BuildMintTxTestParams.cachedClientResponse); + when(cachedClient.getAnonymitySet( + groupId: "1", + coin: Coin.firo, + )).thenAnswer( + (_) async => GetAnonymitySetSampleData.data, + ); + when(cachedClient.getAnonymitySet( + groupId: "2", + coin: Coin.firo, + )).thenAnswer( + (_) async => GetAnonymitySetSampleData.data, + ); when(client.getBlockHeadTip()).thenAnswer( (_) async => {"height": 455873, "hex": "this value not used here"}); + when(client.getLatestCoinId()).thenAnswer((_) async => 2); when(mainDB.getAddress("${testWalletId}buildMintTransaction", any)) .thenAnswer((realInvocation) async => null); + when(mainDB.getHighestUsedMintIndex( + walletId: "${testWalletId}submitHexToNetwork")) + .thenAnswer((_) async => null); + when(mainDB.getHighestUsedMintIndex( + walletId: "testWalletIDbuildMintTransaction")) + .thenAnswer((_) async => null); + final firo = FiroWallet( walletName: testWalletName, walletId: "${testWalletId}buildMintTransaction", diff --git a/test/services/coins/firo/firo_wallet_test.mocks.dart b/test/services/coins/firo/firo_wallet_test.mocks.dart index 95d1750e9..0f7a0b0a3 100644 --- a/test/services/coins/firo/firo_wallet_test.mocks.dart +++ b/test/services/coins/firo/firo_wallet_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/services/coins/firo/firo_wallet_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i5; @@ -32,8 +30,8 @@ import 'package:tuple/tuple.dart' as _i13; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeDecimal_0 extends _i1.SmartFake implements _i2.Decimal { - _FakeDecimal_0( +class _FakeDuration_0 extends _i1.SmartFake implements Duration { + _FakeDuration_0( Object parent, Invocation parentInvocation, ) : super( @@ -42,8 +40,8 @@ class _FakeDecimal_0 extends _i1.SmartFake implements _i2.Decimal { ); } -class _FakeElectrumX_1 extends _i1.SmartFake implements _i3.ElectrumX { - _FakeElectrumX_1( +class _FakeDecimal_1 extends _i1.SmartFake implements _i2.Decimal { + _FakeDecimal_1( Object parent, Invocation parentInvocation, ) : super( @@ -52,8 +50,8 @@ class _FakeElectrumX_1 extends _i1.SmartFake implements _i3.ElectrumX { ); } -class _FakeIsar_2 extends _i1.SmartFake implements _i4.Isar { - _FakeIsar_2( +class _FakeElectrumX_2 extends _i1.SmartFake implements _i3.ElectrumX { + _FakeElectrumX_2( Object parent, Invocation parentInvocation, ) : super( @@ -62,9 +60,19 @@ class _FakeIsar_2 extends _i1.SmartFake implements _i4.Isar { ); } -class _FakeQueryBuilder_3<OBJ, R, S> extends _i1.SmartFake +class _FakeIsar_3 extends _i1.SmartFake implements _i4.Isar { + _FakeIsar_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeQueryBuilder_4<OBJ, R, S> extends _i1.SmartFake implements _i4.QueryBuilder<OBJ, R, S> { - _FakeQueryBuilder_3( + _FakeQueryBuilder_4( Object parent, Invocation parentInvocation, ) : super( @@ -103,6 +111,15 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { returnValueForMissingStub: null, ); @override + Duration get connectionTimeoutForSpecialCaseJsonRPCClients => + (super.noSuchMethod( + Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), + returnValue: _FakeDuration_0( + this, + Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), + ), + ) as Duration); + @override String get host => (super.noSuchMethod( Invocation.getter(#host), returnValue: '', @@ -121,9 +138,9 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { _i5.Future<dynamic> request({ required String? command, List<dynamic>? args = const [], - Duration? connectionTimeout = const Duration(seconds: 60), String? requestID, int? retries = 2, + Duration? requestTimeout = const Duration(seconds: 60), }) => (super.noSuchMethod( Invocation.method( @@ -132,9 +149,9 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { { #command: command, #args: args, - #connectionTimeout: connectionTimeout, #requestID: requestID, #retries: retries, + #requestTimeout: requestTimeout, }, ), returnValue: _i5.Future<dynamic>.value(), @@ -143,7 +160,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { _i5.Future<List<Map<String, dynamic>>> batchRequest({ required String? command, required Map<String, List<dynamic>>? args, - Duration? connectionTimeout = const Duration(seconds: 60), + Duration? requestTimeout = const Duration(seconds: 60), int? retries = 2, }) => (super.noSuchMethod( @@ -153,7 +170,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { { #command: command, #args: args, - #connectionTimeout: connectionTimeout, + #requestTimeout: requestTimeout, #retries: retries, }, ), @@ -394,7 +411,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { #blocks: blocks, }, ), - returnValue: _i5.Future<_i2.Decimal>.value(_FakeDecimal_0( + returnValue: _i5.Future<_i2.Decimal>.value(_FakeDecimal_1( this, Invocation.method( #estimateFee, @@ -413,7 +430,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { [], {#requestID: requestID}, ), - returnValue: _i5.Future<_i2.Decimal>.value(_FakeDecimal_0( + returnValue: _i5.Future<_i2.Decimal>.value(_FakeDecimal_1( this, Invocation.method( #relayFee, @@ -435,7 +452,7 @@ class MockCachedElectrumX extends _i1.Mock implements _i6.CachedElectrumX { @override _i3.ElectrumX get electrumXClient => (super.noSuchMethod( Invocation.getter(#electrumXClient), - returnValue: _FakeElectrumX_1( + returnValue: _FakeElectrumX_2( this, Invocation.getter(#electrumXClient), ), @@ -603,7 +620,7 @@ class MockMainDB extends _i1.Mock implements _i9.MainDB { @override _i4.Isar get isar => (super.noSuchMethod( Invocation.getter(#isar), - returnValue: _FakeIsar_2( + returnValue: _FakeIsar_3( this, Invocation.getter(#isar), ), @@ -688,7 +705,7 @@ class MockMainDB extends _i1.Mock implements _i9.MainDB { #getAddresses, [walletId], ), - returnValue: _FakeQueryBuilder_3<_i12.Address, _i12.Address, + returnValue: _FakeQueryBuilder_4<_i12.Address, _i12.Address, _i4.QAfterWhereClause>( this, Invocation.method( @@ -761,7 +778,7 @@ class MockMainDB extends _i1.Mock implements _i9.MainDB { #getTransactions, [walletId], ), - returnValue: _FakeQueryBuilder_3<_i12.Transaction, _i12.Transaction, + returnValue: _FakeQueryBuilder_4<_i12.Transaction, _i12.Transaction, _i4.QAfterWhereClause>( this, Invocation.method( @@ -829,7 +846,7 @@ class MockMainDB extends _i1.Mock implements _i9.MainDB { [walletId], ), returnValue: - _FakeQueryBuilder_3<_i12.UTXO, _i12.UTXO, _i4.QAfterWhereClause>( + _FakeQueryBuilder_4<_i12.UTXO, _i12.UTXO, _i4.QAfterWhereClause>( this, Invocation.method( #getUTXOs, @@ -856,7 +873,7 @@ class MockMainDB extends _i1.Mock implements _i9.MainDB { returnValueForMissingStub: _i5.Future<void>.value(), ) as _i5.Future<void>); @override - _i5.Future<void> updateUTXOs( + _i5.Future<bool> updateUTXOs( String? walletId, List<_i12.UTXO>? utxos, ) => @@ -868,9 +885,8 @@ class MockMainDB extends _i1.Mock implements _i9.MainDB { utxos, ], ), - returnValue: _i5.Future<void>.value(), - returnValueForMissingStub: _i5.Future<void>.value(), - ) as _i5.Future<void>); + returnValue: _i5.Future<bool>.value(false), + ) as _i5.Future<bool>); @override _i5.Stream<_i12.UTXO?> watchUTXO({ required int? id, @@ -896,7 +912,7 @@ class MockMainDB extends _i1.Mock implements _i9.MainDB { #getTransactionNotes, [walletId], ), - returnValue: _FakeQueryBuilder_3<_i12.TransactionNote, + returnValue: _FakeQueryBuilder_4<_i12.TransactionNote, _i12.TransactionNote, _i4.QAfterWhereClause>( this, Invocation.method( @@ -965,7 +981,7 @@ class MockMainDB extends _i1.Mock implements _i9.MainDB { #getAddressLabels, [walletId], ), - returnValue: _FakeQueryBuilder_3<_i12.AddressLabel, + returnValue: _FakeQueryBuilder_4<_i12.AddressLabel, _i12.AddressLabel, _i4.QAfterWhereClause>( this, Invocation.method( @@ -1107,7 +1123,7 @@ class MockMainDB extends _i1.Mock implements _i9.MainDB { #getEthContracts, [], ), - returnValue: _FakeQueryBuilder_3<_i12.EthContract, _i12.EthContract, + returnValue: _FakeQueryBuilder_4<_i12.EthContract, _i12.EthContract, _i4.QWhere>( this, Invocation.method( @@ -1151,4 +1167,14 @@ class MockMainDB extends _i1.Mock implements _i9.MainDB { returnValue: _i5.Future<void>.value(), returnValueForMissingStub: _i5.Future<void>.value(), ) as _i5.Future<void>); + @override + _i5.Future<int?> getHighestUsedMintIndex({required String? walletId}) => + (super.noSuchMethod( + Invocation.method( + #getHighestUsedMintIndex, + [], + {#walletId: walletId}, + ), + returnValue: _i5.Future<int?>.value(), + ) as _i5.Future<int?>); } diff --git a/test/services/coins/manager_test.mocks.dart b/test/services/coins/manager_test.mocks.dart index 8b48a38a9..ba4c8eb37 100644 --- a/test/services/coins/manager_test.mocks.dart +++ b/test/services/coins/manager_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/services/coins/manager_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i11; @@ -14,7 +12,6 @@ import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart' as _i5; import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i4; import 'package:stackwallet/models/balance.dart' as _i6; import 'package:stackwallet/models/isar/models/isar_models.dart' as _i13; -import 'package:stackwallet/models/lelantus_coin.dart' as _i15; import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i3; import 'package:stackwallet/models/signing_data.dart' as _i14; import 'package:stackwallet/services/coins/firo/firo_wallet.dart' as _i10; @@ -106,8 +103,18 @@ class _FakeAmount_6 extends _i1.SmartFake implements _i8.Amount { ); } -class _FakeDecimal_7 extends _i1.SmartFake implements _i9.Decimal { - _FakeDecimal_7( +class _FakeDuration_7 extends _i1.SmartFake implements Duration { + _FakeDuration_7( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeDecimal_8 extends _i1.SmartFake implements _i9.Decimal { + _FakeDecimal_8( Object parent, Invocation parentInvocation, ) : super( @@ -589,15 +596,6 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet { returnValueForMissingStub: _i11.Future<void>.value(), ) as _i11.Future<void>); @override - List<Map<dynamic, _i15.LelantusCoin>> getLelantusCoinMap() => - (super.noSuchMethod( - Invocation.method( - #getLelantusCoinMap, - [], - ), - returnValue: <Map<dynamic, _i15.LelantusCoin>>[], - ) as List<Map<dynamic, _i15.LelantusCoin>>); - @override _i11.Future<void> anonymizeAllPublicFunds() => (super.noSuchMethod( Invocation.method( #anonymizeAllPublicFunds, @@ -755,15 +753,6 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet { returnValueForMissingStub: _i11.Future<void>.value(), ) as _i11.Future<void>); @override - _i11.Future<dynamic> getCoinsToJoinSplit(int? required) => - (super.noSuchMethod( - Invocation.method( - #getCoinsToJoinSplit, - [required], - ), - returnValue: _i11.Future<dynamic>.value(), - ) as _i11.Future<dynamic>); - @override _i11.Future<int> estimateJoinSplitFee(int? spendAmount) => (super.noSuchMethod( Invocation.method( @@ -1061,51 +1050,6 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet { ), returnValueForMissingStub: null, ); - @override - void initFiroHive(String? walletId) => super.noSuchMethod( - Invocation.method( - #initFiroHive, - [walletId], - ), - returnValueForMissingStub: null, - ); - @override - _i11.Future<void> firoUpdateJIndex(List<dynamic>? jIndex) => - (super.noSuchMethod( - Invocation.method( - #firoUpdateJIndex, - [jIndex], - ), - returnValue: _i11.Future<void>.value(), - returnValueForMissingStub: _i11.Future<void>.value(), - ) as _i11.Future<void>); - @override - _i11.Future<void> firoUpdateLelantusCoins(List<dynamic>? lelantusCoins) => - (super.noSuchMethod( - Invocation.method( - #firoUpdateLelantusCoins, - [lelantusCoins], - ), - returnValue: _i11.Future<void>.value(), - returnValueForMissingStub: _i11.Future<void>.value(), - ) as _i11.Future<void>); - @override - int firoGetMintIndex() => (super.noSuchMethod( - Invocation.method( - #firoGetMintIndex, - [], - ), - returnValue: 0, - ) as int); - @override - _i11.Future<void> firoUpdateMintIndex(int? mintIndex) => (super.noSuchMethod( - Invocation.method( - #firoUpdateMintIndex, - [mintIndex], - ), - returnValue: _i11.Future<void>.value(), - returnValueForMissingStub: _i11.Future<void>.value(), - ) as _i11.Future<void>); } /// A class which mocks [ElectrumX]. @@ -1138,6 +1082,15 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { returnValueForMissingStub: null, ); @override + Duration get connectionTimeoutForSpecialCaseJsonRPCClients => + (super.noSuchMethod( + Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), + returnValue: _FakeDuration_7( + this, + Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), + ), + ) as Duration); + @override String get host => (super.noSuchMethod( Invocation.getter(#host), returnValue: '', @@ -1156,9 +1109,9 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { _i11.Future<dynamic> request({ required String? command, List<dynamic>? args = const [], - Duration? connectionTimeout = const Duration(seconds: 60), String? requestID, int? retries = 2, + Duration? requestTimeout = const Duration(seconds: 60), }) => (super.noSuchMethod( Invocation.method( @@ -1167,9 +1120,9 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { { #command: command, #args: args, - #connectionTimeout: connectionTimeout, #requestID: requestID, #retries: retries, + #requestTimeout: requestTimeout, }, ), returnValue: _i11.Future<dynamic>.value(), @@ -1178,7 +1131,7 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { _i11.Future<List<Map<String, dynamic>>> batchRequest({ required String? command, required Map<String, List<dynamic>>? args, - Duration? connectionTimeout = const Duration(seconds: 60), + Duration? requestTimeout = const Duration(seconds: 60), int? retries = 2, }) => (super.noSuchMethod( @@ -1188,7 +1141,7 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { { #command: command, #args: args, - #connectionTimeout: connectionTimeout, + #requestTimeout: requestTimeout, #retries: retries, }, ), @@ -1429,7 +1382,7 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { #blocks: blocks, }, ), - returnValue: _i11.Future<_i9.Decimal>.value(_FakeDecimal_7( + returnValue: _i11.Future<_i9.Decimal>.value(_FakeDecimal_8( this, Invocation.method( #estimateFee, @@ -1448,7 +1401,7 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { [], {#requestID: requestID}, ), - returnValue: _i11.Future<_i9.Decimal>.value(_FakeDecimal_7( + returnValue: _i11.Future<_i9.Decimal>.value(_FakeDecimal_8( this, Invocation.method( #relayFee, diff --git a/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart b/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart index b464d141c..ea48f7e85 100644 --- a/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart +++ b/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/services/coins/namecoin/namecoin_wallet_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i4; @@ -26,8 +24,8 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i6; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeDecimal_0 extends _i1.SmartFake implements _i2.Decimal { - _FakeDecimal_0( +class _FakeDuration_0 extends _i1.SmartFake implements Duration { + _FakeDuration_0( Object parent, Invocation parentInvocation, ) : super( @@ -36,8 +34,18 @@ class _FakeDecimal_0 extends _i1.SmartFake implements _i2.Decimal { ); } -class _FakeElectrumX_1 extends _i1.SmartFake implements _i3.ElectrumX { - _FakeElectrumX_1( +class _FakeDecimal_1 extends _i1.SmartFake implements _i2.Decimal { + _FakeDecimal_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeElectrumX_2 extends _i1.SmartFake implements _i3.ElectrumX { + _FakeElectrumX_2( Object parent, Invocation parentInvocation, ) : super( @@ -76,6 +84,15 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { returnValueForMissingStub: null, ); @override + Duration get connectionTimeoutForSpecialCaseJsonRPCClients => + (super.noSuchMethod( + Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), + returnValue: _FakeDuration_0( + this, + Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), + ), + ) as Duration); + @override String get host => (super.noSuchMethod( Invocation.getter(#host), returnValue: '', @@ -94,9 +111,9 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { _i4.Future<dynamic> request({ required String? command, List<dynamic>? args = const [], - Duration? connectionTimeout = const Duration(seconds: 60), String? requestID, int? retries = 2, + Duration? requestTimeout = const Duration(seconds: 60), }) => (super.noSuchMethod( Invocation.method( @@ -105,9 +122,9 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { { #command: command, #args: args, - #connectionTimeout: connectionTimeout, #requestID: requestID, #retries: retries, + #requestTimeout: requestTimeout, }, ), returnValue: _i4.Future<dynamic>.value(), @@ -116,7 +133,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { _i4.Future<List<Map<String, dynamic>>> batchRequest({ required String? command, required Map<String, List<dynamic>>? args, - Duration? connectionTimeout = const Duration(seconds: 60), + Duration? requestTimeout = const Duration(seconds: 60), int? retries = 2, }) => (super.noSuchMethod( @@ -126,7 +143,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { { #command: command, #args: args, - #connectionTimeout: connectionTimeout, + #requestTimeout: requestTimeout, #retries: retries, }, ), @@ -367,7 +384,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { #blocks: blocks, }, ), - returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_0( + returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_1( this, Invocation.method( #estimateFee, @@ -386,7 +403,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { [], {#requestID: requestID}, ), - returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_0( + returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_1( this, Invocation.method( #relayFee, @@ -408,7 +425,7 @@ class MockCachedElectrumX extends _i1.Mock implements _i5.CachedElectrumX { @override _i3.ElectrumX get electrumXClient => (super.noSuchMethod( Invocation.getter(#electrumXClient), - returnValue: _FakeElectrumX_1( + returnValue: _FakeElectrumX_2( this, Invocation.getter(#electrumXClient), ), diff --git a/test/services/coins/particl/particl_wallet_test.mocks.dart b/test/services/coins/particl/particl_wallet_test.mocks.dart index f0e94f2ec..862c352d2 100644 --- a/test/services/coins/particl/particl_wallet_test.mocks.dart +++ b/test/services/coins/particl/particl_wallet_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/services/coins/particl/particl_wallet_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i4; @@ -26,8 +24,8 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i6; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeDecimal_0 extends _i1.SmartFake implements _i2.Decimal { - _FakeDecimal_0( +class _FakeDuration_0 extends _i1.SmartFake implements Duration { + _FakeDuration_0( Object parent, Invocation parentInvocation, ) : super( @@ -36,8 +34,18 @@ class _FakeDecimal_0 extends _i1.SmartFake implements _i2.Decimal { ); } -class _FakeElectrumX_1 extends _i1.SmartFake implements _i3.ElectrumX { - _FakeElectrumX_1( +class _FakeDecimal_1 extends _i1.SmartFake implements _i2.Decimal { + _FakeDecimal_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeElectrumX_2 extends _i1.SmartFake implements _i3.ElectrumX { + _FakeElectrumX_2( Object parent, Invocation parentInvocation, ) : super( @@ -76,6 +84,15 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { returnValueForMissingStub: null, ); @override + Duration get connectionTimeoutForSpecialCaseJsonRPCClients => + (super.noSuchMethod( + Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), + returnValue: _FakeDuration_0( + this, + Invocation.getter(#connectionTimeoutForSpecialCaseJsonRPCClients), + ), + ) as Duration); + @override String get host => (super.noSuchMethod( Invocation.getter(#host), returnValue: '', @@ -94,9 +111,9 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { _i4.Future<dynamic> request({ required String? command, List<dynamic>? args = const [], - Duration? connectionTimeout = const Duration(seconds: 60), String? requestID, int? retries = 2, + Duration? requestTimeout = const Duration(seconds: 60), }) => (super.noSuchMethod( Invocation.method( @@ -105,9 +122,9 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { { #command: command, #args: args, - #connectionTimeout: connectionTimeout, #requestID: requestID, #retries: retries, + #requestTimeout: requestTimeout, }, ), returnValue: _i4.Future<dynamic>.value(), @@ -116,7 +133,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { _i4.Future<List<Map<String, dynamic>>> batchRequest({ required String? command, required Map<String, List<dynamic>>? args, - Duration? connectionTimeout = const Duration(seconds: 60), + Duration? requestTimeout = const Duration(seconds: 60), int? retries = 2, }) => (super.noSuchMethod( @@ -126,7 +143,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { { #command: command, #args: args, - #connectionTimeout: connectionTimeout, + #requestTimeout: requestTimeout, #retries: retries, }, ), @@ -367,7 +384,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { #blocks: blocks, }, ), - returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_0( + returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_1( this, Invocation.method( #estimateFee, @@ -386,7 +403,7 @@ class MockElectrumX extends _i1.Mock implements _i3.ElectrumX { [], {#requestID: requestID}, ), - returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_0( + returnValue: _i4.Future<_i2.Decimal>.value(_FakeDecimal_1( this, Invocation.method( #relayFee, @@ -408,7 +425,7 @@ class MockCachedElectrumX extends _i1.Mock implements _i5.CachedElectrumX { @override _i3.ElectrumX get electrumXClient => (super.noSuchMethod( Invocation.getter(#electrumXClient), - returnValue: _FakeElectrumX_1( + returnValue: _FakeElectrumX_2( this, Invocation.getter(#electrumXClient), ), diff --git a/test/services/wallets_service_test.mocks.dart b/test/services/wallets_service_test.mocks.dart index 4e2b60412..b2d820427 100644 --- a/test/services/wallets_service_test.mocks.dart +++ b/test/services/wallets_service_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/services/wallets_service_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i3; diff --git a/test/widget_tests/address_book_card_test.mocks.dart b/test/widget_tests/address_book_card_test.mocks.dart index 1956952d8..b24ff6ed7 100644 --- a/test/widget_tests/address_book_card_test.mocks.dart +++ b/test/widget_tests/address_book_card_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/widget_tests/address_book_card_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i4; import 'dart:ui' as _i5; diff --git a/test/widget_tests/custom_buttons/favorite_toggle_test.mocks.dart b/test/widget_tests/custom_buttons/favorite_toggle_test.mocks.dart index 142582194..0abed1954 100644 --- a/test/widget_tests/custom_buttons/favorite_toggle_test.mocks.dart +++ b/test/widget_tests/custom_buttons/favorite_toggle_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/widget_tests/custom_buttons/favorite_toggle_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i5; import 'dart:typed_data' as _i6; diff --git a/test/widget_tests/custom_loading_overlay_test.mocks.dart b/test/widget_tests/custom_loading_overlay_test.mocks.dart index c70d52974..c96558898 100644 --- a/test/widget_tests/custom_loading_overlay_test.mocks.dart +++ b/test/widget_tests/custom_loading_overlay_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/widget_tests/custom_loading_overlay_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i5; import 'dart:typed_data' as _i6; diff --git a/test/widget_tests/desktop/desktop_scaffold_test.mocks.dart b/test/widget_tests/desktop/desktop_scaffold_test.mocks.dart index c292cdafa..37476e4c3 100644 --- a/test/widget_tests/desktop/desktop_scaffold_test.mocks.dart +++ b/test/widget_tests/desktop/desktop_scaffold_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/widget_tests/desktop/desktop_scaffold_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i5; import 'dart:typed_data' as _i6; diff --git a/test/widget_tests/managed_favorite_test.mocks.dart b/test/widget_tests/managed_favorite_test.mocks.dart index ee7b22e84..f81f66ebd 100644 --- a/test/widget_tests/managed_favorite_test.mocks.dart +++ b/test/widget_tests/managed_favorite_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/widget_tests/managed_favorite_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i23; import 'dart:typed_data' as _i29; @@ -2952,6 +2950,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/widget_tests/node_card_test.mocks.dart b/test/widget_tests/node_card_test.mocks.dart index da768f3c6..0b5f8128b 100644 --- a/test/widget_tests/node_card_test.mocks.dart +++ b/test/widget_tests/node_card_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/widget_tests/node_card_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i5; import 'dart:ui' as _i7; diff --git a/test/widget_tests/node_options_sheet_test.mocks.dart b/test/widget_tests/node_options_sheet_test.mocks.dart index 7b35c18e9..86fdf5727 100644 --- a/test/widget_tests/node_options_sheet_test.mocks.dart +++ b/test/widget_tests/node_options_sheet_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/widget_tests/node_options_sheet_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i11; import 'dart:ui' as _i13; diff --git a/test/widget_tests/table_view/table_view_row_test.mocks.dart b/test/widget_tests/table_view/table_view_row_test.mocks.dart index cce7fd163..2079f64b0 100644 --- a/test/widget_tests/table_view/table_view_row_test.mocks.dart +++ b/test/widget_tests/table_view/table_view_row_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/widget_tests/table_view/table_view_row_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i22; import 'dart:typed_data' as _i27; @@ -2198,6 +2196,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/widget_tests/trade_card_test.mocks.dart b/test/widget_tests/trade_card_test.mocks.dart index bf205972c..1bb236eb4 100644 --- a/test/widget_tests/trade_card_test.mocks.dart +++ b/test/widget_tests/trade_card_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/widget_tests/trade_card_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i5; import 'dart:typed_data' as _i6; diff --git a/test/widget_tests/transaction_card_test.mocks.dart b/test/widget_tests/transaction_card_test.mocks.dart index 4fc6f8832..66bd37cf0 100644 --- a/test/widget_tests/transaction_card_test.mocks.dart +++ b/test/widget_tests/transaction_card_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/widget_tests/transaction_card_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i19; import 'dart:typed_data' as _i34; @@ -585,6 +583,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, @@ -1602,15 +1605,6 @@ class MockFiroWallet extends _i1.Mock implements _i23.FiroWallet { returnValueForMissingStub: _i19.Future<void>.value(), ) as _i19.Future<void>); @override - List<Map<dynamic, _i8.LelantusCoin>> getLelantusCoinMap() => - (super.noSuchMethod( - Invocation.method( - #getLelantusCoinMap, - [], - ), - returnValue: <Map<dynamic, _i8.LelantusCoin>>[], - ) as List<Map<dynamic, _i8.LelantusCoin>>); - @override _i19.Future<void> anonymizeAllPublicFunds() => (super.noSuchMethod( Invocation.method( #anonymizeAllPublicFunds, @@ -1768,15 +1762,6 @@ class MockFiroWallet extends _i1.Mock implements _i23.FiroWallet { returnValueForMissingStub: _i19.Future<void>.value(), ) as _i19.Future<void>); @override - _i19.Future<dynamic> getCoinsToJoinSplit(int? required) => - (super.noSuchMethod( - Invocation.method( - #getCoinsToJoinSplit, - [required], - ), - returnValue: _i19.Future<dynamic>.value(), - ) as _i19.Future<dynamic>); - @override _i19.Future<int> estimateJoinSplitFee(int? spendAmount) => (super.noSuchMethod( Invocation.method( @@ -2075,51 +2060,6 @@ class MockFiroWallet extends _i1.Mock implements _i23.FiroWallet { ), returnValueForMissingStub: null, ); - @override - void initFiroHive(String? walletId) => super.noSuchMethod( - Invocation.method( - #initFiroHive, - [walletId], - ), - returnValueForMissingStub: null, - ); - @override - _i19.Future<void> firoUpdateJIndex(List<dynamic>? jIndex) => - (super.noSuchMethod( - Invocation.method( - #firoUpdateJIndex, - [jIndex], - ), - returnValue: _i19.Future<void>.value(), - returnValueForMissingStub: _i19.Future<void>.value(), - ) as _i19.Future<void>); - @override - _i19.Future<void> firoUpdateLelantusCoins(List<dynamic>? lelantusCoins) => - (super.noSuchMethod( - Invocation.method( - #firoUpdateLelantusCoins, - [lelantusCoins], - ), - returnValue: _i19.Future<void>.value(), - returnValueForMissingStub: _i19.Future<void>.value(), - ) as _i19.Future<void>); - @override - int firoGetMintIndex() => (super.noSuchMethod( - Invocation.method( - #firoGetMintIndex, - [], - ), - returnValue: 0, - ) as int); - @override - _i19.Future<void> firoUpdateMintIndex(int? mintIndex) => (super.noSuchMethod( - Invocation.method( - #firoUpdateMintIndex, - [mintIndex], - ), - returnValue: _i19.Future<void>.value(), - returnValueForMissingStub: _i19.Future<void>.value(), - ) as _i19.Future<void>); } /// A class which mocks [LocaleService]. @@ -3261,7 +3201,7 @@ class MockMainDB extends _i1.Mock implements _i14.MainDB { returnValueForMissingStub: _i19.Future<void>.value(), ) as _i19.Future<void>); @override - _i19.Future<void> updateUTXOs( + _i19.Future<bool> updateUTXOs( String? walletId, List<_i22.UTXO>? utxos, ) => @@ -3273,9 +3213,8 @@ class MockMainDB extends _i1.Mock implements _i14.MainDB { utxos, ], ), - returnValue: _i19.Future<void>.value(), - returnValueForMissingStub: _i19.Future<void>.value(), - ) as _i19.Future<void>); + returnValue: _i19.Future<bool>.value(false), + ) as _i19.Future<bool>); @override _i19.Stream<_i22.UTXO?> watchUTXO({ required int? id, @@ -3559,4 +3498,14 @@ class MockMainDB extends _i1.Mock implements _i14.MainDB { returnValue: _i19.Future<void>.value(), returnValueForMissingStub: _i19.Future<void>.value(), ) as _i19.Future<void>); + @override + _i19.Future<int?> getHighestUsedMintIndex({required String? walletId}) => + (super.noSuchMethod( + Invocation.method( + #getHighestUsedMintIndex, + [], + {#walletId: walletId}, + ), + returnValue: _i19.Future<int?>.value(), + ) as _i19.Future<int?>); } diff --git a/test/widget_tests/wallet_card_test.mocks.dart b/test/widget_tests/wallet_card_test.mocks.dart index 2e9675869..f2d6e83b1 100644 --- a/test/widget_tests/wallet_card_test.mocks.dart +++ b/test/widget_tests/wallet_card_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/widget_tests/wallet_card_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i21; import 'dart:typed_data' as _i28; diff --git a/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.mocks.dart b/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.mocks.dart index 78483fb45..36d369769 100644 --- a/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.mocks.dart +++ b/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i23; import 'dart:typed_data' as _i29; @@ -2307,6 +2305,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/widget_tests/wallet_info_row/wallet_info_row_test.mocks.dart b/test/widget_tests/wallet_info_row/wallet_info_row_test.mocks.dart index f0a2750fd..357672d7a 100644 --- a/test/widget_tests/wallet_info_row/wallet_info_row_test.mocks.dart +++ b/test/widget_tests/wallet_info_row/wallet_info_row_test.mocks.dart @@ -1,9 +1,7 @@ -// Mocks generated by Mockito 5.4.1 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in stackwallet/test/widget_tests/wallet_info_row/wallet_info_row_test.dart. // Do not manually edit this file. -// @dart=2.19 - // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i23; import 'dart:typed_data' as _i28; @@ -2410,6 +2408,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false,