Flutter Lightning Wallet (Rust FFI)

A high-performance Lightning wallet library for Flutter, built with Rust and using dart:ffi for native bindings. This library provides Bitcoin and Lightning Network functionality through a unified Dart API.

Features

  • High Performance: All wallet logic runs in native Rust code
  • Bitcoin Wallet: BIP39/BIP32 HD wallet with native address derivation
  • Lightning Network: LDK-node integration for Lightning payments
  • UTXO Management: Native UTXO tracking with caching
  • Transaction Building: Coin selection and transaction signing
  • Payment History: SQLite-based payment database
  • Event Streaming: Real-time wallet events
  • Cross-platform: Supports Android, iOS, macOS, Linux, and Windows

Architecture

lightening_wallet/
├── rust/               # Rust core library
│   ├── src/
│   │   ├── lib.rs           # C FFI bindings
│   │   ├── coordinator.rs   # Main orchestrator
│   │   ├── wallet/          # Bitcoin wallet logic
│   │   ├── ldk/             # LDK-node integration
│   │   ├── storage/         # SQLite & caching
│   │   └── events.rs        # Event system
│   └── Cargo.toml
├── lib/                # Dart API
│   ├── lightening_wallet.dart  # Main export
│   └── src/
│       ├── bindings.dart     # FFI bindings
│       ├── lightning_wallet.dart  # High-level API
│       └── models.dart       # Data models
├── android/            # Android plugin config
├── ios/                # iOS plugin config & headers
├── macos/              # macOS plugin config & headers
├── linux/              # Linux plugin config (CMake)
├── windows/            # Windows plugin config (CMake)
└── scripts/            # Build scripts

Installation

Prerequisites

  • Flutter 3.10+
  • Rust 1.70+ with cargo
  • Android NDK (if building for Android)
  • Xcode (if building for iOS/macOS)
  • cargo-ndk for Android builds: cargo install cargo-ndk
  • zig and cargo-zigbuild for cross-compiling Linux/Windows from macOS:
    brew install zig
    cargo install cargo-zigbuild
    

Add to your project

Add to your pubspec.yaml:

dependencies:
  lightening_wallet:
    path: /path/to/lightening_wallet

Or if published to pub.dev:

dependencies:
  lightening_wallet: ^0.1.42

Build the native library

Before using the plugin, you need to build the Rust library for your target platforms.

Android

# Using the build script (recommended)
./scripts/build-android.sh

# Or manually with cargo-ndk
cd rust
cargo ndk -t arm64-v8a -t armeabi-v7a -t x86_64 -t x86 \
  -o ../android/src/main/jniLibs build --release

Requirements:

  • Android NDK installed (set ANDROID_NDK_HOME environment variable)
  • cargo-ndk installed: cargo install cargo-ndk
  • Rust Android targets:
    rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android i686-linux-android
    

iOS

# Using the build script (recommended)
./scripts/build-ios.sh

This will:

  1. Build for iOS device (aarch64-apple-ios)
  2. Build for iOS simulators (x86_64-apple-ios, aarch64-apple-ios-sim)
  3. Create a universal XCFramework

Requirements:

  • Xcode with command line tools
  • Rust iOS targets:
    rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
    

macOS

./scripts/build-macos.sh

This builds a universal static library (arm64 + x86_64) at macos/liblightening_wallet_macos.a.

Requirements:

  • Xcode with command line tools
  • Rust macOS targets:
    rustup target add aarch64-apple-darwin x86_64-apple-darwin
    

Linux

./scripts/build-linux.sh

This builds linux/liblightening_wallet.so. When cross-compiling from macOS, uses cargo-zigbuild.

Requirements:

  • Rust Linux target:
    rustup target add x86_64-unknown-linux-gnu
    
  • For cross-compilation from macOS: zig and cargo-zigbuild

Windows

./scripts/build-windows.sh

This builds windows/lightening_wallet.dll. When cross-compiling from macOS, uses cargo-zigbuild.

Requirements:

  • Rust Windows target:
    rustup target add x86_64-pc-windows-gnu
    
  • For cross-compilation from macOS: zig and cargo-zigbuild

Usage

Initialize the library

import 'package:lightening_wallet/lightening_wallet.dart';

void main() {
  // Initialize the native library (required before any other calls)
  LightningWallet.initialize();

  runApp(MyApp());
}

Generate a new wallet

// Generate a new BIP39 mnemonic
final result = LightningWallet.generateMnemonic();
if (result.success) {
  final mnemonic = result.data!;
  print('Mnemonic: $mnemonic');
}

Initialize wallet

final initResult = LightningWallet.initializeWallet(
  userId: 'user123',
  mnemonic: mnemonic,
  network: BitcoinNetwork.testnet,
  dbPath: '/path/to/wallet/data',
);

if (initResult.success) {
  print('Wallet initialized!');
} else {
  print('Error: ${initResult.error}');
}

Get balance

final balanceResult = LightningWallet.getBalance('user123');
if (balanceResult.success) {
  final balance = balanceResult.data!;
  print('On-chain confirmed: ${balance.onchainConfirmed} sats');
  print('Lightning balance: ${balance.lightningBalance} sats');
  print('Total: ${balance.total} sats');
}

Sync wallet

final syncResult = LightningWallet.sync('user123');
if (syncResult.success) {
  print('Wallet synced!');
}

Get receiving address

final addressResult = LightningWallet.getReceivingAddress('user123');
if (addressResult.success) {
  print('Receive to: ${addressResult.data}');
}

List payment history

final paymentsResult = LightningWallet.listPayments(
  'user123',
  limit: 10,
  offset: 0,
);

if (paymentsResult.success) {
  for (final payment in paymentsResult.data!) {
    print('${payment.paymentType}: ${payment.amountSats} sats');
  }
}

Poll for events

final eventsResult = LightningWallet.getEvents('user123');
if (eventsResult.success) {
  for (final event in eventsResult.data!) {
    switch (event.eventType) {
      case 'BalanceUpdated':
        print('Balance updated!');
        break;
      case 'PaymentReceived':
        print('Payment received!');
        break;
      case 'SyncCompleted':
        print('Sync completed!');
        break;
    }
  }
}

Disconnect wallet

LightningWallet.disconnect('user123');

API Reference

Dart API (LightningWallet)

All methods are static and return a WalletResponse<T> wrapper with success, data, and error fields.


Wallet Lifecycle

static void initialize()

Initialize the native library bindings. Must be called before any other methods.

LightningWallet.initialize();
static WalletResponse<String> generateMnemonic()

Generate a new 24-word BIP39 mnemonic phrase.

Returns: Space-separated string of 24 BIP39 words.

static WalletResponse<void> initializeWallet({...})

Initialize a wallet instance (blocking).

Parameters:

Parameter Type Required Description
userId String Yes Unique identifier for this wallet instance
mnemonic String Yes 24-word BIP39 mnemonic phrase
network BitcoinNetwork Yes bitcoin, testnet, or regtest
dbPath String Yes Path to store wallet data
esploraUrl String? No Custom Esplora server URL
static WalletResponse<void> initializeWalletAsync({...})

Initialize a wallet instance (non-blocking). Use getInitStatus() to poll for completion.

Parameters: Same as initializeWallet().

static WalletResponse<InitStatus> getInitStatus(String userId)

Get the current initialization status for async initialization.

Returns: InitStatus with status (not_started, pending, initializing, completed, failed), progress, and error.

static WalletResponse<void> clearInitStatus(String userId)

Clear the initialization status (for retry after failure).

static WalletResponse<void> disconnect(String userId)

Disconnect and cleanup the wallet. Stops the Lightning node and releases resources.


Balance and Sync

static WalletResponse<WalletBalance> getBalance(String userId)

Get the current wallet balance.

Returns: WalletBalance with:

  • onchain - On-chain balance in satoshis
  • lightning - Lightning channel balance in satoshis
  • total - Sum of both balances
static WalletResponse<void> sync(String userId)

Sync the wallet with the blockchain (blocking).

static WalletResponse<String> syncAsync(String userId)

Sync the wallet (non-blocking). Returns operation ID.

static WalletResponse<void> restartNode(String userId)

Restart the Lightning node to reconnect peers. Useful after opening new channels.


Addresses

static WalletResponse<String> getReceivingAddress(String userId)

Generate a fresh on-chain receiving address (BIP84 native SegWit).

Returns: A bech32 Bitcoin address.

static WalletResponse<String> getNodeId(String userId)

Get the Lightning node's public key.

Returns: 33-byte hex-encoded public key.


Payment History

static WalletResponse<List<Payment>> listPayments(String userId, {int? limit, int? offset})

List payment history from database.

Parameters:

Parameter Type Required Description
limit int? No Maximum payments to return (0 = all)
offset int? No Number of payments to skip

Returns: List of Payment objects.

static WalletResponse<List<Payment>> getOnchainTransactions(String userId)

Get on-chain transaction history by querying Esplora directly.

static WalletResponse<String> getOnchainTransactionsAsync(String userId)

Get on-chain transactions (non-blocking). Returns operation ID.


Events

static WalletResponse<List<WalletEvent>> getEvents(String userId)

Drain pending wallet events from the event queue. Events are removed after being returned.

Returns: List of WalletEvent with eventType, data, and timestamp.

Event Types:

  • BalanceUpdated - Balance changed
  • PaymentReceived - Incoming payment completed
  • PaymentSent - Outgoing payment completed
  • PaymentFailed - Payment failed
  • ChannelOpened - New channel opened
  • ChannelClosed - Channel closed
  • SyncCompleted - Blockchain sync completed

Peer Management

static WalletResponse<void> connectPeer(String userId, {required String nodeId, required String address, required int port})

Connect to a Lightning Network peer (blocking).

Parameters:

Parameter Type Required Description
nodeId String Yes 33-byte hex-encoded public key
address String Yes IP address or hostname
port int Yes TCP port (typically 9735)
static WalletResponse<String> connectPeerAsync(String userId, {required String nodeId, required String address, required int port})

Connect to a peer (non-blocking). Returns operation ID.

static WalletResponse<void> disconnectPeer(String userId, String nodeId)

Disconnect from a Lightning Network peer.

static WalletResponse<List<Peer>> listPeers(String userId)

List all connected peers.

Returns: List of Peer with nodeId, address, port, and isConnected.


Channel Management

static WalletResponse<String> openChannel(String userId, {...})

Open a new Lightning channel (blocking).

Parameters:

Parameter Type Required Description
counterpartyNodeId String Yes Peer's 33-byte hex public key
channelValueSats int Yes Total channel capacity in sats
pushMsat int No Amount to push to peer (default: 0)
peerAddress String? No Peer address if not connected
peerPort int? No Peer port if not connected

Returns: Hex-encoded channel ID.

static WalletResponse<String> openChannelAsync(String userId, {...})

Open a channel (non-blocking). Returns operation ID. Same parameters as openChannel().

static WalletResponse<void> closeChannel(String userId, String channelId, {bool force = false})

Close a Lightning channel (blocking).

Parameters:

Parameter Type Required Description
channelId String Yes Hex-encoded channel ID
force bool No Force close unilaterally (default: false)
static WalletResponse<String> closeChannelAsync(String userId, String channelId, {bool force = false, String? peerAddress, int? peerPort})

Close a channel (non-blocking). Returns operation ID. Optionally provide peer address for cooperative close.

static WalletResponse<List<Channel>> listChannels(String userId)

List all Lightning channels.

Returns: List of Channel with:

  • channelId - Unique channel identifier
  • counterpartyNodeId - Peer's public key
  • channelValueSats - Total channel capacity
  • balanceSats - Local balance
  • inboundCapacitySats - Available inbound liquidity
  • outboundCapacitySats - Available outbound liquidity
  • isUsable - Whether channel can route payments
  • isPublic - Whether channel is announced to network
  • shortChannelId - Short channel ID (if confirmed)

Invoice Management

static WalletResponse<InvoiceInfo> createInvoice(String userId, {int? amountSats, String? description, int expirySecs = 3600})

Create a BOLT11 Lightning invoice.

Parameters:

Parameter Type Required Description
amountSats int? No Invoice amount (null for "any amount")
description String? No Human-readable description
expirySecs int No Expiry time in seconds (default: 3600)

Returns: InvoiceInfo with bolt11, paymentHash, amountSats, description, createdAt, expiresAt.


Payment Operations

static WalletResponse<PaymentResult> payInvoice(String userId, String bolt11, {int? amountSats})

Pay a BOLT11 Lightning invoice (blocking).

Parameters:

Parameter Type Required Description
bolt11 String Yes BOLT11-encoded invoice
amountSats int? No Amount for "any amount" invoices

Returns: PaymentResult with payment details.

static WalletResponse<String> payInvoiceAsync(String userId, String bolt11, {int? amountSats})

Pay an invoice (non-blocking). Returns operation ID.

static WalletResponse<PaymentResult> sendKeysend(String userId, {required String destinationPubkey, required int amountSats, Map<int, List<int>>? customRecords})

Send a spontaneous keysend payment (blocking).

Parameters:

Parameter Type Required Description
destinationPubkey String Yes Destination node's 33-byte hex public key
amountSats int Yes Amount to send in satoshis
customRecords Map<int, List<int>>? No TLV custom records (Podcasting 2.0)

Podcasting 2.0 TLV Types:

const podcastTlv = 7629169;    // Podcast name
const episodeTlv = 7629171;    // Episode GUID
const actionTlv = 7629173;     // Action (stream/boost)
const timestampTlv = 7629175;  // Timestamp in episode
const appNameTlv = 7629177;    // App name
static WalletResponse<String> sendKeysendAsync(String userId, {...})

Send keysend (non-blocking). Returns operation ID. Same parameters as sendKeysend().

static WalletResponse<PaymentResult> payLightningAddress(String userId, {required String lightningAddress, required int amountSats, String? comment})

Pay a Lightning Address (LNURL-pay) (blocking).

Parameters:

Parameter Type Required Description
lightningAddress String Yes Lightning Address (e.g., user@domain.com)
amountSats int Yes Amount to send in satoshis
comment String? No Optional payment comment
static WalletResponse<String> payLightningAddressAsync(String userId, {...})

Pay Lightning Address (non-blocking). Returns operation ID.

static WalletResponse<String> sendOnchain(String userId, {required String address, required int amountSats, int feeRateSatPerVbyte = 0})

Send an on-chain Bitcoin transaction (blocking).

Parameters:

Parameter Type Required Description
address String Yes Destination Bitcoin address
amountSats int Yes Amount to send in satoshis
feeRateSatPerVbyte int No Fee rate (0 = use default)

Returns: Transaction ID (txid) as hex string.

static WalletResponse<String> sendOnchainAsync(String userId, {...})

Send on-chain (non-blocking). Returns operation ID.


Fee Estimation

static WalletResponse<FeeEstimate> estimateOnchainFee(String userId, {required int amountSats, required int feeRateSatPerVbyte})

Estimate the fee for an on-chain transaction.

Returns: FeeEstimate with:

  • feeSats - Estimated fee in satoshis
  • feeRateSatPerVbyte - Fee rate used
  • estimatedVbytes - Estimated transaction size
  • totalSats - Total amount including fee
static WalletResponse<RecommendedFeeRates> getRecommendedFeeRates(String userId)

Get recommended fee rates for different confirmation speeds (blocking).

Returns: RecommendedFeeRates with:

  • fastSatPerVbyte - Fast (1-2 blocks)
  • mediumSatPerVbyte - Medium (3-6 blocks)
  • slowSatPerVbyte - Slow (7+ blocks)
static WalletResponse<String> getRecommendedFeeRatesAsync(String userId)

Get fee rates (non-blocking). Returns operation ID.


Async Operation Management

All *Async methods return an operation ID. Use these methods to poll for results:

static WalletResponse<OperationStatus> getOperationStatus(String operationId)

Get the status of an async operation.

Returns: OperationStatus with:

  • operationId - The operation ID
  • operationType - Type of operation
  • status - pending, running, completed, failed, not_found
  • progress - Progress message (if available)
  • result - Result data (if completed)
  • error - Error message (if failed)

Helper properties:

  • isPending, isRunning, isCompleted, isFailed, isNotFound
  • isInProgress - true if pending or running
  • isDone - true if completed or failed
static WalletResponse<void> clearOperationStatus(String operationId)

Clear the status of an operation to free memory. Call after you're done polling.

Types

WalletResponse

Generic response wrapper for all API calls.

class WalletResponse<T> {
  final bool success;   // Whether the operation succeeded
  final T? data;        // Result data (if success)
  final String? error;  // Error message (if failed)
}

WalletBalance

Balance information for the wallet.

class WalletBalance {
  final int onchain;     // On-chain balance in satoshis
  final int lightning;   // Lightning channel balance in satoshis
  int get total;         // Sum of both balances
}

InitStatus

Initialization status for async wallet initialization.

class InitStatus {
  final String status;    // "not_started", "pending", "initializing", "completed", "failed"
  final String? progress; // Progress message
  final String? error;    // Error message (if failed)

  // Helper getters
  bool get isNotStarted;
  bool get isPending;
  bool get isInitializing;
  bool get isCompleted;
  bool get isFailed;
  bool get isInProgress;  // true if pending or initializing
}

Payment

Payment record for both Lightning and on-chain transactions.

class Payment {
  final String paymentHash;    // Unique payment identifier
  final String paymentType;    // 'sent', 'received', 'onchain_sent', 'onchain_received'
  final String status;         // 'pending', 'completed', 'failed'
  final int amountSats;        // Payment amount in satoshis
  final int? feeSats;          // Fee paid (if applicable)
  final String? bolt11;        // Original BOLT11 invoice
  final String? preimage;      // Lightning payment preimage
  final int timestamp;         // Unix timestamp
  final String? description;   // Payment description/memo
  final String? destination;   // Destination node ID or address
  final String? txid;          // On-chain transaction ID
}

Channel

Lightning channel information.

class Channel {
  final String channelId;           // Unique channel identifier
  final String counterpartyNodeId;  // Peer's public key
  final int channelValueSats;       // Total channel capacity
  final int balanceSats;            // Local balance
  final int inboundCapacitySats;    // Available for receiving
  final int outboundCapacitySats;   // Available for sending
  final bool isUsable;              // Can route payments
  final bool isPublic;              // Announced to network
  final String? shortChannelId;     // Short channel ID (if confirmed)
}

Peer

Connected Lightning peer information.

class Peer {
  final String nodeId;      // Peer's public key
  final String? address;    // IP address or hostname
  final int? port;          // TCP port
  final bool isConnected;   // Connection status
}

InvoiceInfo

BOLT11 invoice creation result.

class InvoiceInfo {
  final String bolt11;         // BOLT11-encoded invoice
  final String paymentHash;    // Unique payment identifier
  final int? amountSats;       // Invoice amount (null for "any amount")
  final String? description;   // Invoice description
  final int createdAt;         // Unix timestamp of creation
  final int expiresAt;         // Unix timestamp of expiry
}

PaymentResult

Payment result from paying an invoice or sending keysend.

class PaymentResult {
  final String paymentHash;    // Unique payment identifier
  final String paymentType;    // 'sent', 'received', etc.
  final int amountSats;        // Payment amount
  final int? feeSats;          // Fee paid
  final String status;         // 'pending', 'completed', 'failed'
  final int timestamp;         // Unix timestamp
  final String? description;   // Payment description
  final String? destination;   // Destination node/address
  final String? preimage;      // Payment preimage (proof of payment)
  final String? bolt11;        // Original invoice
}

FeeEstimate

On-chain fee estimate.

class FeeEstimate {
  final int feeSats;              // Estimated fee in satoshis
  final int feeRateSatPerVbyte;   // Fee rate used (sat/vbyte)
  final int estimatedVbytes;      // Estimated transaction size
  final int totalSats;            // Total amount including fee
}

RecommendedFeeRates

Recommended fee rates for different confirmation speeds.

class RecommendedFeeRates {
  final int fastSatPerVbyte;    // Fast (1-2 blocks)
  final int mediumSatPerVbyte;  // Medium (3-6 blocks)
  final int slowSatPerVbyte;    // Slow (7+ blocks)
}

WalletEvent

Real-time wallet event.

class WalletEvent {
  final String eventType;           // Event type string
  final Map<String, dynamic> data;  // Event-specific data
  final int timestamp;              // Unix timestamp
}

OperationStatus

Status of an async operation.

class OperationStatus {
  final String operationId;     // The operation ID
  final String operationType;   // Type of operation
  final String status;          // "pending", "running", "completed", "failed", "not_found"
  final String? progress;       // Progress message
  final dynamic result;         // Result data (if completed)
  final String? error;          // Error message (if failed)

  // Helper getters
  bool get isPending;
  bool get isRunning;
  bool get isCompleted;
  bool get isFailed;
  bool get isNotFound;
  bool get isInProgress;  // true if pending or running
  bool get isDone;        // true if completed or failed
}

BitcoinNetwork

Supported Bitcoin networks.

enum BitcoinNetwork {
  bitcoin,   // Mainnet (real funds)
  testnet,   // Testnet (test funds)
  regtest,   // Regtest (local testing)
}

Rust FFI Reference

The native Rust library exposes C-compatible FFI functions. All functions return JSON strings with the format:

{"success": true, "data": {...}}
// or
{"success": false, "error": "error message"}

Core Functions

C Function Parameters Description
wallet_initialize user_id, mnemonic, network, db_path Initialize wallet instance
wallet_generate_mnemonic None Generate 24-word BIP39 mnemonic
wallet_get_balance user_id Get wallet balance
wallet_sync user_id Sync with blockchain
wallet_get_receiving_address user_id Get new receiving address
wallet_list_payments user_id, limit, offset List payment history
wallet_get_events user_id Drain pending events
wallet_disconnect user_id Disconnect and cleanup
wallet_free_string ptr Free allocated string memory

Peer Functions

C Function Parameters Description
wallet_connect_peer user_id, node_id, address, port Connect to peer
wallet_disconnect_peer user_id, node_id Disconnect from peer
wallet_list_peers user_id List connected peers

Channel Functions

C Function Parameters Description
wallet_open_channel user_id, counterparty_node_id, channel_value_sats, push_msat, peer_address, peer_port Open channel
wallet_close_channel user_id, channel_id, force Close channel
wallet_list_channels user_id List all channels

Invoice Functions

C Function Parameters Description
wallet_create_invoice user_id, amount_sats, description, expiry_secs Create BOLT11 invoice

Payment Functions

C Function Parameters Description
wallet_pay_invoice user_id, bolt11, amount_sats Pay BOLT11 invoice
wallet_send_keysend user_id, destination_pubkey, amount_sats, custom_records_json Send keysend payment
wallet_send_onchain user_id, address, amount_sats Send on-chain transaction
wallet_get_node_id user_id Get node public key

Android JNI Functions

For Android, the library also exports JNI-compatible functions:

JNI Function Equivalent C Function
Java_..._nativeInitialize wallet_initialize
Java_..._nativeGenerateMnemonic wallet_generate_mnemonic
Java_..._nativeGetBalance wallet_get_balance
Java_..._nativeSyncWallet wallet_sync
Java_..._nativeGetReceivingAddress wallet_get_receiving_address
Java_..._nativeListPayments wallet_list_payments
Java_..._nativeGetEvents wallet_get_events
Java_..._nativeDisconnect wallet_disconnect

Rust Module Reference

WalletCoordinator (coordinator.rs)

Main orchestrator that manages wallet components.

impl WalletCoordinator {
    pub fn new(db_path: &str) -> Result<Self, CoordinatorError>;
    pub fn initialize(&self, mnemonic: &str, network: BitcoinNetwork) -> Result<(), CoordinatorError>;
    pub fn sync(&self) -> Result<(), CoordinatorError>;
    pub fn get_lightning_node(&self) -> Arc<LightningNode>;
    pub fn get_database(&self) -> Arc<Database>;
    pub fn get_event_emitter(&self) -> Arc<EventEmitter>;
    pub fn disconnect(&self) -> Result<(), CoordinatorError>;
}

KeyManager (wallet/keys.rs)

BIP39/BIP32 HD wallet key management.

impl KeyManager {
    pub fn from_mnemonic(mnemonic: &str, network: Network) -> Result<Self, KeyError>;
    pub fn generate_mnemonic() -> String;
    pub fn get_lightning_seed(&self) -> Result<[u8; 32], KeyError>;  // Derives at m/535'/0'
}

LightningNode (ldk/node.rs)

LDK-node wrapper for Lightning Network operations.

impl LightningNode {
    // Lifecycle
    pub fn new() -> Self;
    pub fn initialize(&self, entropy: [u8; 32], network: Network, storage_path: &str, esplora_server: Option<&str>) -> Result<(), LightningError>;
    pub fn start(&self) -> Result<(), LightningError>;
    pub fn stop(&self) -> Result<(), LightningError>;
    pub fn sync_wallets(&self) -> Result<(), LightningError>;

    // Peer Management
    pub fn connect_peer(&self, params: ConnectPeerParams) -> Result<(), LightningError>;
    pub fn disconnect_peer(&self, node_id: &str) -> Result<(), LightningError>;
    pub fn list_peers(&self) -> Result<Vec<PeerInfo>, LightningError>;
    pub fn get_node_id(&self) -> Result<String, LightningError>;

    // Channel Management
    pub fn open_channel(&self, params: OpenChannelParams) -> Result<String, LightningError>;
    pub fn close_channel(&self, params: CloseChannelParams) -> Result<(), LightningError>;
    pub fn list_channels(&self) -> Result<Vec<ChannelInfo>, LightningError>;

    // Invoices
    pub fn create_invoice(&self, params: CreateInvoiceParams) -> Result<InvoiceInfo, LightningError>;

    // Payments
    pub fn pay_invoice(&self, params: PayInvoiceParams) -> Result<PaymentInfo, LightningError>;
    pub fn send_keysend(&self, params: KeysendParams) -> Result<PaymentInfo, LightningError>;
    pub fn send_onchain(&self, params: SendOnChainParams) -> Result<String, LightningError>;
    pub fn list_payments(&self) -> Result<Vec<PaymentInfo>, LightningError>;

    // Balance
    pub fn get_onchain_address(&self) -> Result<String, LightningError>;
    pub fn get_spendable_onchain_balance(&self) -> Result<u64, LightningError>;
    pub fn get_total_onchain_balance(&self) -> Result<u64, LightningError>;
}

Database (storage/db.rs)

SQLite storage for payment history.

impl Database {
    pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, DatabaseError>;
    pub fn list_payments(&self, limit: Option<usize>, offset: Option<usize>) -> Result<Vec<Payment>, DatabaseError>;
}

EventEmitter (events.rs)

Bounded event queue for real-time notifications.

impl EventEmitter {
    pub fn new(capacity: usize) -> Self;
    pub fn emit(&self, event: WalletEvent);
    pub fn drain_events(&self) -> Vec<WalletEvent>;
}

Podcasting20Builder (ldk/podcasting.rs)

Fluent builder for Podcasting 2.0 value-for-value TLV records.

impl Podcasting20Builder {
    pub fn new() -> Self;
    pub fn podcast(self, name: &str) -> Self;        // TLV 7629169
    pub fn episode(self, guid: &str) -> Self;        // TLV 7629171
    pub fn action(self, action: &str) -> Self;       // TLV 7629173
    pub fn timestamp(self, timestamp: u64) -> Self;  // TLV 7629175
    pub fn app_name(self, name: &str) -> Self;       // TLV 7629177
    pub fn custom(self, key: u64, value: &str) -> Self;
    pub fn custom_bytes(self, key: u64, value: Vec<u8>) -> Self;
    pub fn build(self) -> HashMap<u64, Vec<u8>>;
}

// Usage example:
let custom_records = Podcasting20Builder::new()
    .podcast("My Podcast")
    .episode("episode-guid-123")
    .action("stream")
    .app_name("MyApp")
    .build();

Error Types

CoordinatorError

pub enum CoordinatorError {
    AlreadyInitialized,           // Wallet already initialized
    KeyError(String),             // Key derivation/management error
    StorageError(String),         // Database/storage error
    LightningError(String),       // Lightning node error
}

KeyError

pub enum KeyError {
    InvalidMnemonic(String),      // Invalid BIP39 mnemonic
    Derivation(String),           // BIP32 derivation failed
    InvalidNetwork(String),       // Network mismatch
}

LightningError

pub enum LightningError {
    NotInitialized,               // Node not initialized
    AlreadyInitialized,           // Node already initialized
    LdkError(String),             // LDK-node error
    NetworkError(String),         // Network communication error
    PeerError(String),            // Peer connection error
    ChannelError(String),         // Channel operation error
    PaymentError(String),         // Payment failed
    InvoiceError(String),         // Invoice creation/parsing error
    StorageError(String),         // Storage error
}

DatabaseError

pub enum DatabaseError {
    Connection(String),           // Database connection failed
    Query(String),                // Query execution error
    NotFound(String),             // Record not found
}

Development

Run Rust tests

cd rust
cargo test

Build Rust library (debug)

cd rust
cargo build

Format Rust code

cd rust
cargo fmt

Lint Rust code

cd rust
cargo clippy

Regenerate FFI bindings

If you modify the C header file, regenerate bindings:

dart run ffigen

Troubleshooting

Android: Library not found

Make sure you've built the native library and the .so files are in android/src/main/jniLibs/:

android/src/main/jniLibs/
├── arm64-v8a/liblightening_wallet.so
├── armeabi-v7a/liblightening_wallet.so
├── x86_64/liblightening_wallet.so
└── x86/liblightening_wallet.so

iOS: Framework not found

Ensure the XCFramework was built correctly:

./scripts/build-ios.sh

Check that ios/LighteningWallet.xcframework exists.

macOS: Library not found

Ensure the static library was built:

./scripts/build-macos.sh

Check that macos/liblightening_wallet_macos.a exists.

iOS/macOS CI Builds: "Build input file cannot be found"

When building in CI environments, you may encounter this error:

Build input file cannot be found: '.../LighteningWallet.xcframework/ios-arm64/liblightening_wallet.a'

Cause: The podspec uses a CocoaPods script_phase to download the native binaries from GitLab releases before compilation. However, the user_target_xcconfig linker flags reference these files, and Xcode validates that referenced files exist before running script phases.

Workaround: Download the native binaries in your CI pipeline before running flutter build or pod install. Add this to your CI configuration:

For iOS:

# After flutter pub get, before pod install
LW_VERSION=$(grep -A 10 "lightening_wallet:" pubspec.lock | grep "version:" | head -1 | awk '{print $2}' | tr -d '"')
PLUGIN_DIR="$HOME/.pub-cache/hosted/pub.dev/lightening_wallet-${LW_VERSION}/ios"
if [ ! -d "$PLUGIN_DIR/LighteningWallet.xcframework" ]; then
  curl -L -f "https://gitlab.com/elementfm/flutter-lightening-wallet-rs/-/releases/v${LW_VERSION}/downloads/LighteningWallet.xcframework.zip" -o "$PLUGIN_DIR/framework.zip"
  unzip -o "$PLUGIN_DIR/framework.zip" -d "$PLUGIN_DIR"
  rm "$PLUGIN_DIR/framework.zip"
fi

For macOS:

# After flutter pub get, before build
LW_VERSION=$(grep -A 10 "lightening_wallet:" pubspec.lock | grep "version:" | head -1 | awk '{print $2}' | tr -d '"')
PLUGIN_DIR="$HOME/.pub-cache/hosted/pub.dev/lightening_wallet-${LW_VERSION}/macos"
if [ ! -f "$PLUGIN_DIR/liblightening_wallet_macos.a" ]; then
  curl -L -f "https://gitlab.com/elementfm/flutter-lightening-wallet-rs/-/releases/v${LW_VERSION}/downloads/liblightening_wallet_macos.a" -o "$PLUGIN_DIR/liblightening_wallet_macos.a"
fi

This is only needed for CI. Local development builds work correctly because the script phase has time to download the binaries before subsequent builds reference them.

Linux: Library not found

Ensure the shared library was built:

./scripts/build-linux.sh

Check that linux/liblightening_wallet.so exists.

Windows: DLL not found

Ensure the DLL was built:

./scripts/build-windows.sh

Check that windows/lightening_wallet.dll exists.

License

MIT

Credits

This library integrates and builds upon:

Libraries

lightening_wallet
High-performance Lightning wallet for Flutter using Rust FFI with LDK-node.