ldk_derive_address
Generate Bitcoin addresses from a BIP39 mnemonic using BIP84 derivation path. Specify account index, address index, change flag, and network for precise address creation.
Instructions
Derive Bitcoin addresses from seed using BIP84
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| accountIndex | No | Account index (default: 0) | |
| addressIndex | No | Address index (default: 0) | |
| isChange | No | Is change address (default: false) | |
| mnemonic | Yes | BIP39 mnemonic phrase | |
| network | No | Bitcoin network | testnet |
Input Schema (JSON Schema)
{
"properties": {
"accountIndex": {
"default": 0,
"description": "Account index (default: 0)",
"type": "number"
},
"addressIndex": {
"default": 0,
"description": "Address index (default: 0)",
"type": "number"
},
"isChange": {
"default": false,
"description": "Is change address (default: false)",
"type": "boolean"
},
"mnemonic": {
"description": "BIP39 mnemonic phrase",
"type": "string"
},
"network": {
"default": "testnet",
"description": "Bitcoin network",
"enum": [
"mainnet",
"testnet",
"regtest"
],
"type": "string"
}
},
"required": [
"mnemonic"
],
"type": "object"
}
Implementation Reference
- src/tools/deriveAddress.ts:40-280 (handler)The main handler function for the 'ldk_derive_address' tool. It converts mnemonic to seed, derives the address using WalletService, and returns the result including a Swift code example.execute: async (args: any): Promise<ToolResult> => { try { const seed = walletService.mnemonicToSeed(args.mnemonic); const derivation = walletService.deriveAddress( seed, args.accountIndex || 0, args.isChange || false, args.addressIndex || 0 ); return { content: [{ type: 'text', text: JSON.stringify({ success: true, address: derivation.address, publicKey: derivation.publicKey, derivationPath: derivation.derivationPath, network: args.network || 'testnet', swiftExample: ` // Swift code for address derivation in your iOS app import BitcoinDevKit import LightningDevKit class AddressManager { private let wallet: BitcoinDevKit.Wallet private let network: BitcoinDevKit.Network init(mnemonic: [String], network: BitcoinDevKit.Network = .testnet) throws { self.network = network // Create descriptors let mnemonicStr = mnemonic.joined(separator: " ") let secretKey = DescriptorSecretKey( network: network, mnemonic: Mnemonic(mnemonic: mnemonicStr), password: nil ) // BIP84 descriptors (native segwit) let descriptor = Descriptor( descriptor: "wpkh(\\(secretKey.asString())/84'/\\(network == .bitcoin ? "0" : "1")'/0'/0/*)", network: network ) let changeDescriptor = Descriptor( descriptor: "wpkh(\\(secretKey.asString())/84'/\\(network == .bitcoin ? "0" : "1")'/0'/1/*)", network: network ) // Initialize wallet let walletDB = DatabaseConfig.memory wallet = try BitcoinDevKit.Wallet( descriptor: descriptor, changeDescriptor: changeDescriptor, network: network, databaseConfig: walletDB ) } func getNewAddress() throws -> AddressInfo { let addressInfo = try wallet.getAddress(addressIndex: .new) return AddressInfo( address: addressInfo.address.asString(), index: addressInfo.index, keychain: addressInfo.keychain, isChange: addressInfo.keychain == .external ? false : true ) } func getAddress(at index: UInt32, isChange: Bool = false) throws -> String { let keychain: KeychainKind = isChange ? .internal : .external let addressInfo = try wallet.getAddress(addressIndex: .peek(index: index)) return addressInfo.address.asString() } func validateAddress(_ address: String) -> Bool { do { _ = try Address(address: address, network: network) return true } catch { return false } } } struct AddressInfo { let address: String let index: UInt32 let keychain: KeychainKind let isChange: Bool } // SwiftUI view for address management struct AddressManagerView: View { @State private var addresses: [AddressInfo] = [] @State private var showingNewAddress = false @State private var selectedAddress: AddressInfo? @State private var copiedAddress: String? var body: some View { List { Section { Button(action: generateNewAddress) { Label("Generate New Address", systemImage: "plus.circle") .foregroundColor(.accentColor) } } Section("Receive Addresses") { ForEach(addresses.filter { !$0.isChange }, id: \\.address) { address in AddressRow( address: address, isCopied: copiedAddress == address.address, onCopy: { copyAddress(address.address) } ) } } } .navigationTitle("Addresses") .task { loadAddresses() } .sheet(isPresented: $showingNewAddress) { if let address = selectedAddress { NewAddressView(addressInfo: address) { showingNewAddress = false selectedAddress = nil loadAddresses() } } } } func generateNewAddress() { do { let addressManager = try AddressManager( mnemonic: LDKManager.shared.getMnemonic() ) let newAddress = try addressManager.getNewAddress() selectedAddress = newAddress showingNewAddress = true } catch { // Handle error } } func loadAddresses() { // Load existing addresses from wallet do { let addressManager = try AddressManager( mnemonic: LDKManager.shared.getMnemonic() ) var loadedAddresses: [AddressInfo] = [] // Load up to 20 addresses for i in 0..<20 { let address = try addressManager.getAddress(at: UInt32(i)) loadedAddresses.append(AddressInfo( address: address, index: UInt32(i), keychain: .external, isChange: false )) } addresses = loadedAddresses } catch { // Handle error } } func copyAddress(_ address: String) { UIPasteboard.general.string = address copiedAddress = address // Reset after 2 seconds DispatchQueue.main.asyncAfter(deadline: .now() + 2) { if copiedAddress == address { copiedAddress = nil } } } } struct AddressRow: View { let address: AddressInfo let isCopied: Bool let onCopy: () -> Void var body: some View { VStack(alignment: .leading, spacing: 8) { HStack { Text("Index \\(address.index)") .font(.caption) .foregroundColor(.secondary) Spacer() if isCopied { Label("Copied", systemImage: "checkmark") .font(.caption) .foregroundColor(.green) } } HStack { Text(address.address) .font(.system(.caption, design: .monospaced)) .lineLimit(1) .truncationMode(.middle) Button(action: onCopy) { Image(systemName: "doc.on.doc") .font(.caption) } .buttonStyle(.borderless) } } .padding(.vertical, 4) } }`.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/deriveAddress.ts:9-39 (schema)Input schema defining the parameters for deriving a Bitcoin address: mnemonic (required), accountIndex, addressIndex, isChange, network.inputSchema: { type: 'object', properties: { mnemonic: { type: 'string', description: 'BIP39 mnemonic phrase' }, accountIndex: { type: 'number', description: 'Account index (default: 0)', default: 0 }, addressIndex: { type: 'number', description: 'Address index (default: 0)', default: 0 }, isChange: { type: 'boolean', description: 'Is change address (default: false)', default: false }, network: { type: 'string', enum: ['mainnet', 'testnet', 'regtest'], description: 'Bitcoin network', default: 'testnet' } }, required: ['mnemonic'] },
- src/index.ts:38-62 (registration)Registration of the deriveAddressTool in the main tools array used by the MCP server for tool listing and execution.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, ];
- src/services/walletService.ts:30-66 (helper)Core helper function in WalletService that derives BIP84 native segwit Bitcoin addresses from seed, used by the tool handler.deriveAddress( seed: Buffer, accountIndex: number = 0, isChange: boolean = false, addressIndex: number = 0 ): { address: string; publicKey: string; privateKey: string; derivationPath: string; } { const root = bip32.fromSeed(seed, this.network); // BIP84 (native segwit) derivation path const purpose = 84; const coinType = this.network === bitcoin.networks.bitcoin ? 0 : 1; const change = isChange ? 1 : 0; const path = `m/${purpose}'/${coinType}'/${accountIndex}'/${change}/${addressIndex}`; const child = root.derivePath(path); const { address } = bitcoin.payments.p2wpkh({ pubkey: child.publicKey, network: this.network }); if (!address) { throw new Error('Failed to derive address'); } return { address, publicKey: child.publicKey.toString('hex'), privateKey: child.privateKey!.toString('hex'), derivationPath: path }; }
- src/services/walletService.ts:23-28 (helper)Helper function in WalletService to convert BIP39 mnemonic to seed buffer, used in the address derivation process.mnemonicToSeed(mnemonic: string, passphrase: string = ''): Buffer { if (!bip39.validateMnemonic(mnemonic)) { throw new Error('Invalid mnemonic'); } return bip39.mnemonicToSeedSync(mnemonic, passphrase); }