Skip to content

Repository files navigation

bull_sdk

Unified flutter_rust_bridge bindings for Bitcoin and Liquid wallet operations.

Why

Over the past years, we contributed to the open source community by creating flutter_rust_bridge bindings for several Bitcoin/Liquid libraries:

Each of these packages works standalone and is used by other projects in the community.

However, our wallet app Bull Bitcoin depends on all of them. This creates two problems:

  1. Build time — Each package compiles its own native library (.so/.dylib). Building them all individually is slow, especially for Android where each targets 3 architectures.
  2. Binary size — Shared Rust dependencies (bitcoin, secp256k1, tokio, openssl, etc.) are duplicated across each native library.

Approach

bull_sdk is a single flutter_rust_bridge package that generates unified Dart bindings by scanning the Rust API of each sub-crate as external dependencies:

# flutter_rust_bridge.yaml
rust_input: crate::api, ark_wallet::ark, bbqr, dart_bbqr::api, boltz::api, lwk::api

This produces one native library containing all the Rust code, with one FRB dispatcher handling all FFI calls.

Key constraints

  • Standalone packages are preserved — Each sub-crate remains a fully functional standalone flutter_rust_bridge package. The community can keep using boltz-dart or lwk-dart independently.
  • Sub-crate frb_generated is cfg-gated — When used as a dependency of bull_sdk, each sub-crate's frb_generated.rs is disabled via #[cfg(not(feature = "bull_sdk"))] to avoid duplicate trait implementations. bull_sdk provides its own unified frb_generated.rs.
  • Mirror types for data-variant enums — Rust enums with data (like TxFee, ArkTransaction) become opaque when scanned as external crate types. We use #[frb(mirror)] to generate proper sealed Dart classes.
  • Post-processing — After FRB codegen, fix_frb_generated.sh patches the generated Rust code to wrap error types in FrbWrapper and convert mirrored types via .into().

Structure

bull-sdk/
├── Cargo.toml                          # Cargo workspace
├── packages/
│   ├── bull_sdk/                       # Unified FRB package (single native library)
│   │   ├── flutter_rust_bridge.yaml
│   │   ├── fix_frb_generated.sh        # Post-processing script
│   │   ├── rust/                       # Bridge crate
│   │   └── lib/
│   │       ├── bull_sdk.dart           # BullSdk.init()
│   │       ├── ark.dart                # Ark wallet types
│   │       ├── bbqr.dart               # BBQr types
│   │       ├── boltz.dart              # Boltz swap types
│   │       └── lwk.dart                # Liquid Wallet Kit types
│   ├── ark-wallet/                     # git submodule → SatoshiPortal/ark-wallet-dart
│   ├── bbqr/                           # git submodule → SatoshiPortal/bbqr-dart
│   ├── boltz/                          # git submodule → SatoshiPortal/boltz-dart
│   ├── lwk/                            # git submodule → SatoshiPortal/lwk-dart
│   ├── boltz-stream/                   # Pure Dart — BoltzWebSocket (depends on bull_sdk)
│   └── satoshifier/                    # git submodule → SatoshiPortal/dart-satoshifier

Regenerating bindings

cd packages/bull_sdk
flutter_rust_bridge_codegen generate
bash fix_frb_generated.sh
cargo check -p rust_lib_bull_sdk

Always run fix_frb_generated.sh after codegen — it patches error type wrapping and mirror type conversions that FRB cannot handle automatically for external crate types.

Usage

import 'package:bull_sdk/bull_sdk.dart';
import 'package:bull_sdk/boltz.dart' as boltz;
import 'package:bull_sdk/lwk.dart' as lwk;
import 'package:bull_sdk/bbqr.dart' as bbqr;
import 'package:bull_sdk/ark.dart' as ark;

await BullSdk.init();

About

Unified flutter_rust_bridge bindings for Bull Bitcoin wallet

Resources

Stars

Watchers

Forks

Contributors

Languages