use anyhow::Result;
use cdk::nuts::CurrencyUnit;
use cdk::wallet::{payment_request as pr, NostrWaitInfo, WalletRepository};
use clap::Args;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct NostrWaitInfoSerializable {
secret_key_hex: String,
relays: Vec<String>,
pubkey_hex: String,
}
impl From<NostrWaitInfo> for NostrWaitInfoSerializable {
fn from(info: NostrWaitInfo) -> Self {
Self {
secret_key_hex: info.keys.secret_key().to_secret_hex(),
relays: info.relays,
pubkey_hex: info.pubkey.to_hex(),
}
}
}
#[derive(Args)]
pub struct CreateRequestSubCommand {
#[arg(short, long)]
amount: Option<u64>,
description: Option<String>,
#[arg(long, action = clap::ArgAction::Append)]
pubkey: Option<Vec<String>>,
#[arg(long, default_value = "1")]
num_sigs: u64,
#[arg(long, conflicts_with = "preimage")]
hash: Option<String>,
#[arg(long, conflicts_with = "hash")]
preimage: Option<String>,
#[arg(long, default_value = "nostr")]
transport: String,
#[arg(long)]
http_url: Option<String>,
#[arg(long, action = clap::ArgAction::Append)]
nostr_relay: Option<Vec<String>>,
#[arg(short, long)]
bech32: bool,
}
pub async fn create_request(
wallet_repository: &WalletRepository,
sub_command_args: &CreateRequestSubCommand,
unit: &CurrencyUnit,
) -> Result<()> {
let params = pr::CreateRequestParams {
amount: sub_command_args.amount,
unit: unit.to_string(),
description: sub_command_args.description.clone(),
pubkeys: sub_command_args.pubkey.clone(),
num_sigs: sub_command_args.num_sigs,
hash: sub_command_args.hash.clone(),
preimage: sub_command_args.preimage.clone(),
transport: sub_command_args.transport.to_lowercase(),
http_url: sub_command_args.http_url.clone(),
nostr_relays: sub_command_args.nostr_relay.clone(),
};
let (req, nostr_wait) = wallet_repository.create_request(params).await?;
if sub_command_args.bech32 {
println!("{}", req.to_bech32_string()?);
} else {
println!("{}", req);
}
if let Some(info) = nostr_wait {
let key = info.pubkey.to_string();
if let Some(wallet) = wallet_repository.get_wallets().await.first() {
let serializable_info = NostrWaitInfoSerializable::from(info.clone());
let val = serde_json::to_vec(&serializable_info)?;
wallet
.localstore
.kv_write("cdk_cli", "pending_nostr_requests", &key, &val)
.await?;
}
println!("Listening for payment via Nostr...");
let amount = wallet_repository.wait_for_nostr_payment(info).await?;
println!("Received {}", amount);
}
Ok(())
}