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-ndkfor Android builds:cargo install cargo-ndkzigandcargo-zigbuildfor 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_HOMEenvironment variable) cargo-ndkinstalled: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:
- Build for iOS device (aarch64-apple-ios)
- Build for iOS simulators (x86_64-apple-ios, aarch64-apple-ios-sim)
- 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:
zigandcargo-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:
zigandcargo-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 satoshislightning- Lightning channel balance in satoshistotal- 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 changedPaymentReceived- Incoming payment completedPaymentSent- Outgoing payment completedPaymentFailed- Payment failedChannelOpened- New channel openedChannelClosed- Channel closedSyncCompleted- 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 identifiercounterpartyNodeId- Peer's public keychannelValueSats- Total channel capacitybalanceSats- Local balanceinboundCapacitySats- Available inbound liquidityoutboundCapacitySats- Available outbound liquidityisUsable- Whether channel can route paymentsisPublic- Whether channel is announced to networkshortChannelId- 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 satoshisfeeRateSatPerVbyte- Fee rate usedestimatedVbytes- Estimated transaction sizetotalSats- 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 IDoperationType- Type of operationstatus-pending,running,completed,failed,not_foundprogress- Progress message (if available)result- Result data (if completed)error- Error message (if failed)
Helper properties:
isPending,isRunning,isCompleted,isFailed,isNotFoundisInProgress- true if pending or runningisDone- 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.