lightening_wallet 0.3.0 copy "lightening_wallet: ^0.3.0" to clipboard
lightening_wallet: ^0.3.0 copied to clipboard

High-performance Lightning wallet for Flutter using Rust FFI with LDK-node. Supports Bitcoin on-chain and Lightning Network payments.

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 and iOS

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
└── scripts/            # Build scripts

Installation #

Prerequisites #

  • Flutter 3.10+
  • Rust 1.70+ with cargo
  • Android NDK (if building for Android)
  • Xcode (if building for iOS)
  • cargo-ndk for Android builds: cargo install cargo-ndk

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
    

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) #

Wallet Initialization & Management

static void initialize()

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

static WalletResponse<String> generateMnemonic()

Generate a new 24-word BIP39 mnemonic phrase using cryptographically secure randomness.

Returns: A space-separated string of 24 BIP39 words.

static WalletResponse<void> initializeWallet({required String userId, required String mnemonic, required BitcoinNetwork network, required String dbPath})

Initialize a wallet instance with mnemonic and network configuration.

Parameters:

  • userId - Unique identifier for this wallet instance (supports multiple concurrent wallets)
  • mnemonic - 24-word BIP39 mnemonic phrase
  • network - Target network (BitcoinNetwork.bitcoin for mainnet, BitcoinNetwork.testnet for testnet)
  • dbPath - Path to store wallet data (SQLite database, LDK state)
static WalletResponse<WalletBalance> getBalance(String userId)

Get the current wallet balance including on-chain and Lightning funds.

Returns: WalletBalance with onchainConfirmed, onchainUnconfirmed, lightningBalance, and total (all in satoshis).

static WalletResponse<void> sync(String userId)

Sync the wallet with the blockchain. Updates on-chain balance, processes Lightning events, and refreshes channel states.

static WalletResponse<String> getReceivingAddress(String userId)

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

Returns: A bech32 Bitcoin address.

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

List payment history with optional pagination. Returns both Lightning and on-chain transactions.

Parameters:

  • limit - Maximum number of payments to return (optional)
  • offset - Number of payments to skip for pagination (optional)
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 objects representing balance updates, payment notifications, sync status, etc.

static WalletResponse<void> disconnect(String userId)

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


Peer Management

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

Connect to a Lightning Network peer.

Parameters:

  • nodeId - 33-byte hex-encoded public key of the peer
  • address - IP address or hostname of the peer
  • port - TCP port (typically 9735)
static WalletResponse<void> disconnectPeer(String userId, {required String nodeId})

Disconnect from a Lightning Network peer.

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

List all connected peers.

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


Channel Management

static WalletResponse<String> openChannel(String userId, {required String counterpartyNodeId, required int channelValueSats, int pushMsat = 0, String? peerAddress, int? peerPort})

Open a new Lightning channel with a peer.

Parameters:

  • counterpartyNodeId - 33-byte hex-encoded public key of the channel partner
  • channelValueSats - Total channel capacity in satoshis
  • pushMsat - Amount (in millisatoshis) to push to the counterparty on open (default: 0)
  • peerAddress - Optional peer address if not already connected
  • peerPort - Optional peer port if not already connected

Returns: Hex-encoded channel ID.

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

Close a Lightning channel.

Parameters:

  • channelId - Hex-encoded channel ID to close
  • force - If true, force close the channel unilaterally (use only if peer is unresponsive)
static WalletResponse<List<ChannelInfo>> listChannels(String userId)

List all Lightning channels.

Returns: List of ChannelInfo with detailed channel state including:

  • channelId - Unique channel identifier
  • counterpartyNodeId - Peer's public key
  • channelValueSats - Total channel capacity
  • balanceSats - Local balance
  • outboundCapacitySats - Available outbound liquidity
  • inboundCapacitySats - Available inbound liquidity
  • isUsable - Whether channel can route payments
  • isPublic - Whether channel is announced to network
  • isReady - Whether channel is fully confirmed
  • isClosing - Whether channel is being closed
  • confirmationsRequired - Block confirmations needed (if pending)

Invoice Management

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

Create a BOLT11 Lightning invoice.

Parameters:

  • amountSats - Invoice amount in satoshis (optional for "any amount" invoices)
  • description - Human-readable description (optional)
  • expirySecs - Invoice expiry time in seconds (default: 3600 = 1 hour)

Returns: InvoiceInfo with:

  • bolt11 - BOLT11-encoded invoice string
  • paymentHash - Unique payment identifier
  • amountSats - Invoice amount (if specified)
  • description - Invoice description
  • createdAt - Unix timestamp of creation
  • expiresAt - Unix timestamp of expiry

Payment Operations

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

Pay a BOLT11 Lightning invoice.

Parameters:

  • bolt11 - BOLT11-encoded invoice string
  • amountSats - Amount to pay (required for "any amount" invoices, optional otherwise)

Returns: PaymentInfo with payment status and details.

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

Send a spontaneous keysend payment (no invoice required).

Parameters:

  • destinationPubkey - 33-byte hex-encoded destination public key
  • amountSats - Amount to send in satoshis
  • customRecords - Optional TLV custom records (for Podcasting 2.0, etc.)

Returns: PaymentInfo with payment status.

Podcasting 2.0 Support: Use custom TLV records for value-for-value streaming payments:

// Standard TLV types for Podcasting 2.0
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> sendOnchain(String userId, {required String address, required int amountSats})

Send an on-chain Bitcoin transaction.

Parameters:

  • address - Destination Bitcoin address
  • amountSats - Amount to send in satoshis

Returns: Transaction ID (txid) as hex string.

static WalletResponse<String> getNodeId(String userId)

Get this wallet's Lightning node public key.

Returns: 33-byte hex-encoded public key.

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 #

Comprehensive balance information across on-chain and Lightning.

class WalletBalance {
  final int onchainConfirmed;    // Confirmed on-chain balance (sats)
  final int onchainUnconfirmed;  // Unconfirmed on-chain balance (sats)
  final int lightningBalance;    // Total Lightning channel balance (sats)
  final int total;               // Sum of all balances (sats)
}

Payment #

Payment record for both Lightning and on-chain transactions.

class Payment {
  final int? id;               // Database ID
  final String paymentHash;    // Unique payment identifier
  final String paymentType;    // 'sent', 'received', 'onchain_sent', 'onchain_received'
  final int amountSats;        // Payment amount in satoshis
  final int? feeSats;          // Fee paid (if applicable)
  final String status;         // 'pending', 'completed', 'failed'
  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
  final String? preimage;      // Lightning payment preimage
  final String? bolt11;        // Original BOLT11 invoice
}

ChannelInfo #

Detailed Lightning channel state information.

class ChannelInfo {
  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 outboundCapacitySats;   // Available for sending
  final int inboundCapacitySats;    // Available for receiving
  final bool isUsable;              // Can route payments
  final bool isPublic;              // Announced to network
  final bool isReady;               // Fully confirmed
  final bool isClosing;             // Being closed
  final int? confirmationsRequired; // Blocks until ready
}

PeerInfo #

Connected Lightning peer information.

class PeerInfo {
  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 details.

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
}

PaymentInfo #

Payment result information.

class PaymentInfo {
  final String paymentHash;    // Unique payment identifier
  final String paymentType;    // 'sent', 'received', 'onchain_sent', 'onchain_received'
  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
}

WalletEvent #

Real-time wallet event types.

// Event types returned by getEvents()
enum WalletEventType {
  balanceUpdated,       // Balance changed
  paymentReceived,      // Incoming payment completed
  paymentSent,          // Outgoing payment completed
  paymentFailed,        // Payment failed
  channelOpened,        // New channel opened
  channelClosed,        // Channel closed
  syncStarted,          // Blockchain sync started
  syncCompleted,        // Blockchain sync completed
  syncFailed,           // Blockchain sync failed
  transactionConfirmed, // On-chain tx confirmed
  error,                // General error
}

BitcoinNetwork #

Supported Bitcoin networks.

enum BitcoinNetwork {
  bitcoin,   // Mainnet (real funds)
  testnet,   // Testnet (test funds)
}

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.

Platform not supported #

The library only supports Android and iOS. Desktop platforms are not currently supported.

License #

MIT

Credits #

This library integrates and builds upon:

0
likes
0
points
589
downloads

Publisher

unverified uploader

Weekly Downloads

High-performance Lightning wallet for Flutter using Rust FFI with LDK-node. Supports Bitcoin on-chain and Lightning Network payments.

Repository (GitHub)
View/report issues

Topics

#bitcoin #lightning #wallet #payments #ffi

License

unknown (license)

Dependencies

ffi, flutter, path

More

Packages that depend on lightening_wallet

Packages that implement lightening_wallet