Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 25 additions & 13 deletions .github/workflows/check_and_lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,35 @@ on:

name: Check and Lint

permissions:
contents: read

jobs:
Flutter:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
with:
channel: "stable"
- name: Pub Get
run: dart pub get --no-precompile
working-directory: build_tool
- name: Dart Format
run: dart format . --output=none --set-exit-if-changed
working-directory: build_tool
- name: Analyze
- name: Pub Get (package)
run: flutter pub get
- name: Pub Get (example)
run: flutter pub get
working-directory: example
- name: Pub Get (tool)
run: dart pub get
working-directory: tool
- name: Dart Format (hook + tool)
run: dart format hook tool --output=none --set-exit-if-changed
- name: Analyze (package)
run: flutter analyze
- name: Analyze (example)
run: flutter analyze
working-directory: example
- name: Analyze (tool)
run: dart analyze
working-directory: build_tool
- name: Test
run: flutter test
working-directory: build_tool
working-directory: tool
- name: Test Cargo dependency parser
run: dart test
working-directory: tool
56 changes: 50 additions & 6 deletions .github/workflows/test_example_plugin_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:

name: Test Example Plugin

permissions:
contents: read

jobs:
Build:
runs-on: ${{ matrix.os }}
Expand All @@ -20,23 +23,29 @@ jobs:
- debug
- release
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
with:
channel: "stable"
- name: Cache Cargo
uses: actions/cache@v4
- name: Cache Cargo registry
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
~/.cargo/registry
~/.cargo/git
rust/target
key: ${{ runner.os }}-cargo-${{ hashFiles('rust/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Cache Rust toolchain
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
~/.rustup/toolchains
~/.rustup/update-hashes
key: ${{ runner.os }}-rustup-${{ hashFiles('rust/rust-toolchain.toml') }}
- name: Install Linux dependencies
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get update && sudo apt-get install -y libclang-dev llvm libgtk-3-dev ninja-build cmake
run: sudo apt-get update && sudo apt-get install -y libgtk-3-dev ninja-build cmake
- name: Pub Get
run: flutter pub get
working-directory: example
Expand All @@ -59,3 +68,38 @@ jobs:
- name: Build Android (${{ matrix.build_mode }})
run: flutter build apk --${{ matrix.build_mode }} -v
working-directory: example

# Keeps the advertised minimum Flutter version honest: the package claims
# Flutter 3.47.2+, so build the example with that exact release too.
Build-Minimum-Flutter:
runs-on: macos-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
with:
flutter-version: "3.47.2"
- name: Cache Cargo registry
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-cargo-${{ hashFiles('rust/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Cache Rust toolchain
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
~/.rustup/toolchains
~/.rustup/update-hashes
key: ${{ runner.os }}-rustup-${{ hashFiles('rust/rust-toolchain.toml') }}
- name: Pub Get
run: flutter pub get
working-directory: example
- name: Build macOS (debug)
run: flutter build macos --debug -v
working-directory: example
- name: Build Android (debug)
run: flutter build apk --debug -v
working-directory: example
27 changes: 7 additions & 20 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,14 @@ migrate_working_dir/
**/doc/api/
.dart_tool/
build/
rust/src/bin
android/src/main/jniLibs/
rust/default/
ios/include/
ios/libs/
macos/include/
macos/libs/
lib/git_versions.dart
temp/

scripts/windows/vcpkg/
scripts/linux/xelis_flutter_bins/
scripts/ios/xelis_flutter_bins/
scripts/android/xelis_flutter_bins/
scripts/windows/xelis_flutter_bins/
scripts/macos/xelis_flutter_bins/
scripts/android/cache/
scripts/android/build/openssl/

linux/bin/
# Rust
target
.dart_tool
*.iml
!pubspec.lock
rust/src/bin
rust/default/

!pubspec.lock
# The maintainer tool in tool/ is a throwaway package; its lockfile is not tracked.
/tool/pubspec.lock
18 changes: 1 addition & 17 deletions .metadata
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,14 @@ version:
revision: "c519ee916eaeb88923e67befb89c0f1dabfa83e6"
channel: "stable"

project_type: plugin
project_type: package

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
- platform: android
create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
- platform: ios
create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
- platform: linux
create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
- platform: macos
create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
- platform: windows
create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6
base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6

# User provided section

Expand All @@ -39,4 +24,3 @@ migration:
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
27 changes: 27 additions & 0 deletions .pubignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# pub uses this file INSTEAD of .gitignore for this directory, so it repeats
# the relevant .gitignore rules and adds pub-only exclusions.
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
*.iml
*.ipr
*.iws
.idea/
.vscode/
**/doc/api/
.dart_tool/
build/
lib/git_versions.dart
temp/
target
rust/src/bin
rust/default/
# Maintainer-only Dart package; not part of the published package.
/tool/
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# 0.4.0

- **Breaking:** the native build moved from Cargokit (podspec / CMake / Gradle FFI plugin) to Flutter native assets: `hook/build.dart` via `flutter_rust_bridge_hooks` and `native_toolchain_rust`. Consumers now need Flutter 3.47.2+ (Dart 3.13+), rustup, and Android NDK r27+ (Windows hosts also need the Visual Studio C++ build tools); nothing else changes on the consumer side (no CocoaPods/CMake/Gradle configuration).
- Removed the `android/`, `ios/`, `macos/`, `linux/`, `windows/`, and `cargokit/` plugin scaffolding; the package is a Dart package with a build hook (`.metadata` now says `package`).
- Bumped `flutter_rust_bridge` to 2.13.0 and regenerated the bindings; bumped `freezed` to 4.0.1 and regenerated the `*.freezed.dart` parts (Dart 3.13 no longer accepts `final` on constructor parameters, which freezed 3 emitted).
- `rust/rust-toolchain.toml` pins Rust 1.91.0 and lists every cross-compilation target; rustup downloads the toolchain (minimal profile) and targets automatically on the first build (roughly 1.9 GB on disk once installed).
- Build behaviour: Rust is always compiled with the release profile (also in Flutter debug builds); artifacts live under the app's `.dart_tool/hooks_runner/`, one Cargo target directory per build configuration (target platform/architecture, debug and release separately), and `flutter clean` removes them (on Windows hosts the Cargo target directory is placed under `%TEMP%\frb_native_assets_<hash>` and is not removed by `flutter clean`); the hook re-runs when Rust sources, `Cargo.toml`, `Cargo.lock` or `rust-toolchain.toml` change.
- The hook runs `cargo build --locked`, links the Android library against the app's `minSdk` API level (as Cargokit did), and forces 16 KB page alignment for 64-bit Android ABIs regardless of the NDK version.
- `flutter test` in a dependent app now runs the hook too and installs the host library under `build/native_assets/<os>/`; on macOS and Linux host tests that call into Rust need `FRB_DART_LOAD_EXTERNAL_LIBRARY_NATIVE_LIB_DIR=build/native_assets/<os>/` (see README), on Windows it is found automatically.
- The bindings regeneration script is its own Dart package under `tool/`: `cd tool && dart run generate_bindings.dart` (maintainers need `rustup component add rustfmt --toolchain 1.91.0`, since the toolchain now uses the minimal profile). It reads the codegen version from the `flutter_rust_bridge` pin in `pubspec.yaml`; the unused `[build-dependencies]` entry for `flutter_rust_bridge_codegen` was removed from `rust/Cargo.toml`.
- `native_toolchain_rust`, which spawns rustup/cargo on consumer machines, is pinned exactly (1.0.6); the shared `hooks` protocol package stays a range (`^2.1.0`) so apps can combine this package with other native-assets packages.
- Removed the unused `path_provider`, `ffi`, and `plugin_platform_interface` dependencies. Apps that used one of them only through this package must now depend on it directly.

# 0.2.0

- Unified API with Genesix reference bindings post-hard-fork
Expand Down
75 changes: 49 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,70 @@
<!--
This README describes the package. If you publish this package to pub.dev,
this README's contents appear on the landing page for your package.

For information about how to write a good package README, see the guide for
[writing package pages](https://dart.dev/tools/pub/writing-package-pages).

For general information about developing packages, see the Dart guide for
[creating packages](https://dart.dev/guides/libraries/create-packages)
and the Flutter guide for
[developing packages and plugins](https://flutter.dev/to/develop-packages).
-->

## NEEDS UPDATING

# Xelis Wallet FFI Bindings for Flutter
This repo is setup to be a shoe-in library in flutter applications, allowing easy access to the native Rust wallet methods from `xelis-blockchain` without the need for any rewrites. This is enabled by the `flutter_rust_bridge_codegen` tool, as well as the automated build & dependency conventions used within this repository.
This repo is set up to be a drop-in library for Flutter applications, allowing easy access to the native Rust wallet methods from `xelis-blockchain` without the need for any rewrites. This is enabled by the `flutter_rust_bridge_codegen` tool, as well as the automated build & dependency conventions used within this repository.

The continued adoption of Genesix features and dev QOL needs for apps are the primary points of focus for this library, helping to facilitate Xelis integration and adoption for the community.

## Compatibility

![Rust](https://img.shields.io/badge/rust-1.91%2B-orange.svg)
![Xelis Dart SDK](https://img.shields.io/badge/xelis__dart__sdk-0.30.9%2B-blue.svg)
![Flutter](https://img.shields.io/badge/flutter-3.47.2%2B-blue.svg)
![Rust](https://img.shields.io/badge/rust-1.91.0-orange.svg)

## Getting started
To make use of `xelis-flutter-ffi`, simply add a git dependency to your flutter app's `pubspec.yaml`, like this:

### Requirements

Every machine that builds an app depending on this package needs:

- **Flutter 3.47.2 or newer** (Dart 3.13+). The Rust crate is compiled and bundled by Flutter native assets (build hooks).
- **[rustup](https://rustup.rs).** The first build downloads the pinned toolchain (Rust 1.91.0, minimal profile) and every cross-compilation target listed in `rust/rust-toolchain.toml`; installed, that is roughly 1.9 GB on disk, about 1.4 GB of it for the cross-compilation targets. The list covers all platforms Flutter can target so that a single checkout builds everywhere.
Cargo must be able to find `rustc`: with a standard rustup install (`~/.cargo/bin` on `PATH`) nothing needs to be done; with Homebrew's rustup add `$(brew --prefix rustup)/bin` to `PATH`, otherwise the build fails with `could not execute process rustc -vV`.
Builds started from Xcode (Product > Run / Archive) do not see your shell's `PATH`; they get the system one from `/etc/paths`. Homebrew puts only `rustup` itself in `/opt/homebrew/bin` and keeps the `cargo`/`rustc` proxies in its keg, which is on no `PATH`, so the build fails there even though it works in a terminal. Link the proxies into `/usr/local/bin` (always on the system `PATH`) once:

```bash
sudo mkdir -p /usr/local/bin && sudo ln -s "$(brew --prefix rustup)"/bin/* /usr/local/bin/
```
- **Windows hosts:** Visual Studio Build Tools with the "Desktop development with C++" workload. The Rust `*-pc-windows-msvc` host toolchain needs its `link.exe`, also for Android-only builds.
- **Android only:** NDK r27 or newer (the NDK Flutter installs by default satisfies this). The library is linked against your app's `minSdk` API level and, for 64-bit ABIs, with 16 KB page alignment regardless of the NDK version.

### Adding the dependency

Add a git dependency to your Flutter app's `pubspec.yaml`:

```yaml
xelis_flutter:
git:
url: https://github.com/xelis-project/xelis-flutter-ffi.git
ref: v0.1.0 # release tags will be attributed to specific xelis-blockchain releases going forward
ref: v0.4.0 # use a release tag; releases track specific xelis-blockchain versions
```

When either `flutter run` or `flutter build` are called, this library will handle all dependency building and/or Podspec operations for you, without the need for any extra setup.
When `flutter run`, `flutter build`, or `flutter test` is invoked, Flutter runs this package's `hook/build.dart`, which compiles the Rust crate for the target platform with Cargo and bundles the resulting library into the app. No CocoaPods, CMake, or Gradle configuration is required on your side.

### Build behaviour worth knowing

- The Rust crate is always compiled with Cargo's release profile, also for Flutter debug builds (build hooks have no notion of the Flutter build mode). The first build of each target architecture takes several minutes; later builds are incremental and the hook only re-runs when Rust sources, `Cargo.toml`, `Cargo.lock`, or `rust-toolchain.toml` change.
- Build artifacts live under the app's `.dart_tool/hooks_runner/`, one Cargo target directory per build configuration (target platform and architecture, and debug and release builds separately), so the first release build after debug builds compiles the crate again. `flutter clean` deletes them all, so the next build starts from scratch. On Windows hosts, `flutter_rust_bridge_hooks` keeps paths short by placing the Cargo target directory under `%TEMP%\frb_native_assets_<hash>` instead; `flutter clean` does not remove those, delete them manually to reclaim disk space.
- The hook runs `cargo build --locked`: a `Cargo.toml` change without a matching `Cargo.lock` update fails the build instead of silently re-resolving dependencies.
- Build hooks run with a filtered environment. Flutter passes through `PATH`, `HOME`, `TMPDIR`/`TEMP`, `ANDROID_HOME`, `ANDROID_NDK*`, and a few OS variables, but not `RUSTUP_HOME`, `CARGO_HOME`, `RUSTFLAGS`, or `CARGO_*`. Non-default rustup/cargo locations configured through those variables are therefore not seen by the build, and the toolchain is installed under `~/.rustup`.
- `flutter test` also runs the hook and installs the host library under `build/native_assets/<macos|linux|windows>/`. On Windows that directory is on `PATH` during the test run, so the bindings load it. On macOS and Linux the bindings look the library up by name and will not find it there; point them at it: `FRB_DART_LOAD_EXTERNAL_LIBRARY_NATIVE_LIB_DIR=build/native_assets/macos/ flutter test` (use `linux/` on Linux). Tests that never touch Rust are unaffected apart from the hook's compile time.

The FFI bindings are updated and included in the repo; there is no need to regenerate them on your end unless you modify the files in `rust/`.
## Regenerating the bindings

To regenerate bindings after changing the Rust API, make sure LLVM/Clang is installed and run:
The FFI bindings are committed; there is no need to regenerate them unless you modify the files in `rust/`. Regeneration needs LLVM/Clang (libclang) and `cargo` on `PATH`, plus `rustfmt` for the pinned toolchain, which the minimal profile in `rust/rust-toolchain.toml` does not install:

```bash
dart run tool/generate_bindings.dart
rustup component add rustfmt --toolchain 1.91.0
```

The script detects LLVM through `llvm-config`. If it is installed in a custom location, set `LLVM_PATH` or pass the path directly to `flutter_rust_bridge_codegen` with `--llvm-path`.
Without it the generator still succeeds but writes an unformatted `rust/src/frb_generated.rs`, which shows up as a large spurious diff.

```bash
cd tool
dart run generate_bindings.dart
```

The script locates the package root (the parent of `tool/`), installs the `flutter_rust_bridge_codegen` version matching the `flutter_rust_bridge` pin in `pubspec.yaml` (built with `cargo install --locked`) into the package root's `.dart_tool/frb_bin` on first use, then regenerates the package's `lib/src/` and `rust/src/frb_generated.rs`. It is a separate Dart package on purpose: running it does not trigger this package's build hook, so it keeps working when a Rust API change has made the previously generated `frb_generated.rs` uncompilable.

The script detects LLVM through `llvm-config`. If LLVM is installed in a custom location, set `LLVM_PATH` or pass the path directly to `flutter_rust_bridge_codegen` with `--llvm-path`.

## Usage

In your app's `main` entry point, be sure to initialize the RustLib. This will enable the use of imported bindings anywhere in your app.
Expand All @@ -60,6 +81,7 @@ Here is an example of using the api crate through FFI to create a Xelis Wallet i
```dart
import 'package:xelis_flutter/src/api/wallet.dart' as x_wallet;
import 'package:xelis_flutter/src/api/network.dart' as x_network;
import 'package:xelis_flutter/src/api/precomputed_tables.dart' as x_tables;

Future<void> createXelisWallet() async {
final String name = "example-wallet";
Expand All @@ -72,7 +94,8 @@ Future<void> createXelisWallet() async {
password: password,
network: x_network.Network.mainnet,
precomputedTablesPath: directory, // precomputed tables will be written to this location
l1Low: true, // will generate lightweight precomputed tables, use false for full-sized tables
// lightweight precomputed tables; see PrecomputedTableType for the other variants
precomputedTableType: const x_tables.PrecomputedTableType.l1Low(),
);

final mnemonic = await wallet.getSeed();
Expand Down
8 changes: 2 additions & 6 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
analyzer:
exclude:
- build/**
- android/**
- ios/**
- web/**
- windows/**
- macos/**
- linux/**
# Separate Dart package (maintainer script); analyzed on its own in CI.
- tool/**
include: package:flutter_lints/flutter.yaml

# Additional information about this file can be found at
Expand Down
9 changes: 0 additions & 9 deletions android/.gitignore

This file was deleted.

Loading
Loading