From af457683338ecf41c1069bed6302d3311c3c3bab Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 5 Aug 2026 17:12:49 -0500 Subject: [PATCH 1/8] build: upgrade Flutter and Rust toolchains Pin Flutter 3.44.8 and Rust 1.91.0 across CI, container images, build documentation, and helper scripts. Bootstrap container jobs so the first staging build is not coupled to CI image rebuild timing. Verified: workflow YAML parsed successfully, rust_version.sh passed bash syntax checking, and the staged diff passed git diff --check. --- .github/workflows/build.yaml | 71 +++++++++++++++++++++++++++--------- .github/workflows/test.yaml | 6 +++ Dockerfile | 8 ++-- docs/building.md | 22 +++++------ scripts/rust_version.sh | 14 +++---- 5 files changed, 81 insertions(+), 40 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index e716b930ca..4851c64c22 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -48,6 +48,12 @@ jobs: fetch-depth: 0 submodules: recursive + - name: Use Flutter 3.44.8 + run: | + git -C "$FLUTTER_HOME" fetch --depth 1 origin refs/tags/3.44.8 + git -C "$FLUTTER_HOME" checkout --detach FETCH_HEAD + flutter --version + - name: Set version id: ver run: | @@ -125,6 +131,12 @@ jobs: fetch-depth: 0 submodules: recursive + - name: Use Flutter 3.44.8 + run: | + git -C "$FLUTTER_HOME" fetch --depth 1 origin refs/tags/3.44.8 + git -C "$FLUTTER_HOME" checkout --detach FETCH_HEAD + flutter --version + - name: Set version id: ver run: | @@ -521,6 +533,12 @@ jobs: fetch-depth: 0 submodules: recursive + - name: Use Flutter 3.44.8 + run: | + git -C "$FLUTTER_HOME" fetch --depth 1 origin refs/tags/3.44.8 + git -C "$FLUTTER_HOME" checkout --detach FETCH_HEAD + flutter --version + - name: Set version id: ver run: | @@ -585,6 +603,12 @@ jobs: fetch-depth: 0 submodules: recursive + - name: Use Flutter 3.44.8 + run: | + git -C "$FLUTTER_HOME" fetch --depth 1 origin refs/tags/3.44.8 + git -C "$FLUTTER_HOME" checkout --detach FETCH_HEAD + flutter --version + - name: Set version id: ver run: | @@ -766,6 +790,12 @@ jobs: fetch-depth: 0 submodules: recursive + - name: Use Flutter 3.44.8 + run: | + git -C "$FLUTTER_HOME" fetch --depth 1 origin refs/tags/3.44.8 + git -C "$FLUTTER_HOME" checkout --detach FETCH_HEAD + flutter --version + - name: Set version id: ver run: | @@ -895,8 +925,8 @@ jobs: - name: Install Rust toolchains run: | rustup toolchain install 1.85.1 - rustup toolchain install 1.89.0 - rustup default 1.89.0 + rustup toolchain install 1.91.0 + rustup default 1.91.0 - name: Configure app run: | @@ -981,8 +1011,8 @@ jobs: - name: Install Rust toolchains run: | rustup toolchain install 1.85.1 - rustup toolchain install 1.89.0 - rustup default 1.89.0 + rustup toolchain install 1.91.0 + rustup default 1.91.0 - name: Configure app run: | @@ -1056,10 +1086,10 @@ jobs: - name: Install additional Rust toolchains run: | rustup toolchain install 1.85.1 - rustup toolchain install 1.89.0 - rustup default 1.89.0 - rustup target add aarch64-apple-ios --toolchain 1.89.0 - rustup target add x86_64-apple-ios --toolchain 1.89.0 + rustup toolchain install 1.91.0 + rustup default 1.91.0 + rustup target add aarch64-apple-ios --toolchain 1.91.0 + rustup target add x86_64-apple-ios --toolchain 1.91.0 - name: Configure app run: | @@ -1224,6 +1254,12 @@ jobs: fetch-depth: 0 submodules: recursive + - name: Use Flutter 3.44.8 + run: | + git -C "$FLUTTER_HOME" fetch --depth 1 origin refs/tags/3.44.8 + git -C "$FLUTTER_HOME" checkout --detach FETCH_HEAD + flutter --version + - name: Set version id: ver run: | @@ -1353,8 +1389,8 @@ jobs: - name: Install Rust toolchains run: | rustup toolchain install 1.85.1 - rustup toolchain install 1.89.0 - rustup default 1.89.0 + rustup toolchain install 1.91.0 + rustup default 1.91.0 - name: Configure app run: | @@ -1439,9 +1475,9 @@ jobs: - name: Install Rust toolchains run: | rustup toolchain install 1.85.1 - rustup toolchain install 1.89.0 - rustup default 1.89.0 - rustup target add aarch64-apple-darwin --toolchain 1.89.0 + rustup toolchain install 1.91.0 + rustup default 1.91.0 + rustup target add aarch64-apple-darwin --toolchain 1.91.0 cargo install cargo-lipo - name: Configure app @@ -1516,10 +1552,10 @@ jobs: - name: Install additional Rust toolchains run: | rustup toolchain install 1.85.1 - rustup toolchain install 1.89.0 - rustup default 1.89.0 - rustup target add aarch64-apple-ios --toolchain 1.89.0 - rustup target add x86_64-apple-ios --toolchain 1.89.0 + rustup toolchain install 1.91.0 + rustup default 1.91.0 + rustup target add aarch64-apple-ios --toolchain 1.91.0 + rustup target add x86_64-apple-ios --toolchain 1.91.0 - name: Configure app run: | @@ -1667,4 +1703,3 @@ jobs: with: name: stack_duo-appimage-x86_64-${{ steps.ver.outputs.version }}.AppImage path: stack_duo-appimage-x86_64-${{ steps.ver.outputs.version }}.AppImage - diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index eca0145f8b..4e650a8fdf 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -18,6 +18,12 @@ jobs: fetch-depth: 0 submodules: recursive + - name: Use Flutter 3.44.8 + run: | + git -C "$FLUTTER_HOME" fetch --depth 1 origin refs/tags/3.44.8 + git -C "$FLUTTER_HOME" checkout --detach FETCH_HEAD + flutter --version + - name: Configure app run: | cd scripts diff --git a/Dockerfile b/Dockerfile index 72e8cd3e2c..5936efd1e6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,9 +32,9 @@ ENV RUSTUP_HOME=/usr/local/rustup \ PATH=/usr/local/cargo/bin:$PATH RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ - | sh -s -- -y --default-toolchain 1.89.0 --profile minimal --no-modify-path \ + | sh -s -- -y --default-toolchain 1.91.0 --profile minimal --no-modify-path \ && rustup install 1.85.1 1.71.0 stable --profile minimal \ - && rustup target add x86_64-unknown-linux-gnu --toolchain 1.89.0 \ + && rustup target add x86_64-unknown-linux-gnu --toolchain 1.91.0 \ && cargo install cargo-ndk \ && chmod -R a+rwX "$CARGO_HOME" "$RUSTUP_HOME" @@ -116,10 +116,10 @@ ENV RUSTUP_HOME=/usr/local/rustup \ PATH=/usr/local/cargo/bin:$PATH RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ - | sh -s -- -y --default-toolchain 1.89.0 --profile minimal --no-modify-path \ + | sh -s -- -y --default-toolchain 1.91.0 --profile minimal --no-modify-path \ && rustup target add \ aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android \ - --toolchain 1.89.0 \ + --toolchain 1.91.0 \ && cargo install cargo-ndk \ && chmod -R a+rwX "$CARGO_HOME" "$RUSTUP_HOME" diff --git a/docs/building.md b/docs/building.md index af2d328a94..f2ba2c7b44 100644 --- a/docs/building.md +++ b/docs/building.md @@ -14,7 +14,7 @@ Here you will find instructions on how to install the necessary tools for buildi The following instructions are for building and running on a Linux host. Alternatively, see the [Mac](#mac-host) and/or [Windows](#windows-host) section. This entire section (except for the Android Studio section) needs to be completed in WSL if building on a Windows host. ### Flutter -Install Flutter 3.38.5 by [following their guide](https://docs.flutter.dev/get-started/install/linux/desktop?tab=download#install-the-flutter-sdk). Run `flutter doctor` in a terminal to confirm its installation. +Install Flutter 3.44.8 by [following their guide](https://docs.flutter.dev/get-started/install/linux/desktop?tab=download#install-the-flutter-sdk). Run `flutter doctor` in a terminal to confirm its installation. ### Android Studio Install Android Studio. Follow instructions here [https://developer.android.com/studio/install#linux](https://developer.android.com/studio/install#linux) or install via snap: @@ -51,8 +51,8 @@ Install [Rust](https://www.rust-lang.org/tools/install) via [rustup.rs](https:// ``` curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source ~/.bashrc -rustup install 1.89.0 1.85.1 1.81.0 -rustup default 1.89.0 +rustup install 1.91.0 1.85.1 1.81.0 +rustup default 1.91.0 cargo install cargo-ndk ``` @@ -69,7 +69,7 @@ pip3 install --upgrade meson==0.64.1 markdown==3.4.1 markupsafe==2.1.1 jinja2==3 ``` ### Flutter -Install Flutter 3.38.5 by [following their guide](https://docs.flutter.dev/install/manual). +Install Flutter 3.44.8 by [following their guide](https://docs.flutter.dev/install/manual). Run `flutter doctor` in a terminal to confirm its installation. @@ -206,12 +206,12 @@ brew install brotli cairo coreutils gdbm gettext glib gmp libevent libidn2 libng ``` -Download and install [Rust](https://www.rust-lang.org/tools/install). [Rustup](https://rustup.rs/) is recommended for Rust setup. Use `rustc` to confirm successful installation. Install toolchains 1.81.0, 1.85.1, and 1.89.0 as well as `cbindgen` and `cargo-lipo` too. You will also have to add the platform target(s) `aarch64-apple-ios` and/or `aarch64-apple-darwin`. You can use the command(s): +Download and install [Rust](https://www.rust-lang.org/tools/install). [Rustup](https://rustup.rs/) is recommended for Rust setup. Use `rustc` to confirm successful installation. Install toolchains 1.81.0, 1.85.1, and 1.91.0 as well as `cbindgen` and `cargo-lipo` too. You will also have to add the platform target(s) `aarch64-apple-ios` and/or `aarch64-apple-darwin`. You can use the command(s): ``` curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source ~/.bashrc -rustup install 1.89.0 1.85.1 1.81.0 -rustup default 1.89.0 +rustup install 1.91.0 1.85.1 1.81.0 +rustup default 1.91.0 cargo install cargo-ndk cargo install cbindgen cargo-lipo rustup target add aarch64-apple-ios aarch64-apple-darwin @@ -220,7 +220,7 @@ rustup target add aarch64-apple-ios aarch64-apple-darwin Optionally download [Android Studio](https://developer.android.com/studio) as an IDE and activate its Dart and Flutter plugins. VS Code may work as an alternative, but this is not recommended. ### Flutter -Install 3.38.5 on your Mac host by [following their guide](https://docs.flutter.dev/install/manual). Run `flutter doctor` in a terminal to confirm its installation. +Install 3.44.8 on your Mac host by [following their guide](https://docs.flutter.dev/install/manual). Run `flutter doctor` in a terminal to confirm its installation. ### Build plugins and configure #### Building plugins for iOS @@ -302,13 +302,13 @@ If the DLLs were built on the WSL filesystem instead of on Windows, copy the res Frostdart will be built by the Windows host later. ### Install Flutter on Windows host -Install Flutter 3.38.5 on your Windows host (not in WSL2) by [following their guide](https://docs.flutter.dev/install/manual). Run `flutter doctor` in PowerShell to confirm its installation. +Install Flutter 3.44.8 on your Windows host (not in WSL2) by [following their guide](https://docs.flutter.dev/install/manual). Run `flutter doctor` in PowerShell to confirm its installation. ### Rust Install [Rust](https://www.rust-lang.org/tools/install) on the Windows host (not in WSL2). Download the installer from [rustup.rs](https://rustup.rs), make sure it works on the commandline (you may need to open a new terminal), and install the following versions: ``` -rustup install 1.89.0 1.85.1 1.81.0 -rustup default 1.89.0 +rustup install 1.91.0 1.85.1 1.81.0 +rustup default 1.91.0 cargo install cargo-ndk ``` diff --git a/scripts/rust_version.sh b/scripts/rust_version.sh index 68c52d6b97..4c66d8c047 100755 --- a/scripts/rust_version.sh +++ b/scripts/rust_version.sh @@ -2,19 +2,19 @@ set_rust_to_everything_else() { - if rustup toolchain list | grep -q "1.85.1"; then - rustup default 1.89.0 + if rustup toolchain list | grep -q "1.91.0"; then + rustup default 1.91.0 else - echo "Rust version 1.89.0 is not installed. Please install it using 'rustup install 1.89.0'." >&2 + echo "Rust version 1.91.0 is not installed. Please install it using 'rustup install 1.91.0'." >&2 exit 1 fi } set_rust_version_for_libepiccash() { - if rustup toolchain list | grep -q "1.89.0"; then - rustup default 1.89.0 + if rustup toolchain list | grep -q "1.91.0"; then + rustup default 1.91.0 else - echo "Rust version 1.89.0 is not installed. Please install it using 'rustup install 1.89.0'." >&2 + echo "Rust version 1.91.0 is not installed. Please install it using 'rustup install 1.91.0'." >&2 exit 1 fi } @@ -26,4 +26,4 @@ set_rust_version_for_libmwc() { echo "Rust version 1.85.1 is not installed. Please install it using 'rustup install 1.85.1'." >&2 exit 1 fi -} \ No newline at end of file +} From e3588ee36849a892e083be6946cb5da75431d79f Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 5 Aug 2026 17:13:06 -0500 Subject: [PATCH 2/8] ci: complete fallback API key stubs Keep the no-secret pull-request path aligned with scripts/prebuild.sh so exchange imports compile when repository secrets are unavailable. Verified: test workflow YAML parsed successfully and the staged diff passed git diff --check. --- .github/workflows/test.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 4e650a8fdf..f572c33fb6 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -62,6 +62,10 @@ jobs: const kShopInBitPartnerSecret = ""; const kCakePayApiToken = ""; const kExolixApiKey = ""; + const kLetsExchangeId = ""; + const kLetsExchangeToken = ""; + const kCypherGoatApiKey = ""; + const kCypherGoatAffiliate = ""; EOF fi From 746137b55c989ca04c54896c0bfad7139f43f9c7 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 5 Aug 2026 17:15:37 -0500 Subject: [PATCH 3/8] deps(xelis): upgrade SDK and generator stack Upgrade xelis_dart_sdk to 0.35.1 and xelis_flutter to v0.3.0. Move Isar to 3.3.2 with the analyzer 10 / Hive CE 1.11.1 generator graph, regenerate schemas, and retain the generated Hive registrar. Handle the new pending-transaction wallet event without treating it as finalized, and add an API smoke test. Regenerating the lockfile also restores unrelated entries that the original update branch had resolved against stale template inputs. Verified on Flutter 3.44.8 / Dart 3.12.2: pub get succeeded; build_runner wrote 0 additional outputs; focused Xelis, Hive, and Isar tests passed 16/16; Xelis cargo check completed with Rust 1.91.0; scoped analyzer reported no errors; formatting and git diff checks passed. --- lib/hive_registrar.g.dart | 27 +++++++ .../isar/exchange_cache/currency.g.dart | 2 +- lib/models/isar/exchange_cache/pair.g.dart | 2 +- lib/models/isar/models/address_label.g.dart | 2 +- lib/models/isar/models/block_explorer.g.dart | 2 +- .../models/blockchain_data/address.g.dart | 2 +- .../models/blockchain_data/transaction.g.dart | 2 +- .../isar/models/blockchain_data/utxo.g.dart | 2 +- .../blockchain_data/v2/transaction_v2.g.dart | 2 +- lib/models/isar/models/contact_entry.g.dart | 2 +- .../isar/models/encrypted_string_value.g.dart | 2 +- .../isar/models/ethereum/eth_contract.g.dart | 2 +- lib/models/isar/models/log.g.dart | 2 +- lib/models/isar/models/sent_to_address.g.dart | 2 +- .../isar/models/solana/sol_contract.g.dart | 2 +- .../isar/models/transaction_note.g.dart | 2 +- lib/models/isar/ordinal.g.dart | 2 +- lib/models/isar/stack_theme.g.dart | 2 +- .../isar/models/frost_wallet_info.g.dart | 2 +- lib/wallets/isar/models/spark_coin.g.dart | 2 +- .../isar/models/token_wallet_info.g.dart | 2 +- lib/wallets/isar/models/wallet_info.g.dart | 2 +- .../isar/models/wallet_info_meta.g.dart | 2 +- .../models/wallet_solana_token_info.g.dart | 2 +- pubspec.lock | 80 ++++++++----------- .../templates/pubspec.template.yaml | 19 ++--- test/xelis_dependency_smoke_test.dart | 22 +++++ ...XEL_lib_xelis_interface_impl.template.dart | 2 + 28 files changed, 114 insertions(+), 82 deletions(-) create mode 100644 lib/hive_registrar.g.dart create mode 100644 test/xelis_dependency_smoke_test.dart diff --git a/lib/hive_registrar.g.dart b/lib/hive_registrar.g.dart new file mode 100644 index 0000000000..65261eb4aa --- /dev/null +++ b/lib/hive_registrar.g.dart @@ -0,0 +1,27 @@ +// Generated by Hive CE +// Do not modify +// Check in to version control + +import 'package:hive_ce/hive_ce.dart'; +import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.dart'; +import 'package:stackwallet/models/exchange/response_objects/trade.dart'; +import 'package:stackwallet/models/mwcmqs_config_model.dart'; +import 'package:stackwallet/models/mwcmqs_server_model.dart'; + +extension HiveRegistrar on HiveInterface { + void registerAdapters() { + registerAdapter(ExchangeTransactionStatusAdapter()); + registerAdapter(MwcMqsConfigModelAdapter()); + registerAdapter(MwcMqsServerModelAdapter()); + registerAdapter(TradeAdapter()); + } +} + +extension IsolatedHiveRegistrar on IsolatedHiveInterface { + void registerAdapters() { + registerAdapter(ExchangeTransactionStatusAdapter()); + registerAdapter(MwcMqsConfigModelAdapter()); + registerAdapter(MwcMqsServerModelAdapter()); + registerAdapter(TradeAdapter()); + } +} diff --git a/lib/models/isar/exchange_cache/currency.g.dart b/lib/models/isar/exchange_cache/currency.g.dart index 20865c68c6..b450b3f68f 100644 --- a/lib/models/isar/exchange_cache/currency.g.dart +++ b/lib/models/isar/exchange_cache/currency.g.dart @@ -117,7 +117,7 @@ const CurrencySchema = CollectionSchema( getId: _currencyGetId, getLinks: _currencyGetLinks, attach: _currencyAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _currencyEstimateSize( diff --git a/lib/models/isar/exchange_cache/pair.g.dart b/lib/models/isar/exchange_cache/pair.g.dart index 81479ec618..2d25e5a541 100644 --- a/lib/models/isar/exchange_cache/pair.g.dart +++ b/lib/models/isar/exchange_cache/pair.g.dart @@ -82,7 +82,7 @@ const PairSchema = CollectionSchema( getId: _pairGetId, getLinks: _pairGetLinks, attach: _pairAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _pairEstimateSize( diff --git a/lib/models/isar/models/address_label.g.dart b/lib/models/isar/models/address_label.g.dart index 077359ac8c..803b5f579f 100644 --- a/lib/models/isar/models/address_label.g.dart +++ b/lib/models/isar/models/address_label.g.dart @@ -75,7 +75,7 @@ const AddressLabelSchema = CollectionSchema( getId: _addressLabelGetId, getLinks: _addressLabelGetLinks, attach: _addressLabelAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _addressLabelEstimateSize( diff --git a/lib/models/isar/models/block_explorer.g.dart b/lib/models/isar/models/block_explorer.g.dart index 4265524c41..09258dca60 100644 --- a/lib/models/isar/models/block_explorer.g.dart +++ b/lib/models/isar/models/block_explorer.g.dart @@ -48,7 +48,7 @@ const TransactionBlockExplorerSchema = CollectionSchema( getId: _transactionBlockExplorerGetId, getLinks: _transactionBlockExplorerGetLinks, attach: _transactionBlockExplorerAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _transactionBlockExplorerEstimateSize( diff --git a/lib/models/isar/models/blockchain_data/address.g.dart b/lib/models/isar/models/blockchain_data/address.g.dart index 2878d16402..6bd5857045 100644 --- a/lib/models/isar/models/blockchain_data/address.g.dart +++ b/lib/models/isar/models/blockchain_data/address.g.dart @@ -128,7 +128,7 @@ const AddressSchema = CollectionSchema( getId: _addressGetId, getLinks: _addressGetLinks, attach: _addressAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _addressEstimateSize( diff --git a/lib/models/isar/models/blockchain_data/transaction.g.dart b/lib/models/isar/models/blockchain_data/transaction.g.dart index c87673a99a..32de2b9bc3 100644 --- a/lib/models/isar/models/blockchain_data/transaction.g.dart +++ b/lib/models/isar/models/blockchain_data/transaction.g.dart @@ -151,7 +151,7 @@ const TransactionSchema = CollectionSchema( getId: _transactionGetId, getLinks: _transactionGetLinks, attach: _transactionAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _transactionEstimateSize( diff --git a/lib/models/isar/models/blockchain_data/utxo.g.dart b/lib/models/isar/models/blockchain_data/utxo.g.dart index b338040982..1d8cee5043 100644 --- a/lib/models/isar/models/blockchain_data/utxo.g.dart +++ b/lib/models/isar/models/blockchain_data/utxo.g.dart @@ -127,7 +127,7 @@ const UTXOSchema = CollectionSchema( getId: _uTXOGetId, getLinks: _uTXOGetLinks, attach: _uTXOAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _uTXOEstimateSize( diff --git a/lib/models/isar/models/blockchain_data/v2/transaction_v2.g.dart b/lib/models/isar/models/blockchain_data/v2/transaction_v2.g.dart index c604f36f7c..37b2df0af9 100644 --- a/lib/models/isar/models/blockchain_data/v2/transaction_v2.g.dart +++ b/lib/models/isar/models/blockchain_data/v2/transaction_v2.g.dart @@ -157,7 +157,7 @@ const TransactionV2Schema = CollectionSchema( getId: _transactionV2GetId, getLinks: _transactionV2GetLinks, attach: _transactionV2Attach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _transactionV2EstimateSize( diff --git a/lib/models/isar/models/contact_entry.g.dart b/lib/models/isar/models/contact_entry.g.dart index 99b745c3a7..699565528f 100644 --- a/lib/models/isar/models/contact_entry.g.dart +++ b/lib/models/isar/models/contact_entry.g.dart @@ -68,7 +68,7 @@ const ContactEntrySchema = CollectionSchema( getId: _contactEntryGetId, getLinks: _contactEntryGetLinks, attach: _contactEntryAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _contactEntryEstimateSize( diff --git a/lib/models/isar/models/encrypted_string_value.g.dart b/lib/models/isar/models/encrypted_string_value.g.dart index 2b99600117..6af468faf8 100644 --- a/lib/models/isar/models/encrypted_string_value.g.dart +++ b/lib/models/isar/models/encrypted_string_value.g.dart @@ -48,7 +48,7 @@ const EncryptedStringValueSchema = CollectionSchema( getId: _encryptedStringValueGetId, getLinks: _encryptedStringValueGetLinks, attach: _encryptedStringValueAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _encryptedStringValueEstimateSize( diff --git a/lib/models/isar/models/ethereum/eth_contract.g.dart b/lib/models/isar/models/ethereum/eth_contract.g.dart index b5b3aadbc2..8933e27e81 100644 --- a/lib/models/isar/models/ethereum/eth_contract.g.dart +++ b/lib/models/isar/models/ethereum/eth_contract.g.dart @@ -56,7 +56,7 @@ const EthContractSchema = CollectionSchema( getId: _ethContractGetId, getLinks: _ethContractGetLinks, attach: _ethContractAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _ethContractEstimateSize( diff --git a/lib/models/isar/models/log.g.dart b/lib/models/isar/models/log.g.dart index 358c9c6b15..65a8c75059 100644 --- a/lib/models/isar/models/log.g.dart +++ b/lib/models/isar/models/log.g.dart @@ -57,7 +57,7 @@ const LogSchema = CollectionSchema( getId: _logGetId, getLinks: _logGetLinks, attach: _logAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _logEstimateSize( diff --git a/lib/models/isar/models/sent_to_address.g.dart b/lib/models/isar/models/sent_to_address.g.dart index 354b03038e..f43267deb7 100644 --- a/lib/models/isar/models/sent_to_address.g.dart +++ b/lib/models/isar/models/sent_to_address.g.dart @@ -71,7 +71,7 @@ const SentToAddressSchema = CollectionSchema( getId: _sentToAddressGetId, getLinks: _sentToAddressGetLinks, attach: _sentToAddressAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _sentToAddressEstimateSize( diff --git a/lib/models/isar/models/solana/sol_contract.g.dart b/lib/models/isar/models/solana/sol_contract.g.dart index b75906e254..79a2a1948a 100644 --- a/lib/models/isar/models/solana/sol_contract.g.dart +++ b/lib/models/isar/models/solana/sol_contract.g.dart @@ -55,7 +55,7 @@ const SolContractSchema = CollectionSchema( getId: _solContractGetId, getLinks: _solContractGetLinks, attach: _solContractAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _solContractEstimateSize( diff --git a/lib/models/isar/models/transaction_note.g.dart b/lib/models/isar/models/transaction_note.g.dart index 9a16ae190c..76f39a5ade 100644 --- a/lib/models/isar/models/transaction_note.g.dart +++ b/lib/models/isar/models/transaction_note.g.dart @@ -70,7 +70,7 @@ const TransactionNoteSchema = CollectionSchema( getId: _transactionNoteGetId, getLinks: _transactionNoteGetLinks, attach: _transactionNoteAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _transactionNoteEstimateSize( diff --git a/lib/models/isar/ordinal.g.dart b/lib/models/isar/ordinal.g.dart index 09ba404340..f1da74039c 100644 --- a/lib/models/isar/ordinal.g.dart +++ b/lib/models/isar/ordinal.g.dart @@ -77,7 +77,7 @@ const OrdinalSchema = CollectionSchema( getId: _ordinalGetId, getLinks: _ordinalGetLinks, attach: _ordinalAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _ordinalEstimateSize( diff --git a/lib/models/isar/stack_theme.g.dart b/lib/models/isar/stack_theme.g.dart index 71f51800fa..adeb670265 100644 --- a/lib/models/isar/stack_theme.g.dart +++ b/lib/models/isar/stack_theme.g.dart @@ -857,7 +857,7 @@ const StackThemeSchema = CollectionSchema( getId: _stackThemeGetId, getLinks: _stackThemeGetLinks, attach: _stackThemeAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _stackThemeEstimateSize( diff --git a/lib/wallets/isar/models/frost_wallet_info.g.dart b/lib/wallets/isar/models/frost_wallet_info.g.dart index 459bff7e17..f2c0c9bbdb 100644 --- a/lib/wallets/isar/models/frost_wallet_info.g.dart +++ b/lib/wallets/isar/models/frost_wallet_info.g.dart @@ -66,7 +66,7 @@ const FrostWalletInfoSchema = CollectionSchema( getId: _frostWalletInfoGetId, getLinks: _frostWalletInfoGetLinks, attach: _frostWalletInfoAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _frostWalletInfoEstimateSize( diff --git a/lib/wallets/isar/models/spark_coin.g.dart b/lib/wallets/isar/models/spark_coin.g.dart index adbb522c6b..f41cab69bb 100644 --- a/lib/wallets/isar/models/spark_coin.g.dart +++ b/lib/wallets/isar/models/spark_coin.g.dart @@ -110,7 +110,7 @@ const SparkCoinSchema = CollectionSchema( getId: _sparkCoinGetId, getLinks: _sparkCoinGetLinks, attach: _sparkCoinAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _sparkCoinEstimateSize( diff --git a/lib/wallets/isar/models/token_wallet_info.g.dart b/lib/wallets/isar/models/token_wallet_info.g.dart index 7e07112243..d64cc4872e 100644 --- a/lib/wallets/isar/models/token_wallet_info.g.dart +++ b/lib/wallets/isar/models/token_wallet_info.g.dart @@ -70,7 +70,7 @@ const TokenWalletInfoSchema = CollectionSchema( getId: _tokenWalletInfoGetId, getLinks: _tokenWalletInfoGetLinks, attach: _tokenWalletInfoAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _tokenWalletInfoEstimateSize( diff --git a/lib/wallets/isar/models/wallet_info.g.dart b/lib/wallets/isar/models/wallet_info.g.dart index 1db7d04fcf..0667a4feb0 100644 --- a/lib/wallets/isar/models/wallet_info.g.dart +++ b/lib/wallets/isar/models/wallet_info.g.dart @@ -112,7 +112,7 @@ const WalletInfoSchema = CollectionSchema( getId: _walletInfoGetId, getLinks: _walletInfoGetLinks, attach: _walletInfoAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _walletInfoEstimateSize( diff --git a/lib/wallets/isar/models/wallet_info_meta.g.dart b/lib/wallets/isar/models/wallet_info_meta.g.dart index 4ec63249e3..9bf7a93c6e 100644 --- a/lib/wallets/isar/models/wallet_info_meta.g.dart +++ b/lib/wallets/isar/models/wallet_info_meta.g.dart @@ -55,7 +55,7 @@ const WalletInfoMetaSchema = CollectionSchema( getId: _walletInfoMetaGetId, getLinks: _walletInfoMetaGetLinks, attach: _walletInfoMetaAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _walletInfoMetaEstimateSize( diff --git a/lib/wallets/isar/models/wallet_solana_token_info.g.dart b/lib/wallets/isar/models/wallet_solana_token_info.g.dart index bd83700810..d4e1c7f9a8 100644 --- a/lib/wallets/isar/models/wallet_solana_token_info.g.dart +++ b/lib/wallets/isar/models/wallet_solana_token_info.g.dart @@ -71,7 +71,7 @@ const WalletSolanaTokenInfoSchema = CollectionSchema( getId: _walletSolanaTokenInfoGetId, getLinks: _walletSolanaTokenInfoGetLinks, attach: _walletSolanaTokenInfoAttach, - version: '3.3.0-dev.2', + version: '3.3.2', ); int _walletSolanaTokenInfoEstimateSize( diff --git a/pubspec.lock b/pubspec.lock index 156164fd48..d740591d87 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "0eb33edbbe99a02e73b8bbeb6f2b65972023d902117ee8d1bf0ea1a79f83aa7b" + sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d" url: "https://pub.dev" source: hosted - version: "90.0.0" + version: "93.0.0" analyzer: dependency: "direct dev" description: name: analyzer - sha256: "711e3a890bb529bf55f07d73b8706f4b7504ad77e90d2f205626b116c048583f" + sha256: "92584d8f77b0095cb0cc7041dd4baea8bb3ec1a5a5f84037c93b1cf414942633" url: "https://pub.dev" source: hosted - version: "8.3.0" + version: "10.0.0" another_flushbar: dependency: "direct main" description: @@ -163,10 +163,10 @@ packages: dependency: transitive description: name: build - sha256: ce76b1d48875e3233fde17717c23d1f60a91cc631597e49a400c89b475395b1d + sha256: "45d14a0fb23e018d8287c32fc98d726ce466b231928ed9b9200f29bd3ccd39ae" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "4.0.7" build_cli_annotations: dependency: transitive description: @@ -191,30 +191,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.1" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: d1d57f7807debd7349b4726a19fd32ec8bc177c71ad0febf91a20f84cd2d4b46 - url: "https://pub.dev" - source: hosted - version: "3.0.3" build_runner: dependency: "direct dev" description: name: build_runner - sha256: b24597fceb695969d47025c958f3837f9f0122e237c6a22cb082a5ac66c3ca30 - url: "https://pub.dev" - source: hosted - version: "2.7.1" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: "066dda7f73d8eb48ba630a55acb50c4a84a2e6b453b1cb4567f581729e794f7b" + sha256: "39ad4ca8a2876779737c60e4228b4bcd35d4352ef7e14e47514093edc012c734" url: "https://pub.dev" source: hosted - version: "9.3.1" + version: "2.11.1" built_collection: dependency: transitive description: @@ -737,10 +721,10 @@ packages: dependency: transitive description: name: dart_style - sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b + sha256: "29f7ecc274a86d32920b1d9cfc7502fa87220da41ec60b55f329559d5732e2b2" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.1.7" dartx: dependency: transitive description: @@ -1292,10 +1276,10 @@ packages: dependency: "direct dev" description: name: hive_ce_generator - sha256: a169feeff2da9cc2c417ce5ae9bcebf7c8a95d7a700492b276909016ad70a786 + sha256: c9a7cda57823ffec35846c051637ddcd105eafa253e7395d9c8c0ed5e87fbb72 url: "https://pub.dev" source: hosted - version: "1.9.3" + version: "1.11.1" hive_test: dependency: "direct dev" description: @@ -1385,26 +1369,26 @@ packages: dependency: "direct main" description: name: isar_community - sha256: eae4a7e659bec0f92fc953afb8738512062df6a2ff99fe838cb53f9ed4fa6e97 + sha256: "683fd093956eac3a660776afb70d65d50a3391b3587b40b89e3d12586b8cc47f" url: "https://pub.dev" source: hosted - version: "3.3.0-dev.2" + version: "3.3.2" isar_community_flutter_libs: dependency: "direct main" description: name: isar_community_flutter_libs - sha256: e8e6668d2c20ed61af9422bddc0bc3d1f6db91e3a5dae4406379a1426c06fbff + sha256: c44340fa38c81ef16d924202d443bbe799cde4826be9a31a9dc92ee612e1966f url: "https://pub.dev" source: hosted - version: "3.3.0-dev.2" + version: "3.3.2" isar_community_generator: dependency: "direct dev" description: name: isar_community_generator - sha256: "9da90eafaecf2ec482f50854373e37f3d9e33387830dbc0265bcdb74d9036e74" + sha256: fd5a2a5df2aac2fccba82425005bf47fd2a48f34190536d0c9fd732dc7303fb3 url: "https://pub.dev" source: hosted - version: "3.3.0-dev.2" + version: "3.3.2" isolate_channel: dependency: transitive description: @@ -1437,6 +1421,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.0" + json_serializable: + dependency: transitive + description: + name: json_serializable + sha256: e45aefa0324f08c683caafbb94b72837aa6193c61822799c916e45f4a263113d + url: "https://pub.dev" + source: hosted + version: "6.14.1" jsontool: dependency: transitive description: @@ -2091,18 +2083,18 @@ packages: dependency: transitive description: name: source_gen - sha256: "7b19d6ba131c6eb98bfcbf8d56c1a7002eba438af2e7ae6f8398b2b0f4f381e3" + sha256: a603f1fb984a7391ae5978d1b92bfaaa08b350dca5c825256f925818f7943bf5 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "4.2.4" source_helper: dependency: transitive description: name: source_helper - sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723" + sha256: "5e6f216fdf6376c9f3852381ae037499797a3385377d388b011dac98d303c67c" url: "https://pub.dev" source: hosted - version: "1.3.8" + version: "1.3.13" source_map_stack_trace: dependency: transitive description: @@ -2281,14 +2273,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.4" - timing: - dependency: transitive - description: - name: timing - sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" - url: "https://pub.dev" - source: hosted - version: "1.0.2" tint: dependency: transitive description: @@ -2616,7 +2600,7 @@ packages: dependency: "direct main" description: path: "." - ref: f67a02c6fff7c3c269f4497e077c7cf419076a60 + ref: "v0.3.0" resolved-ref: f67a02c6fff7c3c269f4497e077c7cf419076a60 url: "https://github.com/xelis-project/xelis-flutter-ffi.git" source: git @@ -2671,4 +2655,4 @@ packages: version: "0.2.4" sdks: dart: ">=3.12.0 <4.0.0" - flutter: ">=3.44.0 <4.0.0" + flutter: ">=3.44.8 <4.0.0" diff --git a/scripts/app_config/templates/pubspec.template.yaml b/scripts/app_config/templates/pubspec.template.yaml index 1ddf61ea7f..094b6ee70b 100644 --- a/scripts/app_config/templates/pubspec.template.yaml +++ b/scripts/app_config/templates/pubspec.template.yaml @@ -15,7 +15,7 @@ version: PLACEHOLDER_V+PLACEHOLDER_B environment: sdk: ">=3.12.0 <4.0.0" - flutter: ^3.44.0 + flutter: ^3.44.8 dependencies: flutter: @@ -33,11 +33,11 @@ dependencies: # xelis_dart_sdk: 0.35.1 ## git: ## url: https://github.com/xelis-project/xelis-dart-sdk.git -## ref: f1da98f8bad8b9ad3645661a23f9efb83e44b0c9 +## ref: a07489e6ed10355ad34f08445dcd9441202393c1 # xelis_flutter: # git: # url: https://github.com/xelis-project/xelis-flutter-ffi.git -# ref: f67a02c6fff7c3c269f4497e077c7cf419076a60 +# ref: v0.3.0 # %%END_ENABLE_XEL%% # %%ENABLE_FIRO%% @@ -187,8 +187,8 @@ dependencies: lottie: ^3.3.2 file_picker: ^10.3.3 connectivity_plus: ^4.0.1 - isar_community: 3.3.0-dev.2 - isar_community_flutter_libs: 3.3.0-dev.2 + isar_community: 3.3.2 + isar_community_flutter_libs: 3.3.2 dropdown_button2: ^2.1.3 string_validator: ^0.3.0 equatable: ^2.0.5 @@ -282,15 +282,15 @@ dev_dependencies: sdk: flutter build_runner: ^2.5.4 flutter_launcher_icons: ^0.13.1 - hive_ce_generator: ^1.9.3 + hive_ce_generator: 1.11.1 dependency_validator: ^5.0.2 hive_test: ^1.0.1 mockito: ^5.4.6 mockingjay: ^0.2.0 - analyzer: ^8.2.2 + analyzer: 10.0.0 import_sorter: ^4.6.0 flutter_lints: ^3.0.1 - isar_community_generator: 3.3.0-dev.2 + isar_community_generator: 3.3.2 drift_dev: ^2.28.3 flutter_native_splash: @@ -348,9 +348,6 @@ dependency_overrides: # required to override solana's lower version decimal: ^3.2.4 - # pin analyzer below 8.4.0 to avoid source_gen 3.1.0 incompatibility (getInvocation() removed in 8.4.0+) - analyzer: ">=8.2.0 <8.4.0" - # xelis override json_rpc_2: ^4.0.0 freezed: ^3.1.0 diff --git a/test/xelis_dependency_smoke_test.dart b/test/xelis_dependency_smoke_test.dart new file mode 100644 index 0000000000..b03ddc0404 --- /dev/null +++ b/test/xelis_dependency_smoke_test.dart @@ -0,0 +1,22 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:xelis_dart_sdk/xelis_dart_sdk.dart' as xelis_sdk; +import 'package:xelis_flutter/src/api/network.dart' as xelis_ffi; +import 'package:xelis_flutter/src/api/precomputed_tables.dart' as xelis_ffi; + +void main() { + test('updated Xelis dependencies expose the APIs used by Stack Wallet', () { + expect( + xelis_sdk.WalletEvent.fromStr('new_transaction'), + xelis_sdk.WalletEvent.newTransaction, + ); + expect( + xelis_sdk.WalletEvent.fromStr('new_pending_transaction'), + xelis_sdk.WalletEvent.newPendingTransaction, + ); + expect(xelis_ffi.Network.mainnet.name, 'mainnet'); + expect( + const xelis_ffi.PrecomputedTableType.l1Low(), + isA(), + ); + }); +} diff --git a/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart b/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart index df1d41ac5d..4fd280efd6 100644 --- a/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart +++ b/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart @@ -171,6 +171,8 @@ final class _LibXelisInterfaceImpl extends LibXelisInterface { ), ); case xelis_sdk.WalletEvent.newPendingTransaction: + // Stack Wallet's transaction model requires a finalized + // topoheight, so refresh this entry after it is finalized. continue; case xelis_sdk.WalletEvent.balanceChanged: final data = xelis_sdk.BalanceChangedEvent.fromJson( From 3dee754dc38858d33a7c2a5b00f1b1baa857adbd Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 5 Aug 2026 22:35:09 -0500 Subject: [PATCH 4/8] fix(xelis): preserve all transaction history variants --- .../wallet/impl/xelis_transaction_mapper.dart | 237 +++++++++++++++ lib/wallets/wallet/impl/xelis_wallet.dart | 212 +------------ .../wallet/intermediate/lib_xelis_wallet.dart | 2 +- .../interfaces/lib_xelis_interface.dart | 101 +++++- test/xelis_dependency_smoke_test.dart | 66 ++++ test/xelis_transaction_mapper_test.dart | 287 ++++++++++++++++++ ...XEL_lib_xelis_interface_impl.template.dart | 68 ++++- 7 files changed, 761 insertions(+), 212 deletions(-) create mode 100644 lib/wallets/wallet/impl/xelis_transaction_mapper.dart create mode 100644 test/xelis_transaction_mapper_test.dart diff --git a/lib/wallets/wallet/impl/xelis_transaction_mapper.dart b/lib/wallets/wallet/impl/xelis_transaction_mapper.dart new file mode 100644 index 0000000000..009f880b3b --- /dev/null +++ b/lib/wallets/wallet/impl/xelis_transaction_mapper.dart @@ -0,0 +1,237 @@ +import 'dart:convert'; + +import '../../../models/isar/models/blockchain_data/transaction.dart'; +import '../../../models/isar/models/blockchain_data/v2/input_v2.dart'; +import '../../../models/isar/models/blockchain_data/v2/output_v2.dart'; +import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; +import '../../../utilities/amount/amount.dart'; +import '../../../wl_gen/interfaces/lib_xelis_interface.dart'; + +TransactionV2 mapXelisTransactionEntry({ + required TransactionEntryWrapper transactionEntry, + required String walletId, + required String walletAddress, + required int fractionDigits, +}) { + final inputs = []; + final outputs = []; + final otherData = {}; + final entry = transactionEntry.entryType; + + InputV2 walletInput(int value) => + InputV2.isarCantDoRequiredInDefaultConstructor( + scriptSigHex: null, + scriptSigAsm: null, + sequence: null, + outpoint: null, + addresses: [walletAddress], + valueStringSats: value.toString(), + witness: null, + innerRedeemScriptAsm: null, + coinbase: null, + walletOwns: true, + ); + + OutputV2 output(int value, String address, {required bool walletOwns}) => + OutputV2.isarCantDoRequiredInDefaultConstructor( + scriptPubKeyHex: '', + valueStringSats: value.toString(), + addresses: [address], + walletOwns: walletOwns, + ); + + void setFee(int fee) { + otherData['overrideFee'] = Amount( + rawValue: BigInt.from(fee), + fractionDigits: fractionDigits, + ).toJsonString(); + } + + void addIncomingContractTransfers(Map> transfers) { + for (final contractEntry in transfers.entries) { + for (final assetEntry in contractEntry.value.entries) { + outputs.add(output(assetEntry.value, walletAddress, walletOwns: true)); + } + } + } + + final TransactionType transactionType; + int? nonce; + + switch (entry) { + case CoinbaseEntryWrapper(:final reward): + transactionType = TransactionType.incoming; + outputs.add(output(reward, walletAddress, walletOwns: true)); + otherData['xelisEntryType'] = 'coinbase'; + setFee(0); + + case BurnEntryWrapper(:final amount, :final fee, :final asset): + transactionType = TransactionType.outgoing; + nonce = entry.nonce; + inputs.add(walletInput(amount + fee)); + outputs.add(output(amount, 'burn', walletOwns: false)); + otherData['xelisEntryType'] = 'burn'; + otherData['burnAsset'] = asset; + setFee(fee); + + case IncomingEntryWrapper(:final from, :final transfers): + transactionType = from == walletAddress + ? TransactionType.sentToSelf + : TransactionType.incoming; + for (final transfer in transfers) { + outputs.add(output(transfer.amount, walletAddress, walletOwns: true)); + otherData['asset_${transfer.asset}'] = transfer.amount.toString(); + if (transfer.extraData != null) { + otherData['extraData_${transfer.asset}'] = transfer.extraData; + } + } + otherData['xelisEntryType'] = 'incoming'; + otherData['from'] = from; + setFee(0); + + case OutgoingEntryWrapper(:final fee, :final transfers): + transactionType = TransactionType.outgoing; + nonce = entry.nonce; + inputs.add(walletInput(fee)); + for (final transfer in transfers) { + inputs.add(walletInput(transfer.amount)); + outputs.add( + output(transfer.amount, transfer.destination, walletOwns: false), + ); + otherData['asset_${transfer.asset}_amount'] = transfer.amount + .toString(); + if (transfer.extraData != null) { + otherData['extraData_${transfer.asset}'] = transfer.extraData; + } + } + otherData['xelisEntryType'] = 'outgoing'; + setFee(fee); + + case MultisigEntryWrapper( + :final participants, + :final threshold, + :final fee, + ): + transactionType = TransactionType.outgoing; + nonce = entry.nonce; + inputs.add(walletInput(fee)); + for (final participant in participants) { + outputs.add(output(0, participant, walletOwns: false)); + } + otherData['xelisEntryType'] = 'multisig'; + otherData['participants'] = participants; + otherData['threshold'] = threshold; + setFee(fee); + + case InvokeContractEntryWrapper( + :final contract, + :final deposits, + :final received, + :final chunkId, + :final fee, + :final maxGas, + ): + transactionType = TransactionType.outgoing; + nonce = entry.nonce; + inputs.add(walletInput(fee)); + for (final deposit in deposits.entries) { + inputs.add(walletInput(deposit.value)); + outputs.add(output(deposit.value, contract, walletOwns: false)); + } + addIncomingContractTransfers(received); + otherData['xelisEntryType'] = 'invokeContract'; + otherData['contract'] = contract; + otherData['deposits'] = deposits; + otherData['received'] = received; + otherData['chunkId'] = chunkId; + otherData['maxGas'] = maxGas; + setFee(fee); + + case DeployContractEntryWrapper(:final fee, :final invoke): + transactionType = TransactionType.outgoing; + nonce = entry.nonce; + inputs.add(walletInput(fee)); + if (invoke != null) { + for (final deposit in invoke.deposits.entries) { + inputs.add(walletInput(deposit.value)); + outputs.add( + output(deposit.value, 'contract-deployment', walletOwns: false), + ); + } + } + otherData['xelisEntryType'] = 'deployContract'; + if (invoke != null) { + otherData['invoke'] = { + 'maxGas': invoke.maxGas, + 'deposits': invoke.deposits, + }; + } + setFee(fee); + + case IncomingContractEntryWrapper(:final transfers): + transactionType = TransactionType.incoming; + addIncomingContractTransfers(transfers); + otherData['xelisEntryType'] = 'incomingContract'; + otherData['transfers'] = transfers; + setFee(0); + + case OutgoingBlobEntryWrapper(:final destinations, :final fee, :final data): + transactionType = TransactionType.outgoing; + nonce = entry.nonce; + inputs.add(walletInput(fee)); + for (final destination in destinations) { + outputs.add(output(0, destination, walletOwns: false)); + } + otherData['xelisEntryType'] = 'outgoingBlob'; + otherData['destinations'] = destinations; + otherData['data'] = data; + setFee(fee); + + case IncomingBlobEntryWrapper( + :final from, + :final destinations, + :final data, + ): + transactionType = from == walletAddress + ? TransactionType.sentToSelf + : TransactionType.incoming; + for (final destination in destinations) { + outputs.add( + output(0, destination, walletOwns: destination == walletAddress), + ); + } + otherData['xelisEntryType'] = 'incomingBlob'; + otherData['from'] = from; + otherData['destinations'] = destinations; + otherData['data'] = data; + setFee(0); + + case UnknownEntryWrapper(:final entryType, :final data): + transactionType = TransactionType.unknown; + otherData['xelisEntryType'] = entryType; + if (data != null) { + otherData['data'] = data; + } + setFee(0); + } + + if (nonce != null) { + otherData['nonce'] = nonce; + } + + return TransactionV2( + walletId: walletId, + blockHash: '', + hash: transactionEntry.hash, + txid: transactionEntry.hash, + timestamp: + (transactionEntry.timestamp?.millisecondsSinceEpoch ?? 0) ~/ 1000, + height: transactionEntry.topoheight, + inputs: List.unmodifiable(inputs), + outputs: List.unmodifiable(outputs), + version: -1, + type: transactionType, + subType: TransactionSubType.none, + otherData: jsonEncode(otherData), + ); +} diff --git a/lib/wallets/wallet/impl/xelis_wallet.dart b/lib/wallets/wallet/impl/xelis_wallet.dart index 66b551bb8e..20f42591d8 100644 --- a/lib/wallets/wallet/impl/xelis_wallet.dart +++ b/lib/wallets/wallet/impl/xelis_wallet.dart @@ -8,9 +8,6 @@ import 'package:stack_wallet_backup/generate_password.dart'; import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; -import '../../../models/isar/models/blockchain_data/transaction.dart'; -import '../../../models/isar/models/blockchain_data/v2/input_v2.dart'; -import '../../../models/isar/models/blockchain_data/v2/output_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; import '../../../models/paymint/fee_object_model.dart'; import '../../../services/event_bus/events/global/wallet_sync_status_changed_event.dart'; @@ -23,6 +20,10 @@ import '../../crypto_currency/crypto_currency.dart'; import '../../models/tx_data.dart'; import '../intermediate/lib_xelis_wallet.dart'; import '../wallet.dart'; +import 'xelis_transaction_mapper.dart'; + +const _defaultFeeEstimateAddress = + 'xel:xz9574c80c4xegnvurazpmxhw5dlg2n0g9qm60uwgt75uqyx3pcsqzzra9m'; class XelisWallet extends LibXelisWallet { Completer? _initCompleter; @@ -57,7 +58,7 @@ class XelisWallet extends LibXelisWallet { seed: mnemonic, network: cryptoCurrency.network, precomputedTablesPath: tablePath, - stack_l1Low: tableState.currentSize.isLow, + stackL1Low: tableState.currentSize.isLow, ); await secureStorageInterface.write( @@ -91,7 +92,7 @@ class XelisWallet extends LibXelisWallet { password: password, network: cryptoCurrency.network, precomputedTablesPath: tablePath, - stack_l1Low: tableState.currentSize.isLow, + stackL1Low: tableState.currentSize.isLow, ); final mnemonic = await libXelis.getSeed(wallet); @@ -123,7 +124,7 @@ class XelisWallet extends LibXelisWallet { password: password!, network: cryptoCurrency.network, precomputedTablesPath: tablePath, - stack_l1Low: tableState.currentSize.isLow, + stackL1Low: tableState.currentSize.isLow, ); await _finishInit(); @@ -381,191 +382,11 @@ class XelisWallet extends LibXelisWallet { continue; // Skip already processed transactions } - final List outputs = []; - final List inputs = []; - TransactionType? txType; - const TransactionSubType txSubType = TransactionSubType.none; - int? nonce; - Amount fee = Amount( - rawValue: BigInt.zero, - fractionDigits: cryptoCurrency.fractionDigits, - ); - final Map otherData = {}; - - final entryType = transactionEntry.entryType; - - if (entryType is CoinbaseEntryWrapper) { - final coinbase = entryType; - txType = TransactionType.incoming; - - final int decimals = await libXelis.getAssetDecimals( - wallet!, - asset: libXelis.xelisAsset, - ); - - fee = Amount( - rawValue: BigInt.zero, - fractionDigits: cryptoCurrency.fractionDigits, - ); - - outputs.add( - OutputV2.isarCantDoRequiredInDefaultConstructor( - scriptPubKeyHex: "", - valueStringSats: coinbase.reward.toString(), - addresses: [thisAddress], - walletOwns: true, - ), - ); - otherData['overrideFee'] = fee.toJsonString(); - } else if (entryType is BurnEntryWrapper) { - final burn = entryType; - txType = TransactionType.outgoing; - - final int decimals = await libXelis.getAssetDecimals( - wallet!, - asset: burn.asset, - ); - - fee = Amount( - rawValue: BigInt.from(burn.fee), - fractionDigits: cryptoCurrency.fractionDigits, - ); - - inputs.add( - InputV2.isarCantDoRequiredInDefaultConstructor( - scriptSigAsm: null, - scriptSigHex: null, - sequence: null, - outpoint: null, - valueStringSats: burn.amount.toString(), - addresses: [thisAddress], - witness: null, - innerRedeemScriptAsm: null, - coinbase: null, - walletOwns: true, - ), - ); - - outputs.add( - OutputV2.isarCantDoRequiredInDefaultConstructor( - scriptPubKeyHex: "", - valueStringSats: burn.amount.toString(), - addresses: ['burn'], - walletOwns: false, - ), - ); - - otherData['burnAsset'] = burn.asset; - } else if (entryType is IncomingEntryWrapper) { - final incoming = entryType; - txType = incoming.from == thisAddress - ? TransactionType.sentToSelf - : TransactionType.incoming; - - for (final transfer in incoming.transfers) { - final int decimals = await libXelis.getAssetDecimals( - wallet!, - asset: transfer.asset, - ); - - fee = Amount( - rawValue: BigInt.zero, - fractionDigits: cryptoCurrency.fractionDigits, - ); - - outputs.add( - OutputV2.isarCantDoRequiredInDefaultConstructor( - scriptPubKeyHex: "", - valueStringSats: transfer.amount.toString(), - addresses: [thisAddress], - walletOwns: true, - ), - ); - - otherData['asset_${transfer.asset}'] = transfer.amount.toString(); - if (transfer.extraData != null) { - otherData['extraData_${transfer.asset}'] = transfer.extraData!; - } - otherData['overrideFee'] = fee.toJsonString(); - } - } else if (entryType is OutgoingEntryWrapper) { - final outgoing = entryType; - txType = TransactionType.outgoing; - nonce = outgoing.nonce; - - fee = Amount( - rawValue: BigInt.from(outgoing.fee), - fractionDigits: cryptoCurrency.fractionDigits, - ); - - inputs.add( - InputV2.isarCantDoRequiredInDefaultConstructor( - scriptSigHex: null, - scriptSigAsm: null, - sequence: null, - outpoint: null, - addresses: [thisAddress], - valueStringSats: (outgoing.fee).toString(), - witness: null, - innerRedeemScriptAsm: null, - coinbase: null, - walletOwns: true, - ), - ); - - for (final transfer in outgoing.transfers) { - inputs.add( - InputV2.isarCantDoRequiredInDefaultConstructor( - scriptSigHex: null, - scriptSigAsm: null, - sequence: null, - outpoint: null, - addresses: [thisAddress], - valueStringSats: (transfer.amount).toString(), - witness: null, - innerRedeemScriptAsm: null, - coinbase: null, - walletOwns: true, - ), - ); - - outputs.add( - OutputV2.isarCantDoRequiredInDefaultConstructor( - scriptPubKeyHex: "", - valueStringSats: transfer.amount.toString(), - addresses: [transfer.destination], - walletOwns: false, - ), - ); - - otherData['asset_${transfer.asset}_amount'] = transfer.amount - .toString(); - if (transfer.extraData != null) { - otherData['extraData_${transfer.asset}'] = transfer.extraData!; - } - } - } else { - // Skip unknown entry types - continue; - } - - final txn = TransactionV2( + final txn = mapXelisTransactionEntry( + transactionEntry: transactionEntry, walletId: walletId, - blockHash: "", // Not provided in Xelis data - hash: transactionEntry.hash, - txid: transactionEntry.hash, - timestamp: - (transactionEntry.timestamp?.millisecondsSinceEpoch ?? 0) ~/ 1000, - height: transactionEntry.topoheight, - inputs: List.unmodifiable(inputs), - outputs: List.unmodifiable(outputs), - version: -1, // Version not provided - type: txType, - subType: txSubType, - otherData: jsonEncode({ - ...otherData, - if (nonce != null) 'nonce': nonce, - }), + walletAddress: thisAddress, + fractionDigits: cryptoCurrency.fractionDigits, ); // Logging.instance.log( @@ -728,13 +549,13 @@ class XelisWallet extends LibXelisWallet { final defaultDecimals = cryptoCurrency.fractionDigits; final defaultFee = BigInt.from(0); - // Use default address if recipients list is empty to ensure basic fee estimates are readily available + // Use a default address when no recipient is available so basic fee + // estimates can still be calculated. final effectiveRecipients = recipients.isNotEmpty ? recipients : [ TxRecipient( - address: - 'xel:xz9574c80c4xegnvurazpmxhw5dlg2n0g9qm60uwgt75uqyx3pcsqzzra9m', + address: _defaultFeeEstimateAddress, amount: amount, isChange: false, addressType: AddressType.xelis, @@ -905,10 +726,7 @@ class XelisWallet extends LibXelisWallet { Future handleNewTransaction(TransactionEntryWrapper tx) async { try { final txList = [tx]; - final newTxIds = await updateTransactions( - isRescan: false, - objTransactions: txList, - ); + await updateTransactions(isRescan: false, objTransactions: txList); await updateBalance(); diff --git a/lib/wallets/wallet/intermediate/lib_xelis_wallet.dart b/lib/wallets/wallet/intermediate/lib_xelis_wallet.dart index eb818021a6..55a840b75e 100644 --- a/lib/wallets/wallet/intermediate/lib_xelis_wallet.dart +++ b/lib/wallets/wallet/intermediate/lib_xelis_wallet.dart @@ -229,7 +229,7 @@ extension XelisTableManagement on LibXelisWallet { final tablePath = await getPrecomputedTablesPath(); await libXelis.updateTables( precomputedTablesPath: tablePath, - stack_l1Low: state.desiredSize.isLow, + stackL1Low: state.desiredSize.isLow, ); await setTableState( diff --git a/lib/wl_gen/interfaces/lib_xelis_interface.dart b/lib/wl_gen/interfaces/lib_xelis_interface.dart index 4674347e3b..477c62da68 100644 --- a/lib/wl_gen/interfaces/lib_xelis_interface.dart +++ b/lib/wl_gen/interfaces/lib_xelis_interface.dart @@ -36,7 +36,7 @@ abstract class LibXelisInterface { Future updateTables({ required String precomputedTablesPath, - required bool stack_l1Low, + required bool stackL1Low, }); Future getSeed(OpaqueXelisWallet wallet); @@ -50,7 +50,7 @@ abstract class LibXelisInterface { String? seed, String? privateKey, String? precomputedTablesPath, - bool? stack_l1Low, + bool? stackL1Low, }); Future openXelisWallet( @@ -60,7 +60,7 @@ abstract class LibXelisInterface { required String password, required CryptoCurrencyNetwork network, String? precomputedTablesPath, - bool? stack_l1Low, + bool? stackL1Low, }); String getAddress(OpaqueXelisWallet wallet); @@ -183,11 +183,13 @@ class BurnEntryWrapper extends EntryWrapper { final int amount; final int fee; final String asset; + final int nonce; const BurnEntryWrapper({ required this.amount, required this.fee, required this.asset, + required this.nonce, }); } @@ -219,7 +221,97 @@ class OutgoingEntryWrapper extends EntryWrapper { }); } -class UnknownEntryWrapper extends EntryWrapper {} +class MultisigEntryWrapper extends EntryWrapper { + final List participants; + final int threshold; + final int fee; + final int nonce; + + const MultisigEntryWrapper({ + required this.participants, + required this.threshold, + required this.fee, + required this.nonce, + }); +} + +class InvokeContractEntryWrapper extends EntryWrapper { + final String contract; + final Map deposits; + final Map> received; + final int chunkId; + final int fee; + final int maxGas; + final int nonce; + + const InvokeContractEntryWrapper({ + required this.contract, + required this.deposits, + required this.received, + required this.chunkId, + required this.fee, + required this.maxGas, + required this.nonce, + }); +} + +class DeployInvokeWrapper { + final int maxGas; + final Map deposits; + + const DeployInvokeWrapper({required this.maxGas, required this.deposits}); +} + +class DeployContractEntryWrapper extends EntryWrapper { + final int fee; + final int nonce; + final DeployInvokeWrapper? invoke; + + const DeployContractEntryWrapper({ + required this.fee, + required this.nonce, + required this.invoke, + }); +} + +class IncomingContractEntryWrapper extends EntryWrapper { + final Map> transfers; + + const IncomingContractEntryWrapper({required this.transfers}); +} + +class OutgoingBlobEntryWrapper extends EntryWrapper { + final List destinations; + final int fee; + final int nonce; + final Map data; + + const OutgoingBlobEntryWrapper({ + required this.destinations, + required this.fee, + required this.nonce, + required this.data, + }); +} + +class IncomingBlobEntryWrapper extends EntryWrapper { + final String from; + final List destinations; + final Map data; + + const IncomingBlobEntryWrapper({ + required this.from, + required this.destinations, + required this.data, + }); +} + +class UnknownEntryWrapper extends EntryWrapper { + final String entryType; + final Map? data; + + const UnknownEntryWrapper({required this.entryType, this.data}); +} // ============================================================================= @@ -257,6 +349,7 @@ class XelisTableState { this.isGenerating = false, this.currentSize = XelisTableSize.low, XelisTableSize desiredSize = XelisTableSize.full, + // ignore: prefer_initializing_formals }) : _desiredSize = desiredSize; XelisTableState copyWith({ diff --git a/test/xelis_dependency_smoke_test.dart b/test/xelis_dependency_smoke_test.dart index b03ddc0404..036aeab401 100644 --- a/test/xelis_dependency_smoke_test.dart +++ b/test/xelis_dependency_smoke_test.dart @@ -1,4 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; +import 'package:stackwallet/wl_gen/interfaces/lib_xelis_interface.dart'; import 'package:xelis_dart_sdk/xelis_dart_sdk.dart' as xelis_sdk; import 'package:xelis_flutter/src/api/network.dart' as xelis_ffi; import 'package:xelis_flutter/src/api/precomputed_tables.dart' as xelis_ffi; @@ -19,4 +20,69 @@ void main() { isA(), ); }); + + test('all Xelis SDK transaction variants have Stack Wallet wrappers', () { + const extraData = xelis_sdk.ExtraData( + data: 'payload', + flag: xelis_sdk.Flag.public, + ); + final entries = [ + const xelis_sdk.TransactionEntryType.coinbase(reward: 1), + const xelis_sdk.TransactionEntryType.burn( + asset: 'asset', + amount: 2, + fee: 3, + nonce: 4, + ), + const xelis_sdk.TransactionEntryType.incoming( + from: 'from', + transfers: [], + ), + const xelis_sdk.TransactionEntryType.outgoing( + fee: 1, + nonce: 2, + transfers: [], + ), + const xelis_sdk.TransactionEntryType.multisig( + participants: ['participant'], + threshold: 1, + fee: 2, + nonce: 3, + ), + const xelis_sdk.TransactionEntryType.invokeContract( + contract: 'contract', + deposits: {'asset': 1}, + received: {}, + chunkId: 2, + fee: 3, + maxGas: 4, + nonce: 5, + ), + const xelis_sdk.TransactionEntryType.deployContract(fee: 1, nonce: 2), + const xelis_sdk.TransactionEntryType.incomingContract( + transfers: { + 'contract': {'asset': 1}, + }, + ), + const xelis_sdk.TransactionEntryType.outgoingBlob( + destinations: ['destination'], + fee: 1, + nonce: 2, + data: extraData, + ), + const xelis_sdk.TransactionEntryType.incomingBlob( + from: 'from', + destinations: ['destination'], + data: extraData, + ), + ]; + + final wrappers = entries.map(xelisEntryTypeToWrapper).toList(); + + expect(wrappers, hasLength(entries.length)); + expect(wrappers.whereType(), isEmpty); + expect(wrappers[7], isA()); + expect(wrappers[8], isA()); + expect(wrappers[9], isA()); + }); } diff --git a/test/xelis_transaction_mapper_test.dart b/test/xelis_transaction_mapper_test.dart new file mode 100644 index 0000000000..42a892b603 --- /dev/null +++ b/test/xelis_transaction_mapper_test.dart @@ -0,0 +1,287 @@ +import 'dart:convert'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart'; +import 'package:stackwallet/wallets/wallet/impl/xelis_transaction_mapper.dart'; +import 'package:stackwallet/wl_gen/interfaces/lib_xelis_interface.dart'; + +void main() { + const walletAddress = 'xel:wallet'; + + TransactionEntryWrapper transaction(EntryWrapper entry) => + TransactionEntryWrapper( + entry, + entryType: entry, + hash: 'hash-${entry.runtimeType}', + timestamp: DateTime.fromMillisecondsSinceEpoch(1234000), + topoheight: 42, + ); + + test('maps coinbase rewards', () { + final result = mapXelisTransactionEntry( + transactionEntry: transaction(const CoinbaseEntryWrapper(reward: 50)), + walletId: 'wallet-id', + walletAddress: walletAddress, + fractionDigits: 8, + ); + + expect(result.type, TransactionType.incoming); + expect(result.outputs.single.valueStringSats, '50'); + expect(result.outputs.single.walletOwns, isTrue); + expect(result.getFee(fractionDigits: 8).raw, BigInt.zero); + }); + + test('maps incoming transfers and sent-to-self history', () { + final result = mapXelisTransactionEntry( + transactionEntry: transaction( + const IncomingEntryWrapper( + from: walletAddress, + transfers: [ + ( + amount: 15, + asset: 'asset-a', + extraData: {'flag': 'public', 'data': 'memo'}, + ), + ], + ), + ), + walletId: 'wallet-id', + walletAddress: walletAddress, + fractionDigits: 8, + ); + + final otherData = jsonDecode(result.otherData!) as Map; + expect(result.type, TransactionType.sentToSelf); + expect(result.outputs.single.valueStringSats, '15'); + expect(otherData['asset_asset-a'], '15'); + expect(otherData['extraData_asset-a']['data'], 'memo'); + }); + + test('maps outgoing transfers, fee, and nonce', () { + final result = mapXelisTransactionEntry( + transactionEntry: transaction( + const OutgoingEntryWrapper( + nonce: 3, + fee: 2, + transfers: [ + ( + destination: 'xel:recipient', + amount: 40, + asset: 'asset-a', + extraData: null, + ), + ], + ), + ), + walletId: 'wallet-id', + walletAddress: walletAddress, + fractionDigits: 8, + ); + + expect(result.type, TransactionType.outgoing); + expect(result.getFee(fractionDigits: 8).raw, BigInt.from(2)); + expect(result.nonce, 3); + expect( + result + .getAmountSentFromThisWallet(fractionDigits: 8, subtractFee: true) + .raw, + BigInt.from(40), + ); + }); + + test('maps incoming contract transfers into wallet-owned outputs', () { + final result = mapXelisTransactionEntry( + transactionEntry: transaction( + const IncomingContractEntryWrapper( + transfers: { + 'contract-a': {'asset-a': 12, 'asset-b': 34}, + 'contract-b': {'asset-a': 56}, + }, + ), + ), + walletId: 'wallet-id', + walletAddress: walletAddress, + fractionDigits: 8, + ); + + expect(result.type, TransactionType.incoming); + expect(result.outputs.map((e) => e.valueStringSats), ['12', '34', '56']); + expect(result.outputs.every((e) => e.walletOwns), isTrue); + expect(jsonDecode(result.otherData!)['xelisEntryType'], 'incomingContract'); + }); + + test('maps outgoing blob fee and participants without dropping history', () { + final result = mapXelisTransactionEntry( + transactionEntry: transaction( + const OutgoingBlobEntryWrapper( + destinations: ['xel:one', 'xel:two'], + fee: 17, + nonce: 9, + data: {'flag': 'public', 'data': 'hello'}, + ), + ), + walletId: 'wallet-id', + walletAddress: walletAddress, + fractionDigits: 8, + ); + + expect(result.type, TransactionType.outgoing); + expect(result.getFee(fractionDigits: 8).raw, BigInt.from(17)); + expect(result.outputs.expand((e) => e.addresses), ['xel:one', 'xel:two']); + expect(result.nonce, 9); + }); + + test('maps incoming blob as a zero-value incoming transaction', () { + final result = mapXelisTransactionEntry( + transactionEntry: transaction( + const IncomingBlobEntryWrapper( + from: 'xel:sender', + destinations: [walletAddress], + data: {'flag': 'private', 'data': 'payload'}, + ), + ), + walletId: 'wallet-id', + walletAddress: walletAddress, + fractionDigits: 8, + ); + + expect(result.type, TransactionType.incoming); + expect(result.outputs.single.valueStringSats, '0'); + expect(result.outputs.single.walletOwns, isTrue); + }); + + test('maps multisig metadata and fee', () { + final result = mapXelisTransactionEntry( + transactionEntry: transaction( + const MultisigEntryWrapper( + participants: ['xel:one', 'xel:two'], + threshold: 2, + fee: 5, + nonce: 6, + ), + ), + walletId: 'wallet-id', + walletAddress: walletAddress, + fractionDigits: 8, + ); + + final otherData = jsonDecode(result.otherData!) as Map; + expect(result.type, TransactionType.outgoing); + expect(result.getFee(fractionDigits: 8).raw, BigInt.from(5)); + expect(result.nonce, 6); + expect(otherData['threshold'], 2); + expect(result.outputs, hasLength(2)); + }); + + test('maps contract deposits and received transfers', () { + final result = mapXelisTransactionEntry( + transactionEntry: transaction( + const InvokeContractEntryWrapper( + contract: 'contract-a', + deposits: {'asset-a': 20}, + received: { + 'contract-a': {'asset-b': 30}, + }, + chunkId: 7, + fee: 3, + maxGas: 50, + nonce: 8, + ), + ), + walletId: 'wallet-id', + walletAddress: walletAddress, + fractionDigits: 8, + ); + + final otherData = jsonDecode(result.otherData!) as Map; + expect(result.type, TransactionType.outgoing); + expect(result.getFee(fractionDigits: 8).raw, BigInt.from(3)); + expect(result.inputs.map((e) => e.valueStringSats), ['3', '20']); + expect(result.outputs.map((e) => e.valueStringSats), ['20', '30']); + expect(result.outputs.map((e) => e.walletOwns), [false, true]); + expect(otherData['chunkId'], 7); + expect(otherData['maxGas'], 50); + }); + + test('maps deploy invocation deposits', () { + final result = mapXelisTransactionEntry( + transactionEntry: transaction( + const DeployContractEntryWrapper( + fee: 4, + nonce: 10, + invoke: DeployInvokeWrapper(maxGas: 100, deposits: {'asset-a': 25}), + ), + ), + walletId: 'wallet-id', + walletAddress: walletAddress, + fractionDigits: 8, + ); + + expect(result.type, TransactionType.outgoing); + expect(result.getFee(fractionDigits: 8).raw, BigInt.from(4)); + expect(result.inputs.map((e) => e.valueStringSats), ['4', '25']); + expect(result.outputs.single.addresses, ['contract-deployment']); + expect(result.nonce, 10); + }); + + test('maps deploys without an invocation', () { + final result = mapXelisTransactionEntry( + transactionEntry: transaction( + const DeployContractEntryWrapper(fee: 4, nonce: 10, invoke: null), + ), + walletId: 'wallet-id', + walletAddress: walletAddress, + fractionDigits: 8, + ); + + expect(result.type, TransactionType.outgoing); + expect(result.inputs.single.valueStringSats, '4'); + expect(result.outputs, isEmpty); + expect(result.nonce, 10); + }); + + test('preserves future unknown entries instead of dropping them', () { + final result = mapXelisTransactionEntry( + transactionEntry: transaction( + const UnknownEntryWrapper( + entryType: 'futureEntry', + data: {'value': 123}, + ), + ), + walletId: 'wallet-id', + walletAddress: walletAddress, + fractionDigits: 8, + ); + + expect(result.type, TransactionType.unknown); + final otherData = jsonDecode(result.otherData!) as Map; + expect(otherData['xelisEntryType'], 'futureEntry'); + expect(otherData['data'], {'value': 123}); + expect(otherData['overrideFee'], isA()); + }); + + test('accounts for both burn amount and fee', () { + final result = mapXelisTransactionEntry( + transactionEntry: transaction( + const BurnEntryWrapper( + amount: 100, + fee: 7, + asset: 'asset-a', + nonce: 11, + ), + ), + walletId: 'wallet-id', + walletAddress: walletAddress, + fractionDigits: 8, + ); + + expect(result.getFee(fractionDigits: 8).raw, BigInt.from(7)); + expect(result.nonce, 11); + expect( + result + .getAmountSentFromThisWallet(fractionDigits: 8, subtractFee: true) + .raw, + BigInt.from(100), + ); + }); +} diff --git a/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart b/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart index 4fd280efd6..6504dfe0f2 100644 --- a/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart +++ b/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart @@ -164,7 +164,7 @@ final class _LibXelisInterfaceImpl extends LibXelisInterface { yield NewTransaction( TransactionEntryWrapper( tx, - entryType: _entryTypeConversion(tx.txEntryType), + entryType: xelisEntryTypeToWrapper(tx.txEntryType), hash: tx.hash, timestamp: tx.timestamp, topoheight: tx.topoheight, @@ -221,12 +221,12 @@ final class _LibXelisInterfaceImpl extends LibXelisInterface { @override Future updateTables({ required String precomputedTablesPath, - required bool stack_l1Low, + required bool stackL1Low, }) async { // TODO: add more granular table size management interface // for now, just patching the old system into the new FFI API - x_tables.PrecomputedTableType tableType = stack_l1Low + x_tables.PrecomputedTableType tableType = stackL1Low ? x_tables.PrecomputedTableType.l1Low() : x_tables.PrecomputedTableType.l1Full(); @@ -249,12 +249,12 @@ final class _LibXelisInterfaceImpl extends LibXelisInterface { String? seed, String? privateKey, String? precomputedTablesPath, - bool? stack_l1Low, + bool? stackL1Low, }) async { // TODO: add more granular table size management interface // for now, just patching the old system into the new FFI API - x_tables.PrecomputedTableType tableType = stack_l1Low ?? false + x_tables.PrecomputedTableType tableType = stackL1Low ?? false ? x_tables.PrecomputedTableType.l1Low() : x_tables.PrecomputedTableType.l1Full(); @@ -280,12 +280,12 @@ final class _LibXelisInterfaceImpl extends LibXelisInterface { required String password, required CryptoCurrencyNetwork network, String? precomputedTablesPath, - bool? stack_l1Low, + bool? stackL1Low, }) async { // TODO: add more granular table size management interface // for now, just patching the old system into the new FFI API - x_tables.PrecomputedTableType tableType = (stack_l1Low ?? false) + x_tables.PrecomputedTableType tableType = (stackL1Low ?? false) ? x_tables.PrecomputedTableType.l1Low() : x_tables.PrecomputedTableType.l1Full(); @@ -322,7 +322,7 @@ final class _LibXelisInterfaceImpl extends LibXelisInterface { final tx = _checkDecodeJsonStringTxEntry(e); return TransactionEntryWrapper( tx, - entryType: _entryTypeConversion(tx.txEntryType), + entryType: xelisEntryTypeToWrapper(tx.txEntryType), hash: tx.hash, timestamp: tx.timestamp, topoheight: tx.topoheight, @@ -446,7 +446,7 @@ extension _CryptoCurrencyNetworkConversion on x_network.Network { } } -EntryWrapper _entryTypeConversion(xelis_sdk.TransactionEntryType entryType) { +EntryWrapper xelisEntryTypeToWrapper(xelis_sdk.TransactionEntryType entryType) { if (entryType is xelis_sdk.CoinbaseEntry) { return CoinbaseEntryWrapper(reward: entryType.reward); } else if (entryType is xelis_sdk.BurnEntry) { @@ -454,6 +454,7 @@ EntryWrapper _entryTypeConversion(xelis_sdk.TransactionEntryType entryType) { amount: entryType.amount, fee: entryType.fee, asset: entryType.asset, + nonce: entryType.nonce, ); } else if (entryType is xelis_sdk.IncomingEntry) { return IncomingEntryWrapper( @@ -483,8 +484,55 @@ EntryWrapper _entryTypeConversion(xelis_sdk.TransactionEntryType entryType) { ) .toList(), ); + } else if (entryType is xelis_sdk.MultisigEntry) { + return MultisigEntryWrapper( + participants: entryType.participants, + threshold: entryType.threshold, + fee: entryType.fee, + nonce: entryType.nonce, + ); + } else if (entryType is xelis_sdk.InvokeContractEntry) { + return InvokeContractEntryWrapper( + contract: entryType.contract, + deposits: entryType.deposits, + received: entryType.received, + chunkId: entryType.chunkId, + fee: entryType.fee, + maxGas: entryType.maxGas, + nonce: entryType.nonce, + ); + } else if (entryType is xelis_sdk.DeployContractEntry) { + final invoke = entryType.invoke; + return DeployContractEntryWrapper( + fee: entryType.fee, + nonce: entryType.nonce, + invoke: invoke == null + ? null + : DeployInvokeWrapper( + maxGas: invoke.maxGas, + deposits: invoke.deposits, + ), + ); + } else if (entryType is xelis_sdk.IncomingContractEntry) { + return IncomingContractEntryWrapper(transfers: entryType.transfers); + } else if (entryType is xelis_sdk.OutgoingBlobEntry) { + return OutgoingBlobEntryWrapper( + destinations: entryType.destinations, + fee: entryType.fee, + nonce: entryType.nonce, + data: entryType.data.toJson(), + ); + } else if (entryType is xelis_sdk.IncomingBlobEntry) { + return IncomingBlobEntryWrapper( + from: entryType.from, + destinations: entryType.destinations, + data: entryType.data.toJson(), + ); } else { - return UnknownEntryWrapper(); + return UnknownEntryWrapper( + entryType: entryType.runtimeType.toString(), + data: entryType.toJson(), + ); } } From 0a361e03183eba835864a07e783d0ab9d1a07b95 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 5 Aug 2026 22:35:56 -0500 Subject: [PATCH 5/8] test(isar): verify 3.3.0-dev.2 database migration --- test/fixtures/isar_3_3_0_dev_2/README.md | 22 + .../isar_3_3_0_dev_2_log.isar.gz | Bin 0 -> 1801 bytes .../isar_3_3_0_dev_2_wallet_data.isar.gz | Bin 0 -> 2523 bytes test/isar_community_migration_test.dart | 199 +++++++ .../.gitignore | 1 + .../README.md | 22 + .../bin/generate.dart | 109 ++++ .../lib/log.dart | 18 + .../lib/transaction_v2.dart | 123 +++++ .../pubspec.lock | 509 ++++++++++++++++++ .../pubspec.yaml | 12 + 11 files changed, 1015 insertions(+) create mode 100644 test/fixtures/isar_3_3_0_dev_2/README.md create mode 100644 test/fixtures/isar_3_3_0_dev_2/isar_3_3_0_dev_2_log.isar.gz create mode 100644 test/fixtures/isar_3_3_0_dev_2/isar_3_3_0_dev_2_wallet_data.isar.gz create mode 100644 test/isar_community_migration_test.dart create mode 100644 tool/isar_migration_fixture_generator/.gitignore create mode 100644 tool/isar_migration_fixture_generator/README.md create mode 100644 tool/isar_migration_fixture_generator/bin/generate.dart create mode 100644 tool/isar_migration_fixture_generator/lib/log.dart create mode 100644 tool/isar_migration_fixture_generator/lib/transaction_v2.dart create mode 100644 tool/isar_migration_fixture_generator/pubspec.lock create mode 100644 tool/isar_migration_fixture_generator/pubspec.yaml diff --git a/test/fixtures/isar_3_3_0_dev_2/README.md b/test/fixtures/isar_3_3_0_dev_2/README.md new file mode 100644 index 0000000000..b756ef91c0 --- /dev/null +++ b/test/fixtures/isar_3_3_0_dev_2/README.md @@ -0,0 +1,22 @@ +# Isar 3.3.0-dev.2 migration fixture + +The fixtures contain databases created by `isar_community` 3.3.0-dev.2 using +schemas that match Stack Wallet 2.3.4: + +- `isar_3_3_0_dev_2_wallet_data.isar.gz` contains a `TransactionV2` with an + input, outpoint, and output. Its collection schema ID is + `-4280912949179256257`, matching Stack Wallet's current wallet database. +- `isar_3_3_0_dev_2_log.isar.gz` contains one legacy Stack Wallet `Log` record. + Its collection schema ID is `7425915233166922082`. + +The committed fixture is gzip-compressed without a filename or timestamp. Its +SHA-256 digest is: + +```text +c7bd6db8215a61d888c78c42a18b10541274d340c3c5377a5424bdcb595ad126 isar_3_3_0_dev_2_log.isar.gz +6a0ce2f29c58875985d32253acb54469632fd55111812c39e1a41a87cf93f22a isar_3_3_0_dev_2_wallet_data.isar.gz +``` + +Use `tool/isar_migration_fixture_generator` to regenerate it. The migration +tests open temporary copies using Isar 3.3.2, verify the databases and indexed +records, write current records, close the databases, and reopen them. diff --git a/test/fixtures/isar_3_3_0_dev_2/isar_3_3_0_dev_2_log.isar.gz b/test/fixtures/isar_3_3_0_dev_2/isar_3_3_0_dev_2_log.isar.gz new file mode 100644 index 0000000000000000000000000000000000000000..937cdfc90760992c356e6d730b0d6023b099d2ad GIT binary patch literal 1801 zcmb2|=3oE=;kS3=v!hdG*gvTI-cHFA7e2_{8px-?v9;&zDWh3NqB1T5&*WXUO)^b? zm{1fndCQs;8y;xSn9eM?=fkCF{UhH4Qq7xw@Ey@!Kec$$Hq6`ZrTDu>F+Pwh^7)+=%S8CJe@$IE z@u%e1r^;WC|1Mv$B3`0)-QO=)=kNc&>)Yq}*ZFs|>-K;A#r}Rz^sk4%?DhYDJ7)gA z_WN;fbH5$75RbsgIZcWG_M1%nA3tNp+;jiK)js|2UOX%E=l|A~6M^L7ugA4N{dcY1 z823*et_93!*qlA%|NPk}{_Fdl@q7M%weZjXr&gqe&8siWT4J+!U(J$#6|VpGuh74C z`ad(O&Vp+)DQ}-2O4<=}+UU#PTT5^CZw;>1K~=->Lw@?_HGftoD&JgD9xdIxMEE;< z@40KeU;Zw+>-S5(_Q$tBD@=YD|9ZSls{MU?^)%+ic>xvY`vJ0 znH73b|Cw{Djp>>H=P_NOHb>*X{h6EMl7AyL|D1Yg>;Ciq!>daCj6PUq~iJ0#cM%iZ$(W6_qY zuk()Xbrkx(`0Dv%N8jeHmV1BXZHA@Po4izIbN0O|XUil^s@7HrtX+2d?YF;AKX&=u zd2b%|@uawo-j(_dyYAWU`gW~o%967`?mcv$yYc+v{ntO5|J`x%-no4T(-zC$@3~xO z+EH`|F!NGkkmcA5HOtnY$;% zuU>fK*ZI~7+n+mgOf3wZ_P;*j^f}H7jsN^;1|Q!s52)7U&AV70apwxv>{#pn3#Kgp zX`lJ!l1lDgu|sM4c|4DvU7CA$>5}^|a&yY8=e{+Oe4`f|yK`63&$DTJHWxiD_*cKb zOs{2wK8E4v@3j7j;&r`NulGcqwdK=FJ5e;-N4cXRFd70>2!R7PzsbM-er`7dg8%~n DN`%g5 literal 0 HcmV?d00001 diff --git a/test/fixtures/isar_3_3_0_dev_2/isar_3_3_0_dev_2_wallet_data.isar.gz b/test/fixtures/isar_3_3_0_dev_2/isar_3_3_0_dev_2_wallet_data.isar.gz new file mode 100644 index 0000000000000000000000000000000000000000..d99a116e8a79f9bd9f5df9ba756c196b44aee149 GIT binary patch literal 2523 zcmeHI`&SZH9Cv4HQCr%>$I{G|HEnukTRIINoPz^XD=W(vCdbu$z_QR%3543wT3M?U zA~Cc~M^2G^B`HK{YO;YQDaAu3F7Xi}FBJ;zcJ}BTw_o-Lbbt8V?>YB#zn|~-bMHOZ zqZnfM=UAgr^cTBH-dQ@!G(mmXibtoR1M8#q4mt~Z>JQ2Y!ys9T*qfUub5ElQ8Gf4!Q*yj{)C%IUhqM$|@6fxB7KiGI-i#Urbz|8_ zy&84SWn_$&hc}E1chK05@S7pepvIRqWT1=Jp^8%__x1%58#)dwXQ&S;oNET~xE3_i z5$VKsnWwCCjpcLMw!@W1CsQ%5o@Bhmx0?Lw$P1Etw4+0$W}PWs6k^p)^E>jI%rf+S zV2cT}Jf|q${j0lw@WG*hXeI589=y1jH$M)34mX1$5(8%j`ihc2u&BV~<)a4(YZdP` z?4Lb<&oK=t=Sf-KfP8pD5orcY%ZZ*B;L^)2WgdT3hOqH(qeFfM|5BW-PF(k`uxMIjzq?1*yb;M$YkQ>K#%5EpUT`**gUd=I_Mt*@t?Fdu}?;c{7AgO{#7W+d{~vwBbpDjQ@>YCq4wkwp4j4PjMAK-lar!9#*!Y5=dhxjsQ+VQCahma&5mxn++~mYs$0gL7 zC@ycU1cPHWm9$B9`@Ufg$nm+vxaw(iCw@Dv6eG|Rmk9(14)-CHob znf&0F| z-D@p$xWLz;$(Ex~u3kR6XFiY8dE@wJAqmqI*k!PEG5b_Rm7L6VgL5not$U`Nl!|iT z_n#O3z%%wYKu#c-U&oMbV}bS}phN|)hI71t!{Tgqm9;4|Am6*-giVC)5AE>Z4!{UT zZ6efN%DnS0DlyCS-PDUczYX~nMJq10!57l_0@KQ2elg7?EjD@ARYFpOpJNdC!JX(|PsO4%$rJiiEzWIM;X$j``?j@v!d9L4grGl6WjZN$&^6!Y!*VwG`gtrLeX6g$ zwSkL@EYA#RsOKJMI(GK0tXS5692E~=K&tb@^V{2+6YopyFB?S2Oo _isarCoreLibraryPath() async { + final packageConfigFile = File('.dart_tool/package_config.json'); + final packageConfig = + jsonDecode(await packageConfigFile.readAsString()) + as Map; + final packages = packageConfig['packages'] as List; + final package = packages.cast?>().firstWhere( + (entry) => entry?['name'] == 'isar_community_flutter_libs', + orElse: () => null, + ); + if (package == null) { + throw StateError('Could not resolve isar_community_flutter_libs'); + } + + final libraryRelativePath = switch (Platform.operatingSystem) { + 'macos' => 'macos/libisar.dylib', + 'linux' => 'linux/libisar.so', + 'windows' => 'windows/libisar.dll', + final platform => throw UnsupportedError( + 'Isar migration test is not supported on $platform', + ), + }; + + final resolvedPackageRoot = packageConfigFile.uri.resolve( + package['rootUri'] as String, + ); + final packageRoot = resolvedPackageRoot.replace( + path: '${resolvedPackageRoot.path}/', + ); + return File.fromUri(packageRoot.resolve(libraryRelativePath)).path; +} + +void main() { + test('opens and updates an Isar 3.3.0-dev.2 database with 3.3.2', () async { + final temporaryDirectory = await Directory.systemTemp.createTemp( + 'stack-wallet-isar-migration-', + ); + Isar? isar; + + try { + await Isar.initializeIsarCore( + libraries: {Abi.current(): await _isarCoreLibraryPath()}, + ); + + final fixture = File( + 'test/fixtures/isar_3_3_0_dev_2/' + 'isar_3_3_0_dev_2_log.isar.gz', + ); + final fixtureBytes = await fixture.readAsBytes(); + expect( + sha256.convert(fixtureBytes).toString(), + 'c7bd6db8215a61d888c78c42a18b10541274d340c3c5377a5424bdcb595ad126', + reason: 'The migration fixture must only change intentionally', + ); + final databaseFile = File( + '${temporaryDirectory.path}/isar_3_3_0_dev_2_log.isar', + ); + await databaseFile.writeAsBytes(gzip.decode(fixtureBytes), flush: true); + + isar = await Isar.open( + [LogSchema], + directory: temporaryDirectory.path, + name: 'isar_3_3_0_dev_2_log', + ); + // ignore: experimental_member_use + await isar.verify(); + + final migratedLog = await isar.logs + .where() + .timestampInMillisUTCEqualTo(1722470400000) + .findFirst(); + expect(migratedLog, isNotNull); + expect(migratedLog!.message, 'created by isar_community 3.3.0-dev.2'); + expect(migratedLog.logLevel, LogLevel.Warning); + + await isar.writeTxn(() async { + await isar!.logs.put( + Log() + ..message = 'written by isar_community 3.3.2' + ..timestampInMillisUTC = 1722470401000 + ..logLevel = LogLevel.Info, + ); + }); + await isar.close(); + isar = null; + + isar = await Isar.open( + [LogSchema], + directory: temporaryDirectory.path, + name: 'isar_3_3_0_dev_2_log', + ); + // ignore: experimental_member_use + await isar.verify(); + expect(await isar.logs.count(), 2); + } finally { + await isar?.close(); + await temporaryDirectory.delete(recursive: true); + } + }); + + test( + 'opens and updates an Isar 3.3.0-dev.2 wallet database with 3.3.2', + () async { + final temporaryDirectory = await Directory.systemTemp.createTemp( + 'stack-wallet-isar-wallet-migration-', + ); + Isar? isar; + + try { + await Isar.initializeIsarCore( + libraries: {Abi.current(): await _isarCoreLibraryPath()}, + ); + + final fixture = File( + 'test/fixtures/isar_3_3_0_dev_2/' + 'isar_3_3_0_dev_2_wallet_data.isar.gz', + ); + final fixtureBytes = await fixture.readAsBytes(); + expect( + sha256.convert(fixtureBytes).toString(), + '6a0ce2f29c58875985d32253acb54469632fd55111812c39e1a41a87cf93f22a', + reason: 'The migration fixture must only change intentionally', + ); + final databaseFile = File( + '${temporaryDirectory.path}/isar_3_3_0_dev_2_wallet_data.isar', + ); + await databaseFile.writeAsBytes(gzip.decode(fixtureBytes), flush: true); + + isar = await Isar.open( + [TransactionV2Schema], + directory: temporaryDirectory.path, + name: 'isar_3_3_0_dev_2_wallet_data', + ); + // ignore: experimental_member_use + await isar.verify(); + + final migratedTransaction = await isar.transactionV2s + .where() + .txidWalletIdEqualTo('migration-transaction', 'migration-wallet') + .findFirst(); + expect(migratedTransaction, isNotNull); + expect(migratedTransaction!.type, TransactionType.outgoing); + expect(migratedTransaction.nonce, 7); + expect(migratedTransaction.inputs.single.valueStringSats, '101'); + expect( + migratedTransaction.inputs.single.outpoint!.txid, + 'previous-transaction', + ); + expect(migratedTransaction.outputs.single.valueStringSats, '100'); + expect(migratedTransaction.outputs.single.addresses, ['xel:recipient']); + + await isar.writeTxn(() async { + await isar!.transactionV2s.put( + TransactionV2( + walletId: 'migration-wallet', + blockHash: 'current-block-hash', + hash: 'current-transaction', + txid: 'current-transaction', + timestamp: 1722470401, + height: 43, + inputs: const [], + outputs: const [], + version: -1, + type: TransactionType.incoming, + subType: TransactionSubType.none, + otherData: jsonEncode({'nonce': 8}), + ), + ); + }); + await isar.close(); + isar = null; + + isar = await Isar.open( + [TransactionV2Schema], + directory: temporaryDirectory.path, + name: 'isar_3_3_0_dev_2_wallet_data', + ); + // ignore: experimental_member_use + await isar.verify(); + expect(await isar.transactionV2s.count(), 2); + } finally { + await isar?.close(); + await temporaryDirectory.delete(recursive: true); + } + }, + ); +} diff --git a/tool/isar_migration_fixture_generator/.gitignore b/tool/isar_migration_fixture_generator/.gitignore new file mode 100644 index 0000000000..22b503f473 --- /dev/null +++ b/tool/isar_migration_fixture_generator/.gitignore @@ -0,0 +1 @@ +lib/*.g.dart diff --git a/tool/isar_migration_fixture_generator/README.md b/tool/isar_migration_fixture_generator/README.md new file mode 100644 index 0000000000..e97dab56bc --- /dev/null +++ b/tool/isar_migration_fixture_generator/README.md @@ -0,0 +1,22 @@ +# Isar migration fixture generator + +This standalone Dart package intentionally pins the Isar version used before +Stack Wallet's 3.3.2 upgrade. Its `Log` model must remain structurally identical +to `lib/models/isar/models/log.dart`, and its `TransactionV2` plus embedded +models must remain structurally identical to the production wallet schema. + +From this directory, regenerate the fixture with: + +```sh +dart pub get --enforce-lockfile +dart run build_runner build --delete-conflicting-outputs +dart run bin/generate.dart ../../test/fixtures/isar_3_3_0_dev_2 \ + /path/to/isar_community_flutter_libs-3.3.0-dev.2//libisar +``` + +On macOS the library is `macos/libisar.dylib`; Linux uses +`linux/libisar.so`; Windows uses `windows/libisar.dll`. + +Isar may vary internal database bytes between generations. Regenerating the +fixture therefore requires an intentional review and update of the SHA-256 in +the fixture README and migration test. diff --git a/tool/isar_migration_fixture_generator/bin/generate.dart b/tool/isar_migration_fixture_generator/bin/generate.dart new file mode 100644 index 0000000000..1e89fe8669 --- /dev/null +++ b/tool/isar_migration_fixture_generator/bin/generate.dart @@ -0,0 +1,109 @@ +import 'dart:ffi'; +import 'dart:io'; + +import 'package:isar_community/isar.dart'; +import 'package:stack_wallet_isar_fixture_generator/log.dart'; +import 'package:stack_wallet_isar_fixture_generator/transaction_v2.dart'; + +Future main(List args) async { + if (args.length != 2) { + stderr.writeln('Usage: generate '); + exitCode = 64; + return; + } + + final outputDirectory = Directory(args[0])..createSync(recursive: true); + await Isar.initializeIsarCore(libraries: {Abi.current(): args[1]}); + + await _generateLogFixture(outputDirectory); + await _generateWalletDataFixture(outputDirectory); +} + +Future _generateLogFixture(Directory outputDirectory) async { + const databaseName = 'isar_3_3_0_dev_2_log'; + final isar = await Isar.open( + [LogSchema], + directory: outputDirectory.path, + name: databaseName, + ); + await isar.writeTxn(() async { + await isar.logs.put( + Log() + ..message = 'created by isar_community 3.3.0-dev.2' + ..timestampInMillisUTC = 1722470400000 + ..logLevel = LogLevel.Warning, + ); + }); + await isar.close(); + + await _compressDatabase(outputDirectory, databaseName); +} + +Future _generateWalletDataFixture(Directory outputDirectory) async { + const databaseName = 'isar_3_3_0_dev_2_wallet_data'; + final isar = await Isar.open( + [TransactionV2Schema], + directory: outputDirectory.path, + name: databaseName, + ); + await isar.writeTxn(() async { + final outpoint = OutpointV2() + ..txid = 'previous-transaction' + ..vout = 1; + final input = InputV2() + ..scriptSigHex = '00' + ..scriptSigAsm = null + ..sequence = 42 + ..outpoint = outpoint + ..addresses = ['xel:sender'] + ..valueStringSats = '101' + ..coinbase = null + ..witness = null + ..innerRedeemScriptAsm = null + ..walletOwns = true; + final output = OutputV2() + ..scriptPubKeyHex = '51' + ..scriptPubKeyAsm = null + ..valueStringSats = '100' + ..addresses = ['xel:recipient'] + ..walletOwns = false; + + await isar.transactionV2s.put( + TransactionV2( + walletId: 'migration-wallet', + blockHash: 'block-hash', + hash: 'migration-transaction', + txid: 'migration-transaction', + timestamp: 1722470400, + height: 42, + inputs: [input], + outputs: [output], + version: -1, + type: TransactionType.outgoing, + subType: TransactionSubType.none, + otherData: '{"nonce":7}', + ), + ); + }); + await isar.close(); + + await _compressDatabase(outputDirectory, databaseName); +} + +Future _compressDatabase( + Directory outputDirectory, + String databaseName, +) async { + final databaseFile = File('${outputDirectory.path}/$databaseName.isar'); + final compressedFile = File('${databaseFile.path}.gz'); + await compressedFile.writeAsBytes( + gzip.encode(await databaseFile.readAsBytes()), + flush: true, + ); + await databaseFile.delete(); + + final lockFile = File('${databaseFile.path}-lck'); + if (await lockFile.exists()) { + await lockFile.delete(); + } +} diff --git a/tool/isar_migration_fixture_generator/lib/log.dart b/tool/isar_migration_fixture_generator/lib/log.dart new file mode 100644 index 0000000000..88bb2266e0 --- /dev/null +++ b/tool/isar_migration_fixture_generator/lib/log.dart @@ -0,0 +1,18 @@ +import 'package:isar_community/isar.dart'; + +part 'log.g.dart'; + +enum LogLevel { Info, Warning, Error, Fatal, Debug } + +@Collection() +class Log { + Id id = Isar.autoIncrement; + + late String message; + + @Index() + late int timestampInMillisUTC; + + @Enumerated(EnumType.name) + late LogLevel logLevel; +} diff --git a/tool/isar_migration_fixture_generator/lib/transaction_v2.dart b/tool/isar_migration_fixture_generator/lib/transaction_v2.dart new file mode 100644 index 0000000000..a8ab82f999 --- /dev/null +++ b/tool/isar_migration_fixture_generator/lib/transaction_v2.dart @@ -0,0 +1,123 @@ +import 'dart:convert'; + +import 'package:isar_community/isar.dart'; + +part 'transaction_v2.g.dart'; + +enum TransactionType { outgoing, incoming, sentToSelf, unknown } + +enum TransactionSubType { + none, + bip47Notification, + mint, + join, + ethToken, + cashFusion, + sparkMint, + sparkSpend, + ordinal, + mweb, + splToken, +} + +@Embedded() +class OutpointV2 { + late final String txid; + late final int vout; + + OutpointV2(); + + @override + int get hashCode => Object.hash(txid, vout); +} + +@Embedded() +class InputV2 { + late final String? scriptSigHex; + late final String? scriptSigAsm; + late final int? sequence; + late final OutpointV2? outpoint; + late final List addresses; + late final String valueStringSats; + late final String? coinbase; + late final String? witness; + late final String? innerRedeemScriptAsm; + late final bool walletOwns; + + InputV2(); +} + +@Embedded() +class OutputV2 { + late final String scriptPubKeyHex; + late final String? scriptPubKeyAsm; + late final String valueStringSats; + late final List addresses; + late final bool walletOwns; + + OutputV2(); +} + +@Collection() +class TransactionV2 { + TransactionV2({ + required this.walletId, + required this.blockHash, + required this.hash, + required this.txid, + required this.timestamp, + required this.height, + required this.inputs, + required this.outputs, + required this.version, + required this.type, + required this.subType, + required this.otherData, + }); + + Id id = Isar.autoIncrement; + + @Index() + final String walletId; + + @Index(unique: true, composite: [CompositeIndex('walletId')]) + final String txid; + + final String hash; + + @Index() + late final int timestamp; + + final int? height; + final String? blockHash; + final int version; + final List inputs; + final List outputs; + + @enumerated + final TransactionType type; + + @enumerated + final TransactionSubType subType; + + final String? otherData; + + Map get _decodedOtherData => + jsonDecode(otherData ?? '{}') as Map; + + bool get isEpiccashTransaction => + _decodedOtherData['isEpiccashTransaction'] == true; + + int? get numberOfMessages => _decodedOtherData['numberOfMessages'] as int?; + + String? get slateId => _decodedOtherData['slateId'] as String?; + + String? get onChainNote => _decodedOtherData['onChainNote'] as String?; + + bool get isCancelled => _decodedOtherData['isCancelled'] == true; + + String? get contractAddress => + _decodedOtherData['contractAddress'] as String?; + + int? get nonce => _decodedOtherData['nonce'] as int?; +} diff --git a/tool/isar_migration_fixture_generator/pubspec.lock b/tool/isar_migration_fixture_generator/pubspec.lock new file mode 100644 index 0000000000..321acea4d9 --- /dev/null +++ b/tool/isar_migration_fixture_generator/pubspec.lock @@ -0,0 +1,509 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f + url: "https://pub.dev" + source: hosted + version: "85.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: f4ad0fea5f102201015c9aae9d93bc02f75dd9491529a8c21f88d17a8523d44c + url: "https://pub.dev" + source: hosted + version: "7.6.0" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 + url: "https://pub.dev" + source: hosted + version: "2.13.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + build: + dependency: transitive + description: + name: build + sha256: ce76b1d48875e3233fde17717c23d1f60a91cc631597e49a400c89b475395b1d + url: "https://pub.dev" + source: hosted + version: "3.1.0" + build_config: + dependency: transitive + description: + name: build_config + sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "79e05eaf15a48d7230b053a4363b8eaac0cc234bbd0134c3229455481f55cbc6" + url: "https://pub.dev" + source: hosted + version: "4.1.5" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: d1d57f7807debd7349b4726a19fd32ec8bc177c71ad0febf91a20f84cd2d4b46 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: b24597fceb695969d47025c958f3837f9f0122e237c6a22cb082a5ac66c3ca30 + url: "https://pub.dev" + source: hosted + version: "2.7.1" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "066dda7f73d8eb48ba630a55acb50c4a84a2e6b453b1cb4567f581729e794f7b" + url: "https://pub.dev" + source: hosted + version: "9.3.1" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "31b24be6615ec7fcf70b3aa5a7469fe35826485e639a16dd7eb83ba30e4cc6a8" + url: "https://pub.dev" + source: hosted + version: "8.12.7" + characters: + dependency: transitive + description: + name: characters + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + url: "https://pub.dev" + source: hosted + version: "1.4.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" + url: "https://pub.dev" + source: hosted + version: "2.0.4" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d" + url: "https://pub.dev" + source: hosted + version: "4.11.1" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.dev" + source: hosted + version: "3.0.7" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "8a0e5fba27e8ee025d2ffb4ee820b4e6e2cf5e4246a6b1a477eb66866947e0bb" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + dartx: + dependency: transitive + description: + name: dartx + sha256: "8b25435617027257d43e6508b5fe061012880ddfdaa75a71d607c3de2a13d244" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + ffi: + dependency: transitive + description: + name: ffi + sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.dev" + source: hosted + version: "2.1.3" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + io: + dependency: transitive + description: + name: io + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + url: "https://pub.dev" + source: hosted + version: "1.0.5" + isar_community: + dependency: "direct main" + description: + name: isar_community + sha256: eae4a7e659bec0f92fc953afb8738512062df6a2ff99fe838cb53f9ed4fa6e97 + url: "https://pub.dev" + source: hosted + version: "3.3.0-dev.2" + isar_community_generator: + dependency: "direct dev" + description: + name: isar_community_generator + sha256: "9da90eafaecf2ec482f50854373e37f3d9e33387830dbc0265bcdb74d9036e74" + url: "https://pub.dev" + source: hosted + version: "3.3.0-dev.2" + js: + dependency: transitive + description: + name: js + sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "2a743920d81b7910627f68ee2c9ac1fc0bfee32b9fc3403587d7c6791ca12f80" + url: "https://pub.dev" + source: hosted + version: "4.12.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd" + url: "https://pub.dev" + source: hosted + version: "0.12.20" + meta: + dependency: transitive + description: + name: meta + sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" + url: "https://pub.dev" + source: hosted + version: "1.19.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + pool: + dependency: transitive + description: + name: pool + sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" + url: "https://pub.dev" + source: hosted + version: "1.5.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + shelf: + dependency: transitive + description: + name: shelf + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + url: "https://pub.dev" + source: hosted + version: "1.4.2" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "7b19d6ba131c6eb98bfcbf8d56c1a7002eba438af2e7ae6f8398b2b0f4f381e3" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.dev" + source: hosted + version: "1.10.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + url: "https://pub.dev" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: "475610b2aa23c19687cce2961e44b0cc57cafe220f67c2b80201231b2a07fbe7" + url: "https://pub.dev" + source: hosted + version: "0.7.13" + time: + dependency: transitive + description: + name: time + sha256: "46187cf30bffdab28c56be9a63861b36e4ab7347bf403297595d6a97e10c789f" + url: "https://pub.dev" + source: hosted + version: "2.1.6" + timing: + dependency: transitive + description: + name: timing + sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + xxh3: + dependency: transitive + description: + name: xxh3 + sha256: "399a0438f5d426785723c99da6b16e136f4953fb1e9db0bf270bd41dd4619916" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" +sdks: + dart: ">=3.11.0 <4.0.0" diff --git a/tool/isar_migration_fixture_generator/pubspec.yaml b/tool/isar_migration_fixture_generator/pubspec.yaml new file mode 100644 index 0000000000..a84592ead2 --- /dev/null +++ b/tool/isar_migration_fixture_generator/pubspec.yaml @@ -0,0 +1,12 @@ +name: stack_wallet_isar_fixture_generator +publish_to: none + +environment: + sdk: ">=3.6.0 <4.0.0" + +dependencies: + isar_community: 3.3.0-dev.2 + +dev_dependencies: + build_runner: 2.7.1 + isar_community_generator: 3.3.0-dev.2 From 72c0290c7625b80e58573b1543a9a3d3d15b5080 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 5 Aug 2026 22:37:18 -0500 Subject: [PATCH 6/8] build: pin Flutter toolchain sources --- .github/workflows/build.yaml | 42 ++++++++++++++++++------------------ .github/workflows/test.yaml | 4 ++-- Dockerfile | 18 +++++++++++++--- 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 4851c64c22..a8f387d3ee 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -50,8 +50,8 @@ jobs: - name: Use Flutter 3.44.8 run: | - git -C "$FLUTTER_HOME" fetch --depth 1 origin refs/tags/3.44.8 - git -C "$FLUTTER_HOME" checkout --detach FETCH_HEAD + git -C "$FLUTTER_HOME" fetch --depth 1 origin 058e0af2c2b57e369d905a03ac9748b0ebf543c6 + git -C "$FLUTTER_HOME" checkout --detach 058e0af2c2b57e369d905a03ac9748b0ebf543c6 flutter --version - name: Set version @@ -133,8 +133,8 @@ jobs: - name: Use Flutter 3.44.8 run: | - git -C "$FLUTTER_HOME" fetch --depth 1 origin refs/tags/3.44.8 - git -C "$FLUTTER_HOME" checkout --detach FETCH_HEAD + git -C "$FLUTTER_HOME" fetch --depth 1 origin 058e0af2c2b57e369d905a03ac9748b0ebf543c6 + git -C "$FLUTTER_HOME" checkout --detach 058e0af2c2b57e369d905a03ac9748b0ebf543c6 flutter --version - name: Set version @@ -264,7 +264,7 @@ jobs: echo "version=${VERSION}" >> $GITHUB_OUTPUT echo "build_number=${BUILD_NUMBER}" >> $GITHUB_OUTPUT - - uses: subosito/flutter-action@v2 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2 with: flutter-version: '3.44.8' channel: 'stable' @@ -383,7 +383,7 @@ jobs: echo "version=${VERSION}" >> $GITHUB_OUTPUT echo "build_number=${BUILD_NUMBER}" >> $GITHUB_OUTPUT - - uses: subosito/flutter-action@v2 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2 with: flutter-version: '3.44.8' channel: 'stable' @@ -465,7 +465,7 @@ jobs: toolchain: stable targets: aarch64-apple-ios - - uses: subosito/flutter-action@v2 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2 with: flutter-version: '3.44.8' channel: 'stable' @@ -535,8 +535,8 @@ jobs: - name: Use Flutter 3.44.8 run: | - git -C "$FLUTTER_HOME" fetch --depth 1 origin refs/tags/3.44.8 - git -C "$FLUTTER_HOME" checkout --detach FETCH_HEAD + git -C "$FLUTTER_HOME" fetch --depth 1 origin 058e0af2c2b57e369d905a03ac9748b0ebf543c6 + git -C "$FLUTTER_HOME" checkout --detach 058e0af2c2b57e369d905a03ac9748b0ebf543c6 flutter --version - name: Set version @@ -605,8 +605,8 @@ jobs: - name: Use Flutter 3.44.8 run: | - git -C "$FLUTTER_HOME" fetch --depth 1 origin refs/tags/3.44.8 - git -C "$FLUTTER_HOME" checkout --detach FETCH_HEAD + git -C "$FLUTTER_HOME" fetch --depth 1 origin 058e0af2c2b57e369d905a03ac9748b0ebf543c6 + git -C "$FLUTTER_HOME" checkout --detach 058e0af2c2b57e369d905a03ac9748b0ebf543c6 flutter --version - name: Set version @@ -792,8 +792,8 @@ jobs: - name: Use Flutter 3.44.8 run: | - git -C "$FLUTTER_HOME" fetch --depth 1 origin refs/tags/3.44.8 - git -C "$FLUTTER_HOME" checkout --detach FETCH_HEAD + git -C "$FLUTTER_HOME" fetch --depth 1 origin 058e0af2c2b57e369d905a03ac9748b0ebf543c6 + git -C "$FLUTTER_HOME" checkout --detach 058e0af2c2b57e369d905a03ac9748b0ebf543c6 flutter --version - name: Set version @@ -910,7 +910,7 @@ jobs: echo "version=${VERSION}" >> $GITHUB_OUTPUT echo "build_number=${BUILD_NUMBER}" >> $GITHUB_OUTPUT - - uses: subosito/flutter-action@v2 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2 with: flutter-version: '3.44.8' channel: 'stable' @@ -999,7 +999,7 @@ jobs: echo "version=${VERSION}" >> $GITHUB_OUTPUT echo "build_number=${BUILD_NUMBER}" >> $GITHUB_OUTPUT - - uses: subosito/flutter-action@v2 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2 with: flutter-version: '3.44.8' channel: 'stable' @@ -1074,7 +1074,7 @@ jobs: toolchain: stable targets: aarch64-apple-ios - - uses: subosito/flutter-action@v2 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2 with: flutter-version: '3.44.8' channel: 'stable' @@ -1256,8 +1256,8 @@ jobs: - name: Use Flutter 3.44.8 run: | - git -C "$FLUTTER_HOME" fetch --depth 1 origin refs/tags/3.44.8 - git -C "$FLUTTER_HOME" checkout --detach FETCH_HEAD + git -C "$FLUTTER_HOME" fetch --depth 1 origin 058e0af2c2b57e369d905a03ac9748b0ebf543c6 + git -C "$FLUTTER_HOME" checkout --detach 058e0af2c2b57e369d905a03ac9748b0ebf543c6 flutter --version - name: Set version @@ -1374,7 +1374,7 @@ jobs: echo "version=${VERSION}" >> $GITHUB_OUTPUT echo "build_number=${BUILD_NUMBER}" >> $GITHUB_OUTPUT - - uses: subosito/flutter-action@v2 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2 with: flutter-version: '3.44.8' channel: 'stable' @@ -1463,7 +1463,7 @@ jobs: echo "version=${VERSION}" >> $GITHUB_OUTPUT echo "build_number=${BUILD_NUMBER}" >> $GITHUB_OUTPUT - - uses: subosito/flutter-action@v2 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2 with: flutter-version: '3.44.8' channel: 'stable' @@ -1540,7 +1540,7 @@ jobs: toolchain: stable targets: aarch64-apple-ios - - uses: subosito/flutter-action@v2 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2 with: flutter-version: '3.44.8' channel: 'stable' diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index f572c33fb6..a855d00719 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -20,8 +20,8 @@ jobs: - name: Use Flutter 3.44.8 run: | - git -C "$FLUTTER_HOME" fetch --depth 1 origin refs/tags/3.44.8 - git -C "$FLUTTER_HOME" checkout --detach FETCH_HEAD + git -C "$FLUTTER_HOME" fetch --depth 1 origin 058e0af2c2b57e369d905a03ac9748b0ebf543c6 + git -C "$FLUTTER_HOME" checkout --detach 058e0af2c2b57e369d905a03ac9748b0ebf543c6 flutter --version - name: Configure app diff --git a/Dockerfile b/Dockerfile index 5936efd1e6..217fe2da4b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -83,7 +83,11 @@ RUN curl -fsSL https://go.dev/dl/go1.24.13.linux-amd64.tar.gz -o /tmp/go.tar.gz ENV FLUTTER_HOME=/opt/flutter \ PATH=/opt/flutter/bin:/opt/flutter/bin/cache/dart-sdk/bin:$PATH -RUN git clone --depth 1 --branch 3.44.8 https://github.com/flutter/flutter.git "$FLUTTER_HOME" \ +RUN git init "$FLUTTER_HOME" \ + && git -C "$FLUTTER_HOME" remote add origin https://github.com/flutter/flutter.git \ + && git -C "$FLUTTER_HOME" fetch --depth 1 origin 058e0af2c2b57e369d905a03ac9748b0ebf543c6 \ + && git -C "$FLUTTER_HOME" checkout --detach 058e0af2c2b57e369d905a03ac9748b0ebf543c6 \ + && git -C "$FLUTTER_HOME" tag 3.44.8 058e0af2c2b57e369d905a03ac9748b0ebf543c6 \ && git config --global --add safe.directory '*' \ && flutter config --no-analytics \ && flutter precache --linux --android \ @@ -168,7 +172,11 @@ RUN curl -fsSL https://go.dev/dl/go1.24.13.linux-amd64.tar.gz -o /tmp/go.tar.gz ENV FLUTTER_HOME=/opt/flutter \ PATH=/opt/flutter/bin:/opt/flutter/bin/cache/dart-sdk/bin:$PATH -RUN git clone --depth 1 --branch 3.44.8 https://github.com/flutter/flutter.git "$FLUTTER_HOME" \ +RUN git init "$FLUTTER_HOME" \ + && git -C "$FLUTTER_HOME" remote add origin https://github.com/flutter/flutter.git \ + && git -C "$FLUTTER_HOME" fetch --depth 1 origin 058e0af2c2b57e369d905a03ac9748b0ebf543c6 \ + && git -C "$FLUTTER_HOME" checkout --detach 058e0af2c2b57e369d905a03ac9748b0ebf543c6 \ + && git -C "$FLUTTER_HOME" tag 3.44.8 058e0af2c2b57e369d905a03ac9748b0ebf543c6 \ && git config --global --add safe.directory '*' \ && flutter config --no-analytics \ && flutter precache --android \ @@ -200,7 +208,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ ENV FLUTTER_HOME=/opt/flutter \ PATH=/opt/flutter/bin:/opt/flutter/bin/cache/dart-sdk/bin:$PATH -RUN git clone --depth 1 --branch 3.44.8 https://github.com/flutter/flutter.git "$FLUTTER_HOME" \ +RUN git init "$FLUTTER_HOME" \ + && git -C "$FLUTTER_HOME" remote add origin https://github.com/flutter/flutter.git \ + && git -C "$FLUTTER_HOME" fetch --depth 1 origin 058e0af2c2b57e369d905a03ac9748b0ebf543c6 \ + && git -C "$FLUTTER_HOME" checkout --detach 058e0af2c2b57e369d905a03ac9748b0ebf543c6 \ + && git -C "$FLUTTER_HOME" tag 3.44.8 058e0af2c2b57e369d905a03ac9748b0ebf543c6 \ && git config --global --add safe.directory '*' \ && flutter config --no-analytics \ && flutter precache --linux \ From 6b890854373645f4a8e3d42a776876d4616d3032 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 5 Aug 2026 22:43:21 -0500 Subject: [PATCH 7/8] test: make PIN setup expectation platform-aware --- .../screen_tests/onboarding/create_pin_view_screen_test.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/screen_tests/onboarding/create_pin_view_screen_test.dart b/test/screen_tests/onboarding/create_pin_view_screen_test.dart index 455d347c45..9029e84792 100644 --- a/test/screen_tests/onboarding/create_pin_view_screen_test.dart +++ b/test/screen_tests/onboarding/create_pin_view_screen_test.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -10,7 +12,6 @@ import 'package:stackwallet/providers/global/prefs_provider.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/themes/theme_service.dart'; import 'package:stackwallet/utilities/biometrics.dart'; -import 'package:stackwallet/utilities/prefs.dart'; import 'package:stackwallet/widgets/custom_pin_put/pin_keyboard.dart'; import '../../sample_data/theme_json.dart'; @@ -132,7 +133,7 @@ void main() { expect(await platformOverrides.secureStorage.read(key: kPinKey), '1234'); expect(platformOverrides.secureStorage.writes, 1); - expect(biometrics.calls, 0); + expect(biometrics.calls, Platform.isLinux ? 0 : 1); verify(prefs.useBiometrics = false).called(1); verify(prefs.hasPin = true).called(1); From ff85741b10fb9bb70acc1d71e89670cadcece435 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 6 Aug 2026 01:07:22 -0500 Subject: [PATCH 8/8] deps(xelis): pin maintained FFI fork --- pubspec.lock | 16 ++++------------ .../app_config/templates/pubspec.template.yaml | 4 ++-- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index d740591d87..d487f766fe 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1421,14 +1421,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.0" - json_serializable: - dependency: transitive - description: - name: json_serializable - sha256: e45aefa0324f08c683caafbb94b72837aa6193c61822799c916e45f4a263113d - url: "https://pub.dev" - source: hosted - version: "6.14.1" jsontool: dependency: transitive description: @@ -2600,11 +2592,11 @@ packages: dependency: "direct main" description: path: "." - ref: "v0.3.0" - resolved-ref: f67a02c6fff7c3c269f4497e077c7cf419076a60 - url: "https://github.com/xelis-project/xelis-flutter-ffi.git" + ref: "3b122aaea48daee2e1844f17650e2f1ad31d8bcb" + resolved-ref: "3b122aaea48daee2e1844f17650e2f1ad31d8bcb" + url: "https://github.com/ManyMath/xelis-flutter-ffi.git" source: git - version: "0.2.0" + version: "0.3.1" xml: dependency: transitive description: diff --git a/scripts/app_config/templates/pubspec.template.yaml b/scripts/app_config/templates/pubspec.template.yaml index 094b6ee70b..cba5bc7bf0 100644 --- a/scripts/app_config/templates/pubspec.template.yaml +++ b/scripts/app_config/templates/pubspec.template.yaml @@ -36,8 +36,8 @@ dependencies: ## ref: a07489e6ed10355ad34f08445dcd9441202393c1 # xelis_flutter: # git: -# url: https://github.com/xelis-project/xelis-flutter-ffi.git -# ref: v0.3.0 +# url: https://github.com/ManyMath/xelis-flutter-ffi.git +# ref: 3b122aaea48daee2e1844f17650e2f1ad31d8bcb # %%END_ENABLE_XEL%% # %%ENABLE_FIRO%%