use cdk::wallet::bip321::Bip321UriBuilder;
use tracing::instrument;
use crate::error::FfiError;
use crate::types::bip321::{BitcoinNetwork, ParsedPaymentInstruction};
pub(crate) fn apply_optional_lightning_methods(
mut builder: Bip321UriBuilder,
bolt11: Option<String>,
bolt12: Option<String>,
) -> Bip321UriBuilder {
if let Some(invoice) = bolt11 {
builder = builder.with_bolt11_invoice(invoice);
}
if let Some(offer) = bolt12 {
builder = builder.with_bolt12_offer(offer);
}
builder
}
#[uniffi::export]
pub fn create_bip321_uri(
creq: Option<String>,
bolt11: Option<String>,
bolt12: Option<String>,
) -> String {
let mut builder = Bip321UriBuilder::new();
if let Some(creq) = creq {
builder = builder.with_cashu_request_str(creq);
}
builder = apply_optional_lightning_methods(builder, bolt11, bolt12);
builder.to_string()
}
#[uniffi::export(async_runtime = "tokio")]
#[instrument(skip_all)]
pub async fn parse_bip321_payment_instruction(
instruction: String,
network: BitcoinNetwork,
) -> Result<ParsedPaymentInstruction, FfiError> {
let parsed =
cdk::wallet::bip321::parse_payment_instruction(&instruction, network.into()).await?;
Ok(parsed.into())
}
#[cfg(all(feature = "bip353", not(target_arch = "wasm32")))]
#[uniffi::export(async_runtime = "tokio")]
#[instrument(skip_all)]
pub async fn resolve_bip353_payment_instruction(
wallet: std::sync::Arc<crate::wallet::Wallet>,
address: String,
network: BitcoinNetwork,
) -> Result<ParsedPaymentInstruction, FfiError> {
let client = wallet.inner().mint_connector();
let parsed =
cdk::wallet::resolve_bip353_payment_instruction(&client, &address, network.into()).await?;
Ok(parsed.into())
}
#[cfg(test)]
mod tests {
use super::*;
const TEST_CREQ: &str = "CREQB1QYQQWER9D4HNZV3NQGQQSQQQQQQQQQQRAQPSQQGQQSQQZQG9QQVXSAR5WPEN5TE0D45KUAPWV4UXZMTSD3JJUCM0D5RQQRJRDANXVET9YPCXZ7TDV4H8GXHR3TQ";
const TEST_BOLT11: &str = "lnbc100n1ptest";
fn assert_uri_prefix(uri: &str) {
assert!(uri.starts_with("BITCOIN:?"));
}
#[test]
fn test_create_bip321_uri_cashu_and_bolt11() {
let uri = create_bip321_uri(
Some(TEST_CREQ.to_string()),
Some(TEST_BOLT11.to_string()),
None,
);
assert_uri_prefix(&uri);
assert!(uri.contains(&format!("CREQ={TEST_CREQ}")));
assert!(uri.contains(&format!("LIGHTNING={}", TEST_BOLT11.to_ascii_uppercase())));
}
#[test]
fn test_create_bip321_uri_cashu_only() {
let uri = create_bip321_uri(Some(TEST_CREQ.to_string()), None, None);
assert_uri_prefix(&uri);
assert!(uri.contains("CREQ="));
assert!(!uri.contains("LIGHTNING="));
assert!(!uri.contains("LNO="));
}
#[test]
fn test_create_bip321_uri_bolt11_only() {
let uri = create_bip321_uri(None, Some(TEST_BOLT11.to_string()), None);
assert_uri_prefix(&uri);
assert!(uri.contains(&format!("LIGHTNING={}", TEST_BOLT11.to_ascii_uppercase())));
assert!(!uri.contains("CREQ="));
}
#[test]
fn test_create_bip321_uri_all_methods() {
let bolt12 = "lno1qgsqtest";
let uri = create_bip321_uri(
Some(TEST_CREQ.to_string()),
Some(TEST_BOLT11.to_string()),
Some(bolt12.to_string()),
);
assert_uri_prefix(&uri);
assert!(uri.contains("CREQ="));
assert!(uri.contains("LIGHTNING="));
assert!(uri.contains("LNO="));
}
#[test]
fn test_create_bip321_uri_empty() {
let uri = create_bip321_uri(None, None, None);
assert_eq!(uri, "BITCOIN:");
}
}