Skip to content

Commit

Permalink
extension: process bip21
Browse files Browse the repository at this point in the history
  • Loading branch information
reez committed Aug 25, 2024
1 parent e622eff commit 6be02aa
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 10 deletions.
75 changes: 67 additions & 8 deletions LDKNodeMonday/Extensions/String+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -180,15 +180,74 @@ extension String {
return params
}

func processBIP21(_ input: String, spendableBalance: UInt64) -> (String, String, Payment) {
print("Processing BIP21 URI")
guard let url = URL(string: input),
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
else {
print("Failed to parse BIP21 URI")
return ("", "0", .isNone)
}

let bitcoinAddress = url.path
var amount = "0"
var bolt12Offer: String?
var bolt11Invoice: String?

for item in components.queryItems ?? [] {
switch item.name.lowercased() {
case "amount":
if let value = item.value, let btcAmount = Double(value) {
amount = String(format: "%.0f", btcAmount * 100_000_000) // Convert BTC to satoshis
}
case "lightning":
bolt11Invoice = item.value
case "lno":
bolt12Offer = item.value
default:
break
}
}

// Priority: Bolt 12 > Bolt 11 > On-chain
if let offer = bolt12Offer {
print("Bolt 12 offer found in BIP21 URI")
return processLightningAddress(offer)
}

if let invoice = bolt11Invoice {
print("Bolt 11 invoice found in BIP21 URI")
return processLightningAddress(invoice)
}

print("Using on-chain Bitcoin address from BIP21 URI")
return (bitcoinAddress, amount, .isBitcoin)
}

func extractPaymentInfo(spendableBalance: UInt64) -> (
address: String, amount: String, payment: Payment
) {
let queryParams = self.queryParameters()
// BIP 21
if self.lowercased().starts(with: "bitcoin:") && self.contains("?") {
return processBIP21(self, spendableBalance: spendableBalance)
}

// Bolt 11 JIT
// Check for BOLT11 invoice, including those prefixed with "lightning:"
if self.lowercased().starts(with: "lightning:") {
let invoice = String(self.dropFirst(10)) // Remove "lightning:" prefix
return processLightningAddress(invoice)
} else if self.lowercased().starts(with: "lnbc") || self.lowercased().starts(with: "lntb") {
return processLightningAddress(self)
}

if let lightningAddress = queryParams["lightning"], !lightningAddress.isEmpty {
return processLightningAddress(lightningAddress)
} else if self.isBitcoinAddress {
return processBitcoinAddress(spendableBalance) // Modified to handle BIP21
// let queryParams = self.queryParameters()
//
// if let lightningAddress = queryParams["lightning"], !lightningAddress.isEmpty {
// return processLightningAddress(lightningAddress)
// }
else if self.isBitcoinAddress {
return processBitcoinAddress(spendableBalance)
} else if self.starts(with: "lnurl") {
return ("LNURL not supported yet", "0", .isLightningURL)
} else {
Expand All @@ -197,9 +256,9 @@ extension String {
}

private func processBitcoinAddress(_ spendableBalance: UInt64) -> (String, String, Payment) {
let address = self.extractBitcoinAddress() // Modified for BIP21 extraction
let address = self.extractBitcoinAddress()
let queryParams = self.queryParameters()
let amount = queryParams["amount"] ?? "0" // Modified: Handling BIP21 amount only
let amount = queryParams["amount"] ?? "0"

// Validate the amount against the spendable balance
if let amountValue = UInt64(amount), amountValue <= spendableBalance {
Expand All @@ -212,7 +271,7 @@ extension String {
private func processLightningAddress(_ address: String) -> (String, String, Payment) {
let sanitizedAddress = address.replacingOccurrences(of: "lightning:", with: "")

if sanitizedAddress.starts(with: "lno") {
if sanitizedAddress.lowercased().starts(with: "lno") {
return (sanitizedAddress, "0", .isLightning)
} else {
let amount = sanitizedAddress.bolt11amount() ?? "0"
Expand Down
7 changes: 5 additions & 2 deletions LDKNodeMonday/View Model/Home/AmountViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,10 @@ class AmountViewModel {

func sendPaymentBolt12(invoice: Bolt12Invoice) async {
do {
try await LightningNodeService.shared.send(bolt12Invoice: invoice)
let paymentId = try await LightningNodeService.shared.send(bolt12Invoice: invoice)
print("sendPaymentBolt12 success w paymentId: \(paymentId)")
} catch let error as NodeError {
print("sendPaymentBolt12 NodeError: \(error.localizedDescription)")
NotificationCenter.default.post(name: .ldkErrorReceived, object: error)
let errorString = handleNodeError(error)
DispatchQueue.main.async {
Expand All @@ -102,6 +104,7 @@ class AmountViewModel {
}
} catch {
DispatchQueue.main.async {
print("sendPaymentBolt12 error: \(error.localizedDescription)")
self.amountConfirmationViewError = .init(
title: "Unexpected error",
detail: error.localizedDescription
Expand All @@ -118,7 +121,7 @@ class AmountViewModel {
}

func handleLightningPayment(address: String, numpadAmount: String) async {
if address.starts(with: "lno") {
if address.lowercased().starts(with: "lno") {
await sendPaymentBolt12(invoice: address)
} else if address.bolt11amount() == "0" {
if let amountSats = UInt64(numpadAmount) {
Expand Down

0 comments on commit 6be02aa

Please sign in to comment.