ldk_get_balance
Retrieve Lightning wallet balance and channel liquidity to monitor funds and optimize transactions using the LDK MCP Server for iOS Lightning wallet development.
Instructions
Get Lightning wallet balance and channel liquidity
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Input Schema (JSON Schema)
{
"properties": {},
"type": "object"
}
Implementation Reference
- src/tools/getBalance.ts:13-251 (handler)Core execution logic for 'ldk_get_balance' tool: retrieves wallet balance and channel statuses via LightningService, calculates inbound liquidity from usable channels, formats response as JSON with balance/liquidity details and embedded SwiftUI example for iOS integration.execute: async (args: any): Promise<ToolResult> => { try { const balance = await lightningService.getBalance(); const channels = await lightningService.getChannelStatus(); // Calculate inbound liquidity let inboundMsat = 0; for (const channel of channels) { if (channel.isUsable) { inboundMsat += channel.remoteBalanceMsat; } } return { content: [{ type: 'text', text: JSON.stringify({ success: true, balance: { totalSats: Math.floor(balance.totalMsat / 1000), spendableSats: Math.floor(balance.spendableMsat / 1000), inboundSats: Math.floor(inboundMsat / 1000) }, liquidity: { canSendMaxSats: Math.floor(balance.spendableMsat / 1000), canReceiveMaxSats: Math.floor(inboundMsat / 1000) }, swiftExample: ` // Swift code to display balance in your iOS app import SwiftUI import LightningDevKit struct BalanceView: View { @State private var balance: WalletBalance? @State private var isLoading = true var body: some View { VStack(spacing: 24) { // Total balance card VStack(spacing: 16) { HStack { Image(systemName: "bitcoinsign.circle.fill") .font(.largeTitle) .foregroundColor(.orange) Spacer() VStack(alignment: .trailing) { Text(formatSats(balance?.totalSats ?? 0)) .font(.system(size: 32, weight: .bold, design: .rounded)) Text("sats") .font(.caption) .foregroundColor(.secondary) } } // Balance breakdown VStack(spacing: 8) { BalanceDetailRow( label: "Spendable", amount: balance?.spendableSats ?? 0, icon: "paperplane.fill", color: .green ) BalanceDetailRow( label: "Receivable", amount: balance?.inboundSats ?? 0, icon: "arrow.down.circle.fill", color: .blue ) } } .padding() .background( LinearGradient( colors: [Color.orange.opacity(0.1), Color.orange.opacity(0.05)], startPoint: .topLeading, endPoint: .bottomTrailing ) ) .cornerRadius(16) // Quick actions HStack(spacing: 16) { QuickActionButton( title: "Send", icon: "paperplane.fill", color: .green, isEnabled: (balance?.spendableSats ?? 0) > 0 ) { // Navigate to send view } QuickActionButton( title: "Receive", icon: "qrcode", color: .blue, isEnabled: (balance?.inboundSats ?? 0) > 0 ) { // Navigate to receive view } } Spacer() } .padding() .navigationTitle("Balance") .task { await loadBalance() } .refreshable { await loadBalance() } .overlay { if isLoading { ProgressView() } } } func loadBalance() async { isLoading = true defer { isLoading = false } balance = await LDKManager.shared.getBalance() } func formatSats(_ sats: Int64) -> String { let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.groupingSeparator = "," return formatter.string(from: NSNumber(value: sats)) ?? "0" } } struct BalanceDetailRow: View { let label: String let amount: Int64 let icon: String let color: Color var body: some View { HStack { Label(label, systemImage: icon) .foregroundColor(color) .font(.callout) Spacer() Text(formatAmount(amount)) .font(.callout) .fontWeight(.medium) .foregroundColor(.secondary) } } func formatAmount(_ sats: Int64) -> String { let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.groupingSeparator = "," return (formatter.string(from: NSNumber(value: sats)) ?? "0") + " sats" } } struct QuickActionButton: View { let title: String let icon: String let color: Color let isEnabled: Bool let action: () -> Void var body: some View { Button(action: action) { VStack(spacing: 8) { Image(systemName: icon) .font(.title2) Text(title) .font(.caption) } .frame(maxWidth: .infinity) .padding() .background(color.opacity(isEnabled ? 0.15 : 0.05)) .foregroundColor(isEnabled ? color : .gray) .cornerRadius(12) } .disabled(!isEnabled) } } // Extension for LDK balance fetching extension LDKManager { func getBalance() async -> WalletBalance { let channels = channelManager.listChannels() var totalMsat: UInt64 = 0 var spendableMsat: UInt64 = 0 var inboundMsat: UInt64 = 0 for channel in channels { if channel.isUsable { let localBalance = channel.balanceMsat let remoteBalance = (channel.channelValueSatoshis * 1000) - localBalance totalMsat += localBalance spendableMsat += max(0, localBalance - (channel.channelValueSatoshis * 10)) // Reserve 1% inboundMsat += remoteBalance } } return WalletBalance( totalSats: Int64(totalMsat / 1000), spendableSats: Int64(spendableMsat / 1000), inboundSats: Int64(inboundMsat / 1000) ) } } struct WalletBalance { let totalSats: Int64 let spendableSats: Int64 let inboundSats: Int64 }`.trim() }, null, 2) }] }; } catch (error) { return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : 'Unknown error' }, null, 2) }], isError: true }; } }
- src/tools/getBalance.ts:9-12 (schema)Input schema definition for the tool, specifying an empty object as no input parameters are required.inputSchema: { type: 'object', properties: {} },
- src/index.ts:24-62 (registration)Registers the 'ldk_get_balance' tool by importing getBalanceTool and including it in the central tools array used by MCP server handlers for tool listing and execution.import { getBalanceTool } from './tools/getBalance.js'; import { decodeInvoiceTool } from './tools/decodeInvoice.js'; import { listPaymentsTool } from './tools/listPayments.js'; import { estimateFeeTool } from './tools/estimateFee.js'; import { generateMnemonicTool } from './tools/generateMnemonic.js'; import { deriveAddressTool } from './tools/deriveAddress.js'; import { getSwiftCodeTool } from './tools/getSwiftCode.js'; import { getArchitectureTool } from './tools/getArchitecture.js'; import { testScenarioTool } from './tools/testScenario.js'; import { networkGraphTool } from './tools/networkGraph.js'; import { eventHandlingTool } from './tools/eventHandling.js'; import { chainSyncTool } from './tools/chainSync.js'; // Aggregate all tools const tools = [ generateInvoiceTool, payInvoiceTool, getChannelStatusTool, getNodeInfoTool, backupStateTool, keychainTestTool, backgroundTestTool, pushNotificationTool, biometricAuthTool, createChannelTool, closeChannelTool, getBalanceTool, decodeInvoiceTool, listPaymentsTool, estimateFeeTool, generateMnemonicTool, deriveAddressTool, getSwiftCodeTool, getArchitectureTool, testScenarioTool, networkGraphTool, eventHandlingTool, chainSyncTool, ];
- Supporting getBalance method in LightningService that sums local balances across open channels, applying a 1% reserve on usable channels for spendable amount.async getBalance(): Promise<{ totalMsat: number; spendableMsat: number }> { let totalMsat = 0; let spendableMsat = 0; for (const channel of this.channels.values()) { if (channel.state === ChannelState.Open) { totalMsat += channel.localBalanceMsat; if (channel.isUsable) { // Reserve 1% for fees spendableMsat += Math.floor(channel.localBalanceMsat * 0.99); } } } return { totalMsat, spendableMsat }; }