mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-22 23:25:22 +01:00
feat: cdk-cli
This commit is contained in:
117
crates/cdk-cli/src/main.rs
Normal file
117
crates/cdk-cli/src/main.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
use std::fs;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use cdk::cdk_database::WalletDatabase;
|
||||
use cdk::wallet::Wallet;
|
||||
use cdk::{cdk_database, Mnemonic};
|
||||
use cdk_redb::RedbWalletDatabase;
|
||||
use cdk_sqlite::WalletSQLiteDatabase;
|
||||
use clap::{Parser, Subcommand};
|
||||
use rand::Rng;
|
||||
|
||||
mod sub_commands;
|
||||
|
||||
/// Simple CLI application to interact with cashu
|
||||
#[derive(Parser)]
|
||||
#[command(name = "cashu-tool")]
|
||||
#[command(author = "thesimplekid <tsk@thesimplekid.com>")]
|
||||
#[command(version = "0.1")]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
/// Database engine to use (sqlite/redb)
|
||||
#[arg(short, long, default_value = "sqlite")]
|
||||
engine: String,
|
||||
/// Path to Seed
|
||||
#[arg(short, long, default_value = "./seed")]
|
||||
seed_path: String,
|
||||
/// File Path to save proofs
|
||||
#[arg(short, long)]
|
||||
db_path: Option<String>,
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
|
||||
const DEFAULT_REDB_DB_PATH: &str = "./cashu_tool.redb";
|
||||
const DEFAULT_SQLITE_DB_PATH: &str = "./cashu_tool.redb";
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Commands {
|
||||
/// Decode a token
|
||||
DecodeToken(sub_commands::decode_token::DecodeTokenSubCommand),
|
||||
/// Pay bolt11 invoice
|
||||
Melt(sub_commands::melt::MeltSubCommand),
|
||||
/// Receive token
|
||||
Receive(sub_commands::receive::ReceiveSubCommand),
|
||||
/// Create token from wallet balance
|
||||
CreateToken(sub_commands::create_token::CreateTokenSubCommand),
|
||||
/// Check if wallet balance is spendable
|
||||
CheckSpendable,
|
||||
/// View mint info
|
||||
MintInfo(sub_commands::mint_info::MintInfoSubcommand),
|
||||
/// Mint proofs via bolt11
|
||||
Mint(sub_commands::mint::MintSubCommand),
|
||||
/// Restore proofs from seed
|
||||
Restore(sub_commands::restore::RestoreSubCommand),
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(tracing::Level::WARN)
|
||||
.init();
|
||||
|
||||
// Parse input
|
||||
let args: Cli = Cli::parse();
|
||||
|
||||
let localstore: Arc<dyn WalletDatabase<Err = cdk_database::Error> + Send + Sync> =
|
||||
match args.engine.as_str() {
|
||||
"sqlite" => Arc::new(RedbWalletDatabase::new(DEFAULT_REDB_DB_PATH)?),
|
||||
"redb" => Arc::new(WalletSQLiteDatabase::new(DEFAULT_SQLITE_DB_PATH).await?),
|
||||
_ => bail!("Unknown DB engine"),
|
||||
};
|
||||
|
||||
let mnemonic = match fs::metadata(args.seed_path.clone()) {
|
||||
Ok(_) => {
|
||||
let contents = fs::read_to_string(args.seed_path.clone())?;
|
||||
Mnemonic::from_str(&contents)?
|
||||
}
|
||||
Err(_e) => {
|
||||
let mut rng = rand::thread_rng();
|
||||
let random_bytes: [u8; 32] = rng.gen();
|
||||
|
||||
let mnemnic = Mnemonic::from_entropy(&random_bytes)?;
|
||||
tracing::info!("Using randomly generated seed you will not be able to restore");
|
||||
|
||||
mnemnic
|
||||
}
|
||||
};
|
||||
|
||||
let wallet = Wallet::new(localstore, &mnemonic.to_seed_normalized(""), vec![]);
|
||||
|
||||
match &args.command {
|
||||
Commands::DecodeToken(sub_command_args) => {
|
||||
sub_commands::decode_token::decode_token(sub_command_args)
|
||||
}
|
||||
Commands::Melt(sub_command_args) => {
|
||||
sub_commands::melt::melt(wallet, sub_command_args).await
|
||||
}
|
||||
Commands::Receive(sub_command_args) => {
|
||||
sub_commands::receive::receive(wallet, sub_command_args).await
|
||||
}
|
||||
Commands::CreateToken(sub_command_args) => {
|
||||
sub_commands::create_token::create_token(wallet, sub_command_args).await
|
||||
}
|
||||
Commands::CheckSpendable => sub_commands::check_spent::check_spent(wallet).await,
|
||||
Commands::MintInfo(sub_command_args) => {
|
||||
sub_commands::mint_info::mint_info(sub_command_args).await
|
||||
}
|
||||
Commands::Mint(sub_command_args) => {
|
||||
sub_commands::mint::mint(wallet, sub_command_args).await
|
||||
}
|
||||
Commands::Restore(sub_command_args) => {
|
||||
sub_commands::restore::restore(wallet, sub_command_args).await
|
||||
}
|
||||
}
|
||||
}
|
||||
41
crates/cdk-cli/src/sub_commands/check_spent.rs
Normal file
41
crates/cdk-cli/src/sub_commands/check_spent.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
use std::{io, println};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use cdk::url::UncheckedUrl;
|
||||
use cdk::wallet::Wallet;
|
||||
|
||||
pub async fn check_spent(wallet: Wallet) -> Result<()> {
|
||||
let mints_amounts: Vec<(UncheckedUrl, HashMap<_, _>)> =
|
||||
wallet.mint_balances().await?.into_iter().collect();
|
||||
|
||||
for (i, (mint, amount)) in mints_amounts.iter().enumerate() {
|
||||
println!("{}: {}, {:?} sats", i, mint, amount);
|
||||
}
|
||||
|
||||
println!("Enter mint number to create token");
|
||||
|
||||
let mut user_input = String::new();
|
||||
let stdin = io::stdin();
|
||||
io::stdout().flush().unwrap();
|
||||
stdin.read_line(&mut user_input)?;
|
||||
|
||||
let mint_number: usize = user_input.trim().parse()?;
|
||||
|
||||
if mint_number.gt(&(mints_amounts.len() - 1)) {
|
||||
bail!("Invalid mint number");
|
||||
}
|
||||
|
||||
let mint_url = mints_amounts[mint_number].0.clone();
|
||||
|
||||
let proofs = wallet.get_proofs(mint_url.clone()).await?.unwrap();
|
||||
|
||||
let send_proofs = wallet.check_proofs_spent(mint_url, proofs.to_vec()).await?;
|
||||
|
||||
for proof in send_proofs {
|
||||
println!("{:#?}", proof);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
164
crates/cdk-cli/src/sub_commands/create_token.rs
Normal file
164
crates/cdk-cli/src/sub_commands/create_token.rs
Normal file
@@ -0,0 +1,164 @@
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
use std::str::FromStr;
|
||||
use std::{io, println};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use cdk::amount::SplitTarget;
|
||||
use cdk::nuts::{Conditions, CurrencyUnit, PublicKey, SpendingConditions};
|
||||
use cdk::url::UncheckedUrl;
|
||||
use cdk::wallet::Wallet;
|
||||
use cdk::Amount;
|
||||
use clap::Args;
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct CreateTokenSubCommand {
|
||||
/// Token Memo
|
||||
#[arg(short, long)]
|
||||
memo: Option<String>,
|
||||
/// Preimage
|
||||
#[arg(long)]
|
||||
preimage: Option<String>,
|
||||
/// Required number of signatures
|
||||
#[arg(long)]
|
||||
required_sigs: Option<u64>,
|
||||
/// Locktime before refund keys can be used
|
||||
#[arg(short, long)]
|
||||
locktime: Option<u64>,
|
||||
/// Publey to lock proofs to
|
||||
#[arg(short, long, action = clap::ArgAction::Append)]
|
||||
pubkey: Vec<String>,
|
||||
/// Publey to lock proofs to
|
||||
#[arg(long, action = clap::ArgAction::Append)]
|
||||
refund_keys: Vec<String>,
|
||||
}
|
||||
|
||||
pub async fn create_token(wallet: Wallet, sub_command_args: &CreateTokenSubCommand) -> Result<()> {
|
||||
let mints_amounts: Vec<(UncheckedUrl, HashMap<_, _>)> =
|
||||
wallet.mint_balances().await?.into_iter().collect();
|
||||
|
||||
for (i, (mint, amount)) in mints_amounts.iter().enumerate() {
|
||||
println!("{}: {}, {:?} sats", i, mint, amount);
|
||||
}
|
||||
|
||||
println!("Enter mint number to create token");
|
||||
|
||||
let mut user_input = String::new();
|
||||
let stdin = io::stdin();
|
||||
io::stdout().flush().unwrap();
|
||||
stdin.read_line(&mut user_input)?;
|
||||
|
||||
let mint_number: usize = user_input.trim().parse()?;
|
||||
|
||||
if mint_number.gt(&(mints_amounts.len() - 1)) {
|
||||
bail!("Invalid mint number");
|
||||
}
|
||||
|
||||
let mint_url = mints_amounts[mint_number].0.clone();
|
||||
|
||||
println!("Enter value of token in sats");
|
||||
|
||||
let mut user_input = String::new();
|
||||
let stdin = io::stdin();
|
||||
io::stdout().flush().unwrap();
|
||||
stdin.read_line(&mut user_input)?;
|
||||
let token_amount = Amount::from(user_input.trim().parse::<u64>()?);
|
||||
|
||||
if token_amount.gt(mints_amounts[mint_number]
|
||||
.1
|
||||
.get(&CurrencyUnit::Sat)
|
||||
.unwrap())
|
||||
{
|
||||
bail!("Not enough funds");
|
||||
}
|
||||
|
||||
let conditions = match &sub_command_args.preimage {
|
||||
Some(preimage) => {
|
||||
let pubkeys = match sub_command_args.pubkey.is_empty() {
|
||||
true => None,
|
||||
false => Some(
|
||||
sub_command_args
|
||||
.pubkey
|
||||
.iter()
|
||||
.map(|p| PublicKey::from_str(p).unwrap())
|
||||
.collect(),
|
||||
),
|
||||
};
|
||||
|
||||
let refund_keys = match sub_command_args.refund_keys.is_empty() {
|
||||
true => None,
|
||||
false => Some(
|
||||
sub_command_args
|
||||
.refund_keys
|
||||
.iter()
|
||||
.map(|p| PublicKey::from_str(p).unwrap())
|
||||
.collect(),
|
||||
),
|
||||
};
|
||||
|
||||
let conditions = Conditions::new(
|
||||
sub_command_args.locktime,
|
||||
pubkeys,
|
||||
refund_keys,
|
||||
sub_command_args.required_sigs,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Some(SpendingConditions::new_htlc(preimage.clone(), conditions)?)
|
||||
}
|
||||
None => match sub_command_args.pubkey.is_empty() {
|
||||
true => None,
|
||||
false => {
|
||||
let pubkeys: Vec<PublicKey> = sub_command_args
|
||||
.pubkey
|
||||
.iter()
|
||||
.map(|p| PublicKey::from_str(p).unwrap())
|
||||
.collect();
|
||||
|
||||
let refund_keys: Vec<PublicKey> = sub_command_args
|
||||
.refund_keys
|
||||
.iter()
|
||||
.map(|p| PublicKey::from_str(p).unwrap())
|
||||
.collect();
|
||||
|
||||
let refund_keys = (!refund_keys.is_empty()).then_some(refund_keys);
|
||||
|
||||
let data_pubkey = pubkeys[0];
|
||||
let pubkeys = pubkeys[1..].to_vec();
|
||||
let pubkeys = (!pubkeys.is_empty()).then_some(pubkeys);
|
||||
|
||||
let conditions = Conditions::new(
|
||||
sub_command_args.locktime,
|
||||
pubkeys,
|
||||
refund_keys,
|
||||
sub_command_args.required_sigs,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
tracing::debug!("{}", data_pubkey.to_string());
|
||||
|
||||
Some(SpendingConditions::P2PKConditions {
|
||||
data: data_pubkey,
|
||||
conditions,
|
||||
})
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let token = wallet
|
||||
.send(
|
||||
&mint_url,
|
||||
CurrencyUnit::Sat,
|
||||
sub_command_args.memo.clone(),
|
||||
token_amount,
|
||||
&SplitTarget::default(),
|
||||
conditions,
|
||||
)
|
||||
.await?;
|
||||
|
||||
println!("{}", token);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
18
crates/cdk-cli/src/sub_commands/decode_token.rs
Normal file
18
crates/cdk-cli/src/sub_commands/decode_token.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Result;
|
||||
use cdk::nuts::Token;
|
||||
use clap::Args;
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct DecodeTokenSubCommand {
|
||||
/// Cashu Token
|
||||
token: String,
|
||||
}
|
||||
|
||||
pub fn decode_token(sub_command_args: &DecodeTokenSubCommand) -> Result<()> {
|
||||
let token = Token::from_str(&sub_command_args.token)?;
|
||||
|
||||
println!("{:}", serde_json::to_string_pretty(&token)?);
|
||||
Ok(())
|
||||
}
|
||||
79
crates/cdk-cli/src/sub_commands/melt.rs
Normal file
79
crates/cdk-cli/src/sub_commands/melt.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
use std::str::FromStr;
|
||||
use std::{io, println};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use cdk::amount::SplitTarget;
|
||||
use cdk::nuts::CurrencyUnit;
|
||||
use cdk::url::UncheckedUrl;
|
||||
use cdk::wallet::Wallet;
|
||||
use cdk::Bolt11Invoice;
|
||||
use clap::Args;
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct MeltSubCommand {}
|
||||
|
||||
pub async fn melt(wallet: Wallet, _sub_command_args: &MeltSubCommand) -> Result<()> {
|
||||
let mints_amounts: Vec<(UncheckedUrl, HashMap<_, _>)> =
|
||||
wallet.mint_balances().await?.into_iter().collect();
|
||||
|
||||
for (i, (mint, amount)) in mints_amounts.iter().enumerate() {
|
||||
println!("{}: {}, {:?} sats", i, mint, amount);
|
||||
}
|
||||
|
||||
println!("Enter mint number to create token");
|
||||
|
||||
let mut user_input = String::new();
|
||||
let stdin = io::stdin();
|
||||
io::stdout().flush().unwrap();
|
||||
stdin.read_line(&mut user_input)?;
|
||||
|
||||
let mint_number: usize = user_input.trim().parse()?;
|
||||
|
||||
if mint_number.gt(&(mints_amounts.len() - 1)) {
|
||||
bail!("Invalid mint number");
|
||||
}
|
||||
|
||||
let mint_url = mints_amounts[mint_number].0.clone();
|
||||
|
||||
println!("Enter bolt11 invoice request");
|
||||
|
||||
let mut user_input = String::new();
|
||||
let stdin = io::stdin();
|
||||
io::stdout().flush().unwrap();
|
||||
stdin.read_line(&mut user_input)?;
|
||||
let bolt11 = Bolt11Invoice::from_str(user_input.trim())?;
|
||||
|
||||
if bolt11
|
||||
.amount_milli_satoshis()
|
||||
.unwrap()
|
||||
.gt(&(<cdk::Amount as Into<u64>>::into(
|
||||
*mints_amounts[mint_number]
|
||||
.1
|
||||
.get(&CurrencyUnit::Sat)
|
||||
.unwrap(),
|
||||
) * 1000_u64))
|
||||
{
|
||||
bail!("Not enough funds");
|
||||
}
|
||||
let quote = wallet
|
||||
.melt_quote(
|
||||
mint_url.clone(),
|
||||
cdk::nuts::CurrencyUnit::Sat,
|
||||
bolt11.to_string(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let melt = wallet
|
||||
.melt(&mint_url, "e.id, SplitTarget::default())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
println!("Paid invoice: {}", melt.paid);
|
||||
if let Some(preimage) = melt.preimage {
|
||||
println!("Payment preimage: {}", preimage);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
59
crates/cdk-cli/src/sub_commands/mint.rs
Normal file
59
crates/cdk-cli/src/sub_commands/mint.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
use cdk::amount::SplitTarget;
|
||||
use cdk::nuts::CurrencyUnit;
|
||||
use cdk::url::UncheckedUrl;
|
||||
use cdk::wallet::Wallet;
|
||||
use cdk::Amount;
|
||||
use clap::Args;
|
||||
use tokio::time::sleep;
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct MintSubCommand {
|
||||
/// Amount
|
||||
#[arg(short, long)]
|
||||
amount: u64,
|
||||
/// Currency unit e.g. sat
|
||||
#[arg(short, long)]
|
||||
unit: String,
|
||||
/// Mint url
|
||||
#[arg(short, long)]
|
||||
mint_url: UncheckedUrl,
|
||||
}
|
||||
|
||||
pub async fn mint(wallet: Wallet, sub_command_args: &MintSubCommand) -> Result<()> {
|
||||
let mint_url = sub_command_args.mint_url.clone();
|
||||
|
||||
let quote = wallet
|
||||
.mint_quote(
|
||||
mint_url.clone(),
|
||||
Amount::from(sub_command_args.amount),
|
||||
CurrencyUnit::from(&sub_command_args.unit),
|
||||
)
|
||||
.await?;
|
||||
|
||||
println!("Quote: {:#?}", quote);
|
||||
|
||||
println!("Please pay: {}", quote.request);
|
||||
|
||||
loop {
|
||||
let status = wallet
|
||||
.mint_quote_status(mint_url.clone(), "e.id)
|
||||
.await?;
|
||||
|
||||
if status.paid {
|
||||
break;
|
||||
}
|
||||
|
||||
sleep(Duration::from_secs(2)).await;
|
||||
}
|
||||
|
||||
let receive_amount = wallet
|
||||
.mint(mint_url.clone(), "e.id, SplitTarget::default(), None)
|
||||
.await?;
|
||||
|
||||
println!("Received {receive_amount} from mint {mint_url}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
23
crates/cdk-cli/src/sub_commands/mint_info.rs
Normal file
23
crates/cdk-cli/src/sub_commands/mint_info.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use anyhow::Result;
|
||||
use cdk::url::UncheckedUrl;
|
||||
use cdk::HttpClient;
|
||||
use clap::Args;
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct MintInfoSubcommand {
|
||||
/// Cashu Token
|
||||
#[arg(short, long)]
|
||||
mint_url: UncheckedUrl,
|
||||
}
|
||||
|
||||
pub async fn mint_info(sub_command_args: &MintInfoSubcommand) -> Result<()> {
|
||||
let client = HttpClient::default();
|
||||
|
||||
let info = client
|
||||
.get_mint_info(sub_command_args.mint_url.clone().try_into()?)
|
||||
.await?;
|
||||
|
||||
println!("{:#?}", info);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
8
crates/cdk-cli/src/sub_commands/mod.rs
Normal file
8
crates/cdk-cli/src/sub_commands/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
pub mod check_spent;
|
||||
pub mod create_token;
|
||||
pub mod decode_token;
|
||||
pub mod melt;
|
||||
pub mod mint;
|
||||
pub mod mint_info;
|
||||
pub mod receive;
|
||||
pub mod restore;
|
||||
81
crates/cdk-cli/src/sub_commands/receive.rs
Normal file
81
crates/cdk-cli/src/sub_commands/receive.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use cdk::amount::SplitTarget;
|
||||
use cdk::nuts::SecretKey;
|
||||
use cdk::wallet::Wallet;
|
||||
use clap::Args;
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct ReceiveSubCommand {
|
||||
/// Cashu Token
|
||||
token: Option<String>,
|
||||
/// Nostr key
|
||||
#[arg(short, long)]
|
||||
nostr_key: Option<String>,
|
||||
/// Signing Key
|
||||
#[arg(short, long, action = clap::ArgAction::Append)]
|
||||
signing_key: Vec<String>,
|
||||
/// Nostr relay
|
||||
#[arg(short, long, action = clap::ArgAction::Append)]
|
||||
relay: Vec<String>,
|
||||
/// Preimage
|
||||
#[arg(short, long, action = clap::ArgAction::Append)]
|
||||
preimage: Vec<String>,
|
||||
}
|
||||
|
||||
pub async fn receive(wallet: Wallet, sub_command_args: &ReceiveSubCommand) -> Result<()> {
|
||||
let nostr_key = match sub_command_args.nostr_key.as_ref() {
|
||||
Some(nostr_key) => {
|
||||
let secret_key = SecretKey::from_str(nostr_key)?;
|
||||
wallet.add_p2pk_signing_key(secret_key.clone()).await;
|
||||
Some(secret_key)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
if !sub_command_args.signing_key.is_empty() {
|
||||
let signing_keys: Vec<SecretKey> = sub_command_args
|
||||
.signing_key
|
||||
.iter()
|
||||
.map(|s| SecretKey::from_str(s).unwrap())
|
||||
.collect();
|
||||
|
||||
for signing_key in signing_keys {
|
||||
wallet.add_p2pk_signing_key(signing_key).await;
|
||||
}
|
||||
}
|
||||
|
||||
let preimage = match sub_command_args.preimage.is_empty() {
|
||||
true => None,
|
||||
false => Some(sub_command_args.preimage.clone()),
|
||||
};
|
||||
|
||||
let amount = match nostr_key {
|
||||
Some(nostr_key) => {
|
||||
assert!(!sub_command_args.relay.is_empty());
|
||||
wallet
|
||||
.add_nostr_relays(sub_command_args.relay.clone())
|
||||
.await?;
|
||||
wallet
|
||||
.nostr_receive(nostr_key, SplitTarget::default())
|
||||
.await?
|
||||
}
|
||||
None => {
|
||||
wallet
|
||||
.receive(
|
||||
sub_command_args
|
||||
.token
|
||||
.as_ref()
|
||||
.ok_or(anyhow!("Token Required"))?,
|
||||
&SplitTarget::default(),
|
||||
preimage,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
};
|
||||
|
||||
println!("Received: {}", amount);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
21
crates/cdk-cli/src/sub_commands/restore.rs
Normal file
21
crates/cdk-cli/src/sub_commands/restore.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
use anyhow::Result;
|
||||
use cdk::url::UncheckedUrl;
|
||||
use cdk::wallet::Wallet;
|
||||
use clap::Args;
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct RestoreSubCommand {
|
||||
/// Mint Url
|
||||
#[arg(short, long)]
|
||||
mint_url: UncheckedUrl,
|
||||
}
|
||||
|
||||
pub async fn restore(wallet: Wallet, sub_command_args: &RestoreSubCommand) -> Result<()> {
|
||||
let mint_url = sub_command_args.mint_url.clone();
|
||||
|
||||
let amount = wallet.restore(mint_url).await?;
|
||||
|
||||
println!("Restored {}", amount);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user