Skip to main content
Glama
createChannel.tsโ€ข8.21 kB
import { Tool, ToolResult } from '../types/tool.js'; import { LightningService } from '../services/lightningService.js'; const lightningService = new LightningService(); export const createChannelTool: Tool = { name: 'ldk_create_channel', description: 'Open a Lightning channel with a peer node', inputSchema: { type: 'object', properties: { remotePubkey: { type: 'string', description: 'Remote node public key (hex encoded)' }, capacitySats: { type: 'number', description: 'Channel capacity in satoshis', minimum: 20000 }, pushSats: { type: 'number', description: 'Amount to push to remote side (optional)', default: 0 }, isPublic: { type: 'boolean', description: 'Whether to announce channel publicly', default: false } }, required: ['remotePubkey', 'capacitySats'] }, execute: async (args: any): Promise<ToolResult> => { try { const channel = await lightningService.createChannel( args.remotePubkey, args.capacitySats * 1000, // Convert to millisats args.pushSats * 1000 ); return { content: [{ type: 'text', text: JSON.stringify({ success: true, channel: { channelId: channel.channelId, shortChannelId: channel.shortChannelId, fundingTxid: channel.fundingTxid, capacitySats: Math.floor(channel.capacityMsat / 1000), localBalanceSats: Math.floor(channel.localBalanceMsat / 1000), remoteBalanceSats: Math.floor(channel.remoteBalanceMsat / 1000), state: channel.state }, swiftExample: ` // Swift code to open a channel in your iOS app using LDK import LightningDevKit import BitcoinDevKit class ChannelOpener { let channelManager: Bindings.ChannelManager let wallet: BitcoinDevKit.Wallet let logger: Bindings.Logger func openChannel( remotePubkey: String, capacitySats: UInt64, pushSats: UInt64 = 0, isPublic: Bool = false ) async throws -> Bindings.ChannelId { // Parse remote pubkey guard let pubkeyData = Data(hexString: remotePubkey), pubkeyData.count == 33 else { throw ChannelError.invalidPubkey } // Configure channel let userConfig = Bindings.UserConfig.initWithDefault() let channelHandshakeConfig = Bindings.ChannelHandshakeConfig.initWithDefault() channelHandshakeConfig.setAnnouncedChannel(val: isPublic) channelHandshakeConfig.setMinimumDepth(val: 3) // 3 confirmations userConfig.setChannelHandshakeConfig(val: channelHandshakeConfig) // Generate user channel ID (16 bytes) let userChannelId = Array<UInt8>(repeating: 0, count: 16) arc4random_buf(&userChannelId, 16) // Create channel with proper error handling let result = channelManager.createChannel( theirNetworkKey: pubkeyData.bytes, channelValueSatoshis: capacitySats, pushMsat: pushSats * 1000, userChannelId: userChannelId, temporaryChannelId: nil, overrideConfig: userConfig ) guard result.isOk() else { let error = result.getError()! throw ChannelError.creationFailed(error.getValueAsApiMisuseError()?.getErrMessage() ?? "Unknown error") } return result.getValue()! } // Handle funding generation event func handleFundingGeneration(event: Bindings.Event.FundingGenerationReady) async throws { let outputScript = Script(rawOutputScript: event.getOutputScript()) let amount = event.getChannelValueSatoshis() // Build funding transaction with BDK let txBuilder = try TxBuilder() .addRecipient(script: outputScript, amount: amount) .feeRate(satPerVbyte: 5.0) .enableRbf() let psbt = try txBuilder.finish(wallet: wallet) let signed = try wallet.sign(psbt: psbt, signOptions: nil) let fundingTx = signed.extractTx() // Provide funding transaction to LDK channelManager.fundingTransactionGenerated( temporaryChannelId: event.getTemporaryChannelId(), counterpartyNodeId: event.getCounterpartyNodeId(), fundingTransaction: fundingTx.serialize() ) // Broadcast transaction try await broadcastTransaction(fundingTx) } } // SwiftUI view for channel opening struct OpenChannelView: View { @State private var remotePubkey = "" @State private var capacitySats = "100000" @State private var pushSats = "0" @State private var isPublic = false @State private var isOpening = false @State private var error: String? var body: some View { Form { Section("Channel Details") { TextField("Remote Node Pubkey", text: $remotePubkey) .font(.system(.body, design: .monospaced)) .textInputAutocapitalization(.never) .autocorrectionDisabled() HStack { TextField("Capacity (sats)", text: $capacitySats) .keyboardType(.numberPad) Text("sats") .foregroundColor(.secondary) } HStack { TextField("Push Amount (sats)", text: $pushSats) .keyboardType(.numberPad) Text("sats") .foregroundColor(.secondary) } Toggle("Public Channel", isOn: $isPublic) } Section("Fee Estimate") { HStack { Label("On-chain Fee", systemImage: "bitcoinsign.circle") Spacer() Text("~500 sats") .foregroundColor(.secondary) } } Section { Button(action: openChannel) { if isOpening { HStack { ProgressView() .scaleEffect(0.8) Text("Opening Channel...") } } else { Text("Open Channel") } } .frame(maxWidth: .infinity) .disabled(isOpening || !isValid) } } .navigationTitle("Open Channel") .alert("Error", isPresented: .constant(error != nil)) { Button("OK") { error = nil } } message: { Text(error ?? "") } } var isValid: Bool { !remotePubkey.isEmpty && Int(capacitySats) ?? 0 >= 20000 && Int(pushSats) ?? 0 >= 0 } func openChannel() { Task { isOpening = true defer { isOpening = false } do { let capacity = UInt64(capacitySats) ?? 0 let push = UInt64(pushSats) ?? 0 try await LDKManager.shared.openChannel( remotePubkey: remotePubkey, capacitySats: capacity, pushSats: push, isPublic: isPublic ) // Navigate back or show success } catch { self.error = error.localizedDescription } } } }`.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 }; } } };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/StevenGeller/ldk-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server