Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions examples/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ enum Commands {
/// Amount in sats (optional)
amount: Option<u64>,
},
/// Get the current lightning address
GetLightningAddress,
/// Register a lightning address
RegisterLightningAddress {
/// The lightning address name to register
name: String,
},
/// Clear the screen
Clear,
/// Exit the application
Expand Down Expand Up @@ -367,6 +374,14 @@ fn parse_command(input: &str) -> Result<Commands> {

Ok(Commands::EstimateFee { destination, amount })
},
"get-lightning-address" | "get-ln-addr" | "ln-addr" => Ok(Commands::GetLightningAddress),
"register-lightning-address" | "register-ln-addr" => {
if parts.len() < 2 {
return Err(anyhow::anyhow!("Usage: register-lightning-address <name>"));
}
let name = parts[1].to_string();
Ok(Commands::RegisterLightningAddress { name })
},
"clear" | "cls" => Ok(Commands::Clear),
"exit" | "quit" | "q" => Ok(Commands::Exit),
"help" => {
Expand Down Expand Up @@ -590,6 +605,60 @@ async fn execute_command(command: Commands, state: &mut WalletState) -> Result<(
},
}
},
Commands::GetLightningAddress => {
let wallet = state.wallet();

println!("{} Fetching lightning address...", "⚡".bright_yellow());

match wallet.get_lightning_address().await {
Ok(Some(address)) => {
println!(
"{} Lightning address: {}",
"⚡".bright_green(),
address.bright_cyan()
);
},
Ok(None) => {
println!("{} No lightning address registered yet.", "⚡".bright_yellow());
println!(
"{} Use 'register-lightning-address <name>' to register one",
"Hint:".bright_yellow().bold()
);
},
Err(e) => {
return Err(anyhow::anyhow!("Failed to get lightning address: {:?}", e));
},
}
},
Commands::RegisterLightningAddress { name } => {
let wallet = state.wallet();

println!(
"{} Registering lightning address: {}...",
"⚡".bright_yellow(),
name.bright_cyan()
);

match wallet.register_lightning_address(name.clone()).await {
Ok(()) => {
println!("{} Lightning address registered successfully!", "✅".bright_green());
// Fetch and display the full address
match wallet.get_lightning_address().await {
Ok(Some(address)) => {
println!(
"{} Your lightning address: {}",
"⚡".bright_green(),
address.bright_cyan()
);
},
_ => {},
}
},
Err(e) => {
return Err(anyhow::anyhow!("Failed to register lightning address: {:?}", e));
},
}
},
Commands::Clear => {
print!("\x1B[2J\x1B[1;1H");
std::io::stdout().flush().unwrap();
Expand Down Expand Up @@ -628,6 +697,12 @@ fn print_help() {
println!(" {} <destination> [amount]", "estimate-fee".bright_green().bold());
println!(" Estimate the fee for a payment");
println!();
println!(" {}", "get-lightning-address".bright_green().bold());
println!(" Get the current lightning address");
println!();
println!(" {} <name>", "register-lightning-address".bright_green().bold());
println!(" Register a lightning address");
println!();
println!(" {}", "clear".bright_green().bold());
println!(" Clear the terminal screen");
println!();
Expand Down
12 changes: 12 additions & 0 deletions orange-sdk/src/ffi/orange/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,4 +251,16 @@ impl Wallet {
pub fn event_handled(&self) -> bool {
self.inner.event_handled().is_ok()
}

/// Gets the lightning address for this wallet, if one is set.
pub async fn get_lightning_address(&self) -> Result<Option<String>, WalletError> {
let result = self.inner.get_lightning_address().await?;
Ok(result)
}

/// Attempts to register the lightning address for this wallet.
pub async fn register_lightning_address(&self, name: String) -> Result<(), WalletError> {
self.inner.register_lightning_address(name).await?;
Ok(())
}
}
12 changes: 11 additions & 1 deletion orange-sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ impl Wallet {
ExtraConfig::Spark(sp) => Arc::new(Box::new(
Spark::init(
&config,
*sp,
sp.clone(),
Arc::clone(&store),
Arc::clone(&event_queue),
tx_metadata.clone(),
Expand Down Expand Up @@ -1345,6 +1345,16 @@ impl Wallet {
res
}

/// Gets the lightning address for this wallet, if one is set.
pub async fn get_lightning_address(&self) -> Result<Option<String>, WalletError> {
Ok(self.inner.trusted.get_lightning_address().await?)
}

/// Attempts to register the lightning address for this wallet.
pub async fn register_lightning_address(&self, name: String) -> Result<(), WalletError> {
Ok(self.inner.trusted.register_lightning_address(name).await?)
}

/// Stops the wallet, which will stop the underlying LDK node and any background tasks.
/// This will ensure that any critical tasks have completed before stopping.
pub async fn stop(&self) {
Expand Down
16 changes: 16 additions & 0 deletions orange-sdk/src/trusted_wallet/cashu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,22 @@ impl TrustedWalletInterface for Cashu {
})
}

fn get_lightning_address(
&self,
) -> Pin<Box<dyn Future<Output = Result<Option<String>, TrustedError>> + Send + '_>> {
Box::pin(async { Ok(None) })
}

fn register_lightning_address(
&self, _name: String,
) -> Pin<Box<dyn Future<Output = Result<(), TrustedError>> + Send + '_>> {
Box::pin(async {
Err(TrustedError::UnsupportedOperation(
"register_lightning_address is not supported in Cashu Wallet".to_string(),
))
})
}

fn stop(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
Box::pin(async move {
log_info!(self.logger, "Stopping Cashu wallet");
Expand Down
16 changes: 16 additions & 0 deletions orange-sdk/src/trusted_wallet/dummy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,22 @@ impl TrustedWalletInterface for DummyTrustedWallet {
})
}

fn get_lightning_address(
&self,
) -> Pin<Box<dyn Future<Output = Result<Option<String>, TrustedError>> + Send + '_>> {
Box::pin(async { Ok(None) })
}

fn register_lightning_address(
&self, _name: String,
) -> Pin<Box<dyn Future<Output = Result<(), TrustedError>> + Send + '_>> {
Box::pin(async {
Err(TrustedError::UnsupportedOperation(
"register_lightning_address is not supported in DummyTrustedWallet".to_string(),
))
})
}

fn stop(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
Box::pin(async move {
let _ = self.ldk_node.stop();
Expand Down
10 changes: 10 additions & 0 deletions orange-sdk/src/trusted_wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,16 @@ pub trait TrustedWalletInterface: Send + Sync + private::Sealed {
&self, payment_hash: [u8; 32],
) -> Pin<Box<dyn Future<Output = Option<ReceivedLightningPayment>> + Send + '_>>;

/// Gets the lightning address for this wallet, if one is set.
fn get_lightning_address(
&self,
) -> Pin<Box<dyn Future<Output = Result<Option<String>, TrustedError>> + Send + '_>>;

/// Attempts to register the lightning address for this wallet.
fn register_lightning_address(
&self, name: String,
) -> Pin<Box<dyn Future<Output = Result<(), TrustedError>> + Send + '_>>;

/// Stops the wallet, cleaning up any resources.
/// This is typically used to gracefully shut down the wallet.
fn stop(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
Expand Down
48 changes: 42 additions & 6 deletions orange-sdk/src/trusted_wallet/spark/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ use bitcoin_payment_instructions::amount::Amount;
use breez_sdk_spark::{
BreezSdk, EventListener, GetInfoRequest, ListPaymentsRequest, PaymentDetails, PaymentStatus,
PaymentType, PrepareSendPaymentRequest, ReceivePaymentMethod, ReceivePaymentRequest,
SdkBuilder, SdkError, SdkEvent, SendPaymentMethod, SendPaymentRequest,
RegisterLightningAddressRequest, SdkBuilder, SdkError, SdkEvent, SendPaymentMethod,
SendPaymentRequest,
};

use graduated_rebalancer::ReceivedLightningPayment;
Expand All @@ -37,7 +38,7 @@ use std::time::Duration;
use uuid::Uuid;

/// Configuration options for the Spark wallet.
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Clone)]
pub struct SparkWalletConfig {
/// How often to sync the wallet with the blockchain, in seconds.
/// Default is 60 seconds.
Expand All @@ -46,11 +47,17 @@ pub struct SparkWalletConfig {
/// lightning when sending and receiving. This has the benefit of lower fees
/// but is at the cost of privacy.
pub prefer_spark_over_lightning: bool,
/// The domain used for receiving through lnurl-pay and lightning address.
pub lnurl_domain: Option<String>,
}

impl Default for SparkWalletConfig {
fn default() -> Self {
SparkWalletConfig { sync_interval_secs: 60, prefer_spark_over_lightning: false }
SparkWalletConfig {
sync_interval_secs: 60,
prefer_spark_over_lightning: false,
lnurl_domain: Some("breez.tips".to_string()),
}
}
}

Expand All @@ -59,7 +66,7 @@ impl Default for SparkWalletConfig {
const BREEZ_API_KEY: &str = "MIIBajCCARygAwIBAgIHPnfOjAhBgzAFBgMrZXAwEDEOMAwGA1UEAxMFQnJlZXowHhcNMjUwOTE5MjEzNTU1WhcNMzUwOTE3MjEzNTU1WjAqMRMwEQYDVQQKEwpvcmFuZ2Utc2RrMRMwEQYDVQQDEwpvcmFuZ2Utc2RrMCowBQYDK2VwAyEA0IP1y98gPByiIMoph1P0G6cctLb864rNXw1LRLOpXXejezB5MA4GA1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTaOaPuXmtLDTJVv++VYBiQr9gHCTAfBgNVHSMEGDAWgBTeqtaSVvON53SSFvxMtiCyayiYazAZBgNVHREEEjAQgQ5iZW5Ac3BpcmFsLnh5ejAFBgMrZXADQQCry+1LkA3nrYa1sovS5iFI1Tkpmr/R0nM/4gJtsO93vFOkm3vBEGwjKAV7lrGzFcFbbuyM1wEJPi4Po1XCEG0D";

impl SparkWalletConfig {
fn to_breez_config(self, network: Network) -> Result<breez_sdk_spark::Config, TrustedError> {
fn into_breez_config(self, network: Network) -> Result<breez_sdk_spark::Config, TrustedError> {
let network = match network {
Network::Bitcoin => breez_sdk_spark::Network::Mainnet,
Network::Regtest => breez_sdk_spark::Network::Regtest,
Expand All @@ -75,7 +82,7 @@ impl SparkWalletConfig {
real_time_sync_server_url: None,
api_key: Some(BREEZ_API_KEY.to_string()),
max_deposit_claim_fee: None,
lnurl_domain: None,
lnurl_domain: self.lnurl_domain,
private_enabled_default: true,
})
}
Expand Down Expand Up @@ -249,6 +256,34 @@ impl TrustedWalletInterface for Spark {
})
}

fn get_lightning_address(
&self,
) -> Pin<Box<dyn Future<Output = Result<Option<String>, TrustedError>> + Send + '_>> {
Box::pin(async move {
match self.spark_wallet.get_lightning_address().await? {
None => Ok(None),
Some(addr) => Ok(Some(addr.lightning_address)),
}
})
}

fn register_lightning_address(
&self, name: String,
) -> Pin<Box<dyn Future<Output = Result<(), TrustedError>> + Send + '_>> {
Box::pin(async move {
let res = self.get_lightning_address().await?;
if res.is_some() {
return Err(TrustedError::Other(
"Wallet already has a lightning address".to_string(),
));
}

let params = RegisterLightningAddressRequest { username: name, description: None };
self.spark_wallet.register_lightning_address(params).await?;
Ok(())
})
}

fn stop(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
Box::pin(async move {
log_info!(self.logger, "Stopping Spark wallet");
Expand All @@ -264,7 +299,8 @@ impl Spark {
event_queue: Arc<EventQueue>, tx_metadata: TxMetadataStore, logger: Arc<Logger>,
runtime: Arc<Runtime>,
) -> Result<Self, InitFailure> {
let spark_config: breez_sdk_spark::Config = spark_config.to_breez_config(config.network)?;
let spark_config: breez_sdk_spark::Config =
spark_config.into_breez_config(config.network)?;

let seed = match &config.seed {
Seed::Seed64(bytes) => breez_sdk_spark::Seed::Entropy(bytes.to_vec()),
Expand Down
Loading