thesimplekid 4 месяцев назад
Родитель
Сommit
9d65b603bc

+ 1 - 1
.helix/languages.toml

@@ -1,2 +1,2 @@
 [language-server.rust-analyzer.config]
-cargo = { features = ["wallet", "mint"] }
+cargo = { features = ["wallet", "mint", "swagger"] }

+ 2 - 16
crates/cdk-cln/src/lib.rs

@@ -16,10 +16,7 @@ use cdk::cdk_lightning::{
     self, CreateInvoiceResponse, MintLightning, PayInvoiceResponse, PaymentQuoteResponse, Settings,
 };
 use cdk::mint::FeeReserve;
-use cdk::nuts::{
-    CurrencyUnit, MeltMethodSettings, MeltQuoteBolt11Request, MeltQuoteState, MintMethodSettings,
-    MintQuoteState,
-};
+use cdk::nuts::{CurrencyUnit, MeltQuoteBolt11Request, MeltQuoteState, MintQuoteState};
 use cdk::util::{hex, unix_time};
 use cdk::{mint, Bolt11Invoice};
 use cln_rpc::model::requests::{
@@ -45,28 +42,19 @@ pub struct Cln {
     rpc_socket: PathBuf,
     cln_client: Arc<Mutex<cln_rpc::ClnRpc>>,
     fee_reserve: FeeReserve,
-    mint_settings: MintMethodSettings,
-    melt_settings: MeltMethodSettings,
     wait_invoice_cancel_token: CancellationToken,
     wait_invoice_is_active: Arc<AtomicBool>,
 }
 
 impl Cln {
     /// Create new [`Cln`]
-    pub async fn new(
-        rpc_socket: PathBuf,
-        fee_reserve: FeeReserve,
-        mint_settings: MintMethodSettings,
-        melt_settings: MeltMethodSettings,
-    ) -> Result<Self, Error> {
+    pub async fn new(rpc_socket: PathBuf, fee_reserve: FeeReserve) -> Result<Self, Error> {
         let cln_client = cln_rpc::ClnRpc::new(&rpc_socket).await?;
 
         Ok(Self {
             rpc_socket,
             cln_client: Arc::new(Mutex::new(cln_client)),
             fee_reserve,
-            mint_settings,
-            melt_settings,
             wait_invoice_cancel_token: CancellationToken::new(),
             wait_invoice_is_active: Arc::new(AtomicBool::new(false)),
         })
@@ -81,8 +69,6 @@ impl MintLightning for Cln {
         Settings {
             mpp: true,
             unit: CurrencyUnit::Msat,
-            mint_settings: self.mint_settings.clone(),
-            melt_settings: self.melt_settings.clone(),
             invoice_description: true,
         }
     }

+ 1 - 12
crates/cdk-fake-wallet/src/lib.rs

@@ -20,10 +20,7 @@ use cdk::cdk_lightning::{
 };
 use cdk::mint;
 use cdk::mint::FeeReserve;
-use cdk::nuts::{
-    CurrencyUnit, MeltMethodSettings, MeltQuoteBolt11Request, MeltQuoteState, MintMethodSettings,
-    MintQuoteState,
-};
+use cdk::nuts::{CurrencyUnit, MeltQuoteBolt11Request, MeltQuoteState, MintQuoteState};
 use cdk::util::unix_time;
 use error::Error;
 use futures::stream::StreamExt;
@@ -44,8 +41,6 @@ pub struct FakeWallet {
     fee_reserve: FeeReserve,
     sender: tokio::sync::mpsc::Sender<String>,
     receiver: Arc<Mutex<Option<tokio::sync::mpsc::Receiver<String>>>>,
-    mint_settings: MintMethodSettings,
-    melt_settings: MeltMethodSettings,
     payment_states: Arc<Mutex<HashMap<String, MeltQuoteState>>>,
     failed_payment_check: Arc<Mutex<HashSet<String>>>,
     payment_delay: u64,
@@ -57,8 +52,6 @@ impl FakeWallet {
     /// Creat new [`FakeWallet`]
     pub fn new(
         fee_reserve: FeeReserve,
-        mint_settings: MintMethodSettings,
-        melt_settings: MeltMethodSettings,
         payment_states: HashMap<String, MeltQuoteState>,
         fail_payment_check: HashSet<String>,
         payment_delay: u64,
@@ -69,8 +62,6 @@ impl FakeWallet {
             fee_reserve,
             sender,
             receiver: Arc::new(Mutex::new(Some(receiver))),
-            mint_settings,
-            melt_settings,
             payment_states: Arc::new(Mutex::new(payment_states)),
             failed_payment_check: Arc::new(Mutex::new(fail_payment_check)),
             payment_delay,
@@ -112,8 +103,6 @@ impl MintLightning for FakeWallet {
         Settings {
             mpp: true,
             unit: CurrencyUnit::Msat,
-            mint_settings: self.mint_settings.clone(),
-            melt_settings: self.melt_settings.clone(),
             invoice_description: true,
         }
     }

+ 2 - 9
crates/cdk-integration-tests/src/init_fake_wallet.rs

@@ -9,7 +9,7 @@ use cdk::{
     cdk_database::{self, MintDatabase},
     cdk_lightning::MintLightning,
     mint::FeeReserve,
-    nuts::{CurrencyUnit, MeltMethodSettings, MintMethodSettings},
+    nuts::CurrencyUnit,
     types::LnKey,
 };
 use cdk_fake_wallet::FakeWallet;
@@ -46,14 +46,7 @@ where
         percent_fee_reserve: 1.0,
     };
 
-    let fake_wallet = FakeWallet::new(
-        fee_reserve,
-        MintMethodSettings::default(),
-        MeltMethodSettings::default(),
-        HashMap::default(),
-        HashSet::default(),
-        0,
-    );
+    let fake_wallet = FakeWallet::new(fee_reserve, HashMap::default(), HashSet::default(), 0);
 
     ln_backends.insert(
         LnKey::new(CurrencyUnit::Sat, cdk::nuts::PaymentMethod::Bolt11),

+ 2 - 8
crates/cdk-integration-tests/src/init_regtest.rs

@@ -7,7 +7,7 @@ use cdk::{
     cdk_database::{self, MintDatabase},
     cdk_lightning::MintLightning,
     mint::{FeeReserve, Mint},
-    nuts::{CurrencyUnit, MeltMethodSettings, MintInfo, MintMethodSettings},
+    nuts::{CurrencyUnit, MintInfo},
     types::{LnKey, QuoteTTL},
 };
 use cdk_cln::Cln as CdkCln;
@@ -131,13 +131,7 @@ pub async fn create_cln_backend(cln_client: &ClnClient) -> Result<CdkCln> {
         percent_fee_reserve: 1.0,
     };
 
-    Ok(CdkCln::new(
-        rpc_path,
-        fee_reserve,
-        MintMethodSettings::default(),
-        MeltMethodSettings::default(),
-    )
-    .await?)
+    Ok(CdkCln::new(rpc_path, fee_reserve).await?)
 }
 
 pub async fn create_mint<D>(

+ 2 - 4
crates/cdk-integration-tests/src/lib.rs

@@ -11,8 +11,8 @@ use cdk::cdk_lightning::MintLightning;
 use cdk::dhke::construct_proofs;
 use cdk::mint::FeeReserve;
 use cdk::nuts::{
-    CurrencyUnit, Id, KeySet, MeltMethodSettings, MintBolt11Request, MintInfo, MintMethodSettings,
-    MintQuoteBolt11Request, MintQuoteState, Nuts, PaymentMethod, PreMintSecrets, Proofs, State,
+    CurrencyUnit, Id, KeySet, MintBolt11Request, MintInfo, MintQuoteBolt11Request, MintQuoteState,
+    Nuts, PaymentMethod, PreMintSecrets, Proofs, State,
 };
 use cdk::types::{LnKey, QuoteTTL};
 use cdk::wallet::client::{HttpClient, HttpClientMethods};
@@ -40,8 +40,6 @@ pub fn create_backends_fake_wallet(
 
     let wallet = Arc::new(FakeWallet::new(
         fee_reserve.clone(),
-        MintMethodSettings::default(),
-        MeltMethodSettings::default(),
         HashMap::default(),
         HashSet::default(),
         0,

+ 1 - 12
crates/cdk-lnbits/src/lib.rs

@@ -15,10 +15,7 @@ use cdk::cdk_lightning::{
     self, CreateInvoiceResponse, MintLightning, PayInvoiceResponse, PaymentQuoteResponse, Settings,
 };
 use cdk::mint::FeeReserve;
-use cdk::nuts::{
-    CurrencyUnit, MeltMethodSettings, MeltQuoteBolt11Request, MeltQuoteState, MintMethodSettings,
-    MintQuoteState,
-};
+use cdk::nuts::{CurrencyUnit, MeltQuoteBolt11Request, MeltQuoteState, MintQuoteState};
 use cdk::util::unix_time;
 use cdk::{mint, Bolt11Invoice};
 use error::Error;
@@ -35,8 +32,6 @@ pub mod error;
 #[derive(Clone)]
 pub struct LNbits {
     lnbits_api: LNBitsClient,
-    mint_settings: MintMethodSettings,
-    melt_settings: MeltMethodSettings,
     fee_reserve: FeeReserve,
     receiver: Arc<Mutex<Option<tokio::sync::mpsc::Receiver<String>>>>,
     webhook_url: String,
@@ -51,8 +46,6 @@ impl LNbits {
         admin_api_key: String,
         invoice_api_key: String,
         api_url: String,
-        mint_settings: MintMethodSettings,
-        melt_settings: MeltMethodSettings,
         fee_reserve: FeeReserve,
         receiver: Arc<Mutex<Option<tokio::sync::mpsc::Receiver<String>>>>,
         webhook_url: String,
@@ -61,8 +54,6 @@ impl LNbits {
 
         Ok(Self {
             lnbits_api,
-            mint_settings,
-            melt_settings,
             receiver,
             fee_reserve,
             webhook_url,
@@ -80,8 +71,6 @@ impl MintLightning for LNbits {
         Settings {
             mpp: false,
             unit: CurrencyUnit::Sat,
-            mint_settings: self.mint_settings.clone(),
-            melt_settings: self.melt_settings.clone(),
             invoice_description: true,
         }
     }

+ 1 - 12
crates/cdk-lnd/src/lib.rs

@@ -18,10 +18,7 @@ use cdk::cdk_lightning::{
     self, CreateInvoiceResponse, MintLightning, PayInvoiceResponse, PaymentQuoteResponse, Settings,
 };
 use cdk::mint::FeeReserve;
-use cdk::nuts::{
-    CurrencyUnit, MeltMethodSettings, MeltQuoteBolt11Request, MeltQuoteState, MintMethodSettings,
-    MintQuoteState,
-};
+use cdk::nuts::{CurrencyUnit, MeltQuoteBolt11Request, MeltQuoteState, MintQuoteState};
 use cdk::util::{hex, unix_time};
 use cdk::{mint, Bolt11Invoice};
 use error::Error;
@@ -43,8 +40,6 @@ pub struct Lnd {
     macaroon_file: PathBuf,
     client: Arc<Mutex<Client>>,
     fee_reserve: FeeReserve,
-    mint_settings: MintMethodSettings,
-    melt_settings: MeltMethodSettings,
     wait_invoice_cancel_token: CancellationToken,
     wait_invoice_is_active: Arc<AtomicBool>,
 }
@@ -56,8 +51,6 @@ impl Lnd {
         cert_file: PathBuf,
         macaroon_file: PathBuf,
         fee_reserve: FeeReserve,
-        mint_settings: MintMethodSettings,
-        melt_settings: MeltMethodSettings,
     ) -> Result<Self, Error> {
         let client = fedimint_tonic_lnd::connect(address.to_string(), &cert_file, &macaroon_file)
             .await
@@ -72,8 +65,6 @@ impl Lnd {
             macaroon_file,
             client: Arc::new(Mutex::new(client)),
             fee_reserve,
-            mint_settings,
-            melt_settings,
             wait_invoice_cancel_token: CancellationToken::new(),
             wait_invoice_is_active: Arc::new(AtomicBool::new(false)),
         })
@@ -88,8 +79,6 @@ impl MintLightning for Lnd {
         Settings {
             mpp: true,
             unit: CurrencyUnit::Msat,
-            mint_settings: self.mint_settings.clone(),
-            melt_settings: self.melt_settings.clone(),
             invoice_description: true,
         }
     }

+ 32 - 3
crates/cdk-mintd/src/config.rs

@@ -35,12 +35,27 @@ pub enum LnBackend {
     Lnd,
 }
 
-#[derive(Debug, Clone, Serialize, Deserialize, Default)]
+#[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct Ln {
     pub ln_backend: LnBackend,
     pub invoice_description: Option<String>,
-    pub fee_percent: f32,
-    pub reserve_fee_min: Amount,
+    pub min_mint: Amount,
+    pub max_mint: Amount,
+    pub min_melt: Amount,
+    pub max_melt: Amount,
+}
+
+impl Default for Ln {
+    fn default() -> Self {
+        Ln {
+            ln_backend: LnBackend::default(),
+            invoice_description: None,
+            min_mint: 1.into(),
+            max_mint: 500_000.into(),
+            min_melt: 1.into(),
+            max_melt: 500_000.into(),
+        }
+    }
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize, Default)]
@@ -54,11 +69,16 @@ pub struct LNbits {
     pub admin_api_key: String,
     pub invoice_api_key: String,
     pub lnbits_api: String,
+    pub fee_percent: f32,
+    pub reserve_fee_min: Amount,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize, Default)]
 pub struct Cln {
     pub rpc_path: PathBuf,
+    pub bolt12: bool,
+    pub fee_percent: f32,
+    pub reserve_fee_min: Amount,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize, Default)]
@@ -66,23 +86,32 @@ pub struct Lnd {
     pub address: String,
     pub cert_file: PathBuf,
     pub macaroon_file: PathBuf,
+    pub fee_percent: f32,
+    pub reserve_fee_min: Amount,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize, Default)]
 pub struct Phoenixd {
     pub api_password: String,
     pub api_url: String,
+    pub bolt12: bool,
+    pub fee_percent: f32,
+    pub reserve_fee_min: Amount,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct FakeWallet {
     pub supported_units: Vec<CurrencyUnit>,
+    pub fee_percent: f32,
+    pub reserve_fee_min: Amount,
 }
 
 impl Default for FakeWallet {
     fn default() -> Self {
         Self {
             supported_units: vec![CurrencyUnit::Sat],
+            fee_percent: 0.02,
+            reserve_fee_min: 2.into(),
         }
     }
 }

+ 22 - 0
crates/cdk-mintd/src/lib.rs

@@ -0,0 +1,22 @@
+//! Cdk mintd lib
+
+use std::path::PathBuf;
+
+pub mod cli;
+pub mod config;
+pub mod setup;
+
+fn expand_path(path: &str) -> Option<PathBuf> {
+    if path.starts_with('~') {
+        if let Some(home_dir) = home::home_dir().as_mut() {
+            let remainder = &path[2..];
+            home_dir.push(remainder);
+            let expanded_path = home_dir;
+            Some(expanded_path.clone())
+        } else {
+            None
+        }
+    } else {
+        Some(PathBuf::from(path))
+    }
+}

+ 112 - 301
crates/cdk-mintd/src/main.rs

@@ -3,7 +3,7 @@
 #![warn(missing_docs)]
 #![warn(rustdoc::bare_urls)]
 
-use std::collections::{HashMap, HashSet};
+use std::collections::HashMap;
 use std::path::PathBuf;
 use std::str::FromStr;
 use std::sync::Arc;
@@ -14,33 +14,22 @@ use bip39::Mnemonic;
 use cdk::cdk_database::{self, MintDatabase};
 use cdk::cdk_lightning;
 use cdk::cdk_lightning::MintLightning;
-use cdk::mint::{FeeReserve, MeltQuote, Mint};
-use cdk::mint_url::MintUrl;
-use cdk::nuts::{
-    nut04, nut05, ContactInfo, CurrencyUnit, MeltMethodSettings, MeltQuoteState, MintInfo,
-    MintMethodSettings, MintVersion, MppMethodSettings, Nuts, PaymentMethod,
-};
-use cdk::types::{LnKey, QuoteTTL};
-use cdk_cln::Cln;
-use cdk_fake_wallet::FakeWallet;
-use cdk_lnbits::LNbits;
-use cdk_lnd::Lnd;
-use cdk_phoenixd::Phoenixd;
+use cdk::mint::{MeltQuote, Mint};
+use cdk::mint::{MintBuilder, MintMeltLimits};
+use cdk::nuts::{ContactInfo, CurrencyUnit, MeltQuoteState, MintVersion, PaymentMethod};
+use cdk::types::LnKey;
+use cdk_mintd::setup::LnBackendSetup;
 use cdk_redb::MintRedbDatabase;
 use cdk_sqlite::MintSqliteDatabase;
-use cdk_strike::Strike;
 use clap::Parser;
-use cli::CLIArgs;
-use config::{DatabaseEngine, LnBackend};
-use tokio::sync::{Mutex, Notify};
+use tokio::sync::Notify;
 use tower_http::cors::CorsLayer;
 use tracing_subscriber::EnvFilter;
-use url::Url;
 #[cfg(feature = "swagger")]
 use utoipa::OpenApi;
 
-mod cli;
-mod config;
+use cdk_mintd::cli::CLIArgs;
+use cdk_mintd::config::{self, DatabaseEngine, LnBackend};
 
 const CARGO_PKG_VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
 const DEFAULT_QUOTE_TTL_SECS: u64 = 1800;
@@ -74,6 +63,8 @@ async fn main() -> anyhow::Result<()> {
         None => work_dir.join("config.toml"),
     };
 
+    let mut mint_builder = MintBuilder::new();
+
     let settings = config::Settings::new(&Some(config_file_arg));
 
     let localstore: Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync> =
@@ -92,6 +83,8 @@ async fn main() -> anyhow::Result<()> {
             }
         };
 
+    mint_builder = mint_builder.with_localstore(localstore);
+
     let mut contact_info: Option<Vec<ContactInfo>> = None;
 
     if let Some(nostr_contact) = &settings.mint_info.contact_nostr_public_key {
@@ -123,324 +116,155 @@ async fn main() -> anyhow::Result<()> {
         CARGO_PKG_VERSION.unwrap_or("Unknown").to_string(),
     );
 
-    let relative_ln_fee = settings.ln.fee_percent;
-
-    let absolute_ln_fee_reserve = settings.ln.reserve_fee_min;
-
-    let fee_reserve = FeeReserve {
-        min_fee_reserve: absolute_ln_fee_reserve,
-        percent_fee_reserve: relative_ln_fee,
-    };
-
     let mut ln_backends: HashMap<
         LnKey,
         Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>,
     > = HashMap::new();
+    let mut ln_routers = vec![];
 
-    let mut supported_units = HashMap::new();
-    let input_fee_ppk = settings.info.input_fee_ppk.unwrap_or(0);
-
-    let mint_url: MintUrl = settings.info.url.parse()?;
+    let mint_melt_limits = MintMeltLimits {
+        mint_min: settings.ln.min_mint,
+        mint_max: settings.ln.max_mint,
+        melt_min: settings.ln.min_melt,
+        melt_max: settings.ln.max_melt,
+    };
 
-    let ln_routers: Vec<Router> = match settings.ln.ln_backend {
+    match settings.ln.ln_backend {
         LnBackend::Cln => {
-            let cln_socket = expand_path(
-                settings
-                    .cln
-                    .expect("Config checked at load that cln is some")
-                    .rpc_path
-                    .to_str()
-                    .ok_or(anyhow!("cln socket not defined"))?,
-            )
-            .ok_or(anyhow!("cln socket not defined"))?;
-            let cln = Arc::new(
-                Cln::new(
-                    cln_socket,
-                    fee_reserve,
-                    MintMethodSettings::default(),
-                    MeltMethodSettings::default(),
-                )
-                .await?,
-            );
+            let cln_settings = settings
+                .cln
+                .clone()
+                .expect("Config checked at load that cln is some");
 
-            ln_backends.insert(LnKey::new(CurrencyUnit::Sat, PaymentMethod::Bolt11), cln);
-            supported_units.insert(CurrencyUnit::Sat, (input_fee_ppk, 64));
-            vec![]
+            let cln = cln_settings
+                .setup(&mut ln_routers, &settings, CurrencyUnit::Msat)
+                .await?;
+            let cln = Arc::new(cln);
+            let ln_key = LnKey {
+                unit: CurrencyUnit::Sat,
+                method: PaymentMethod::Bolt11,
+            };
+            ln_backends.insert(ln_key, cln.clone());
+
+            mint_builder = mint_builder.add_ln_backend(
+                CurrencyUnit::Sat,
+                PaymentMethod::Bolt11,
+                mint_melt_limits,
+                cln.clone(),
+            );
         }
         LnBackend::Strike => {
-            let strike_settings = settings.strike.expect("Checked on config load");
-            let api_key = strike_settings.api_key;
+            let strike_settings = settings.clone().strike.expect("Checked on config load");
 
-            let units = strike_settings
+            for unit in strike_settings
+                .clone()
                 .supported_units
-                .unwrap_or(vec![CurrencyUnit::Sat]);
-
-            let mut routers = vec![];
-
-            for unit in units {
-                // Channel used for strike web hook
-                let (sender, receiver) = tokio::sync::mpsc::channel(8);
-                let webhook_endpoint = format!("/webhook/{}/invoice", unit);
-
-                let webhook_url = mint_url.join(&webhook_endpoint)?;
-
-                let strike = Strike::new(
-                    api_key.clone(),
-                    MintMethodSettings::default(),
-                    MeltMethodSettings::default(),
-                    unit.clone(),
-                    Arc::new(Mutex::new(Some(receiver))),
-                    webhook_url.to_string(),
-                )
-                .await?;
-
-                let router = strike
-                    .create_invoice_webhook(&webhook_endpoint, sender)
+                .unwrap_or(vec![CurrencyUnit::Sat])
+            {
+                let strike = strike_settings
+                    .setup(&mut ln_routers, &settings, unit.clone())
                     .await?;
-                routers.push(router);
-
-                let ln_key = LnKey::new(unit.clone(), PaymentMethod::Bolt11);
-
-                ln_backends.insert(ln_key, Arc::new(strike));
 
-                supported_units.insert(unit, (input_fee_ppk, 64));
+                mint_builder = mint_builder.add_ln_backend(
+                    unit,
+                    PaymentMethod::Bolt11,
+                    mint_melt_limits,
+                    Arc::new(strike),
+                );
             }
-
-            routers
         }
         LnBackend::LNbits => {
-            let lnbits_settings = settings.lnbits.expect("Checked on config load");
-            let admin_api_key = lnbits_settings.admin_api_key;
-            let invoice_api_key = lnbits_settings.invoice_api_key;
-
-            // Channel used for lnbits web hook
-            let (sender, receiver) = tokio::sync::mpsc::channel(8);
-            let webhook_endpoint = "/webhook/lnbits/sat/invoice";
-
-            let webhook_url = mint_url.join(webhook_endpoint)?;
-
-            let lnbits = LNbits::new(
-                admin_api_key,
-                invoice_api_key,
-                lnbits_settings.lnbits_api,
-                MintMethodSettings::default(),
-                MeltMethodSettings::default(),
-                fee_reserve,
-                Arc::new(Mutex::new(Some(receiver))),
-                webhook_url.to_string(),
-            )
-            .await?;
-
-            let router = lnbits
-                .create_invoice_webhook_router(webhook_endpoint, sender)
+            let lnbits_settings = settings.clone().lnbits.expect("Checked on config load");
+            let lnbits = lnbits_settings
+                .setup(&mut ln_routers, &settings, CurrencyUnit::Sat)
                 .await?;
 
-            let unit = CurrencyUnit::Sat;
-
-            let ln_key = LnKey::new(unit.clone(), PaymentMethod::Bolt11);
-
-            ln_backends.insert(ln_key, Arc::new(lnbits));
-
-            supported_units.insert(unit, (input_fee_ppk, 64));
-            vec![router]
+            mint_builder = mint_builder.add_ln_backend(
+                CurrencyUnit::Sat,
+                PaymentMethod::Bolt11,
+                mint_melt_limits,
+                Arc::new(lnbits),
+            );
         }
         LnBackend::Phoenixd => {
-            let api_password = settings
-                .clone()
-                .phoenixd
-                .expect("Checked at config load")
-                .api_password;
-
-            let api_url = settings
-                .clone()
-                .phoenixd
-                .expect("Checked at config load")
-                .api_url;
-
-            if fee_reserve.percent_fee_reserve < 0.04 {
-                bail!("Fee reserve is too low needs to be at least 0.02");
-            }
-
-            let webhook_endpoint = "/webhook/phoenixd";
-
-            let mint_url = Url::parse(&settings.info.url)?;
-
-            let webhook_url = mint_url.join(webhook_endpoint)?.to_string();
-
-            let (sender, receiver) = tokio::sync::mpsc::channel(8);
-
-            let phoenixd = Phoenixd::new(
-                api_password.to_string(),
-                api_url.to_string(),
-                MintMethodSettings::default(),
-                MeltMethodSettings::default(),
-                fee_reserve,
-                Arc::new(Mutex::new(Some(receiver))),
-                webhook_url,
-            )?;
-
-            let router = phoenixd
-                .create_invoice_webhook(webhook_endpoint, sender)
+            let phd_settings = settings.clone().phoenixd.expect("Checked at config load");
+            let phd = phd_settings
+                .setup(&mut ln_routers, &settings, CurrencyUnit::Sat)
                 .await?;
 
-            supported_units.insert(CurrencyUnit::Sat, (input_fee_ppk, 64));
-            ln_backends.insert(
-                LnKey {
-                    unit: CurrencyUnit::Sat,
-                    method: PaymentMethod::Bolt11,
-                },
-                Arc::new(phoenixd),
+            mint_builder = mint_builder.add_ln_backend(
+                CurrencyUnit::Sat,
+                PaymentMethod::Bolt11,
+                mint_melt_limits,
+                Arc::new(phd),
             );
-
-            vec![router]
         }
         LnBackend::Lnd => {
-            let lnd_settings = settings.lnd.expect("Checked at config load");
-
-            let address = lnd_settings.address;
-            let cert_file = lnd_settings.cert_file;
-            let macaroon_file = lnd_settings.macaroon_file;
-
-            let lnd = Lnd::new(
-                address,
-                cert_file,
-                macaroon_file,
-                fee_reserve,
-                MintMethodSettings::default(),
-                MeltMethodSettings::default(),
-            )
-            .await?;
+            let lnd_settings = settings.clone().lnd.expect("Checked at config load");
+            let lnd = lnd_settings
+                .setup(&mut ln_routers, &settings, CurrencyUnit::Msat)
+                .await?;
 
-            supported_units.insert(CurrencyUnit::Sat, (input_fee_ppk, 64));
-            ln_backends.insert(
-                LnKey {
-                    unit: CurrencyUnit::Sat,
-                    method: PaymentMethod::Bolt11,
-                },
+            mint_builder = mint_builder.add_ln_backend(
+                CurrencyUnit::Sat,
+                PaymentMethod::Bolt11,
+                mint_melt_limits,
                 Arc::new(lnd),
             );
-
-            vec![]
         }
         LnBackend::FakeWallet => {
-            let units = settings.fake_wallet.unwrap_or_default().supported_units;
+            let fake_wallet = settings.clone().fake_wallet.expect("Fake wallet defined");
 
-            for unit in units {
-                let ln_key = LnKey::new(unit.clone(), PaymentMethod::Bolt11);
-
-                let wallet = Arc::new(FakeWallet::new(
-                    fee_reserve.clone(),
-                    MintMethodSettings::default(),
-                    MeltMethodSettings::default(),
-                    HashMap::default(),
-                    HashSet::default(),
-                    0,
-                ));
+            for unit in fake_wallet.clone().supported_units {
+                let fake = fake_wallet
+                    .setup(&mut ln_routers, &settings, CurrencyUnit::Sat)
+                    .await?;
 
-                ln_backends.insert(ln_key, wallet);
+                let fake = Arc::new(fake);
 
-                supported_units.insert(unit, (input_fee_ppk, 64));
+                mint_builder = mint_builder.add_ln_backend(
+                    unit.clone(),
+                    PaymentMethod::Bolt11,
+                    mint_melt_limits,
+                    fake.clone(),
+                );
             }
-
-            vec![]
         }
     };
 
-    let (nut04_settings, nut05_settings, mpp_settings): (
-        nut04::Settings,
-        nut05::Settings,
-        Vec<MppMethodSettings>,
-    ) = ln_backends.iter().fold(
-        (
-            nut04::Settings::new(vec![], false),
-            nut05::Settings::new(vec![], false),
-            Vec::new(),
-        ),
-        |(mut nut_04, mut nut_05, mut mpp), (key, ln)| {
-            let settings = ln.get_settings();
-
-            let m = MppMethodSettings {
-                method: key.method,
-                unit: key.unit.clone(),
-                mpp: settings.mpp,
-            };
-
-            let n4 = MintMethodSettings {
-                method: key.method,
-                unit: key.unit.clone(),
-                min_amount: settings.mint_settings.min_amount,
-                max_amount: settings.mint_settings.max_amount,
-                description: settings.invoice_description,
-            };
-
-            let n5 = MeltMethodSettings {
-                method: key.method,
-                unit: key.unit.clone(),
-                min_amount: settings.melt_settings.min_amount,
-                max_amount: settings.melt_settings.max_amount,
-            };
-
-            nut_04.methods.push(n4);
-            nut_05.methods.push(n5);
-            mpp.push(m);
-
-            (nut_04, nut_05, mpp)
-        },
-    );
-
-    let nuts = Nuts::new()
-        .nut04(nut04_settings)
-        .nut05(nut05_settings)
-        .nut07(true)
-        .nut08(true)
-        .nut09(true)
-        .nut10(true)
-        .nut11(true)
-        .nut12(true)
-        .nut14(true)
-        .nut15(mpp_settings);
-
-    let mut mint_info = MintInfo::new()
-        .name(settings.mint_info.name)
-        .version(mint_version)
-        .description(settings.mint_info.description)
-        .nuts(nuts);
-
     if let Some(long_description) = &settings.mint_info.description_long {
-        mint_info = mint_info.long_description(long_description);
+        mint_builder = mint_builder.with_long_description(long_description.to_string());
     }
 
     if let Some(contact_info) = contact_info {
-        mint_info = mint_info.contact_info(contact_info);
+        for info in contact_info {
+            mint_builder = mint_builder.add_contact_info(info);
+        }
     }
 
     if let Some(pubkey) = settings.mint_info.pubkey {
-        mint_info = mint_info.pubkey(pubkey);
+        mint_builder = mint_builder.with_pubkey(pubkey);
     }
 
     if let Some(icon_url) = &settings.mint_info.icon_url {
-        mint_info = mint_info.icon_url(icon_url);
+        mint_builder = mint_builder.with_icon_url(icon_url.to_string());
     }
 
     if let Some(motd) = settings.mint_info.motd {
-        mint_info = mint_info.motd(motd);
+        mint_builder = mint_builder.with_motd(motd);
     }
 
     let mnemonic = Mnemonic::from_str(&settings.info.mnemonic)?;
 
-    let quote_ttl = QuoteTTL::new(10000, 10000);
-
-    let mint = Mint::new(
-        &settings.info.url,
-        &mnemonic.to_seed_normalized(""),
-        mint_info,
-        quote_ttl,
-        localstore,
-        ln_backends.clone(),
-        supported_units,
-        HashMap::new(),
-    )
-    .await?;
+    mint_builder = mint_builder
+        .with_name(settings.mint_info.name)
+        .with_mint_url(settings.info.url)
+        .with_version(mint_version)
+        .with_description(settings.mint_info.description)
+        .with_quote_ttl(10000, 10000)
+        .with_seed(mnemonic.to_seed_normalized("").to_vec());
+
+    let mint = mint_builder.build().await?;
 
     let mint = Arc::new(mint);
 
@@ -530,14 +354,14 @@ async fn check_pending_mint_quotes(
     ln: Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>,
 ) -> Result<()> {
     let mut pending_quotes = mint.get_pending_mint_quotes().await?;
-    tracing::trace!("There are {} pending mint quotes.", pending_quotes.len());
+    tracing::info!("There are {} pending mint quotes.", pending_quotes.len());
     let mut unpaid_quotes = mint.get_unpaid_mint_quotes().await?;
-    tracing::trace!("There are {} unpaid mint quotes.", unpaid_quotes.len());
+    tracing::info!("There are {} unpaid mint quotes.", unpaid_quotes.len());
 
     unpaid_quotes.append(&mut pending_quotes);
 
     for quote in unpaid_quotes {
-        tracing::trace!("Checking status of mint quote: {}", quote.id);
+        tracing::debug!("Checking status of mint quote: {}", quote.id);
         let lookup_id = quote.request_lookup_id;
         match ln.check_incoming_invoice_status(&lookup_id).await {
             Ok(state) => {
@@ -568,8 +392,10 @@ async fn check_pending_melt_quotes(
         .into_iter()
         .filter(|q| q.state == MeltQuoteState::Pending || q.state == MeltQuoteState::Unknown)
         .collect();
+    tracing::info!("There are {} pending melt quotes.", pending_quotes.len());
 
     for pending_quote in pending_quotes {
+        tracing::debug!("Checking status for melt quote {}.", pending_quote.id);
         let melt_request_ln_key = mint.localstore.get_melt_request(&pending_quote.id).await?;
 
         let (melt_request, ln_key) = match melt_request_ln_key {
@@ -646,21 +472,6 @@ async fn check_pending_melt_quotes(
     Ok(())
 }
 
-fn expand_path(path: &str) -> Option<PathBuf> {
-    if path.starts_with('~') {
-        if let Some(home_dir) = home::home_dir().as_mut() {
-            let remainder = &path[2..];
-            home_dir.push(remainder);
-            let expanded_path = home_dir;
-            Some(expanded_path.clone())
-        } else {
-            None
-        }
-    } else {
-        Some(PathBuf::from(path))
-    }
-}
-
 fn work_dir() -> Result<PathBuf> {
     let home_dir = home::home_dir().ok_or(anyhow!("Unknown home dir"))?;
 

+ 229 - 0
crates/cdk-mintd/src/setup.rs

@@ -0,0 +1,229 @@
+use std::{
+    collections::{HashMap, HashSet},
+    sync::Arc,
+};
+
+use anyhow::{anyhow, bail};
+use axum::{async_trait, Router};
+
+use cdk::{cdk_lightning::MintLightning, mint::FeeReserve, mint_url::MintUrl, nuts::CurrencyUnit};
+use tokio::sync::Mutex;
+use url::Url;
+
+use crate::{
+    config::{self, Settings},
+    expand_path,
+};
+
+#[async_trait]
+pub trait LnBackendSetup {
+    async fn setup(
+        &self,
+        routers: &mut Vec<Router>,
+        settings: &Settings,
+        unit: CurrencyUnit,
+    ) -> anyhow::Result<impl MintLightning>;
+}
+
+#[async_trait]
+impl LnBackendSetup for config::Cln {
+    async fn setup(
+        &self,
+        _routers: &mut Vec<Router>,
+        _settings: &Settings,
+        _unit: CurrencyUnit,
+    ) -> anyhow::Result<cdk_cln::Cln> {
+        let cln_socket = expand_path(
+            self.rpc_path
+                .to_str()
+                .ok_or(anyhow!("cln socket not defined"))?,
+        )
+        .ok_or(anyhow!("cln socket not defined"))?;
+
+        let fee_reserve = FeeReserve {
+            min_fee_reserve: self.reserve_fee_min,
+            percent_fee_reserve: self.fee_percent,
+        };
+
+        let cln = cdk_cln::Cln::new(cln_socket, fee_reserve).await?;
+
+        Ok(cln)
+    }
+}
+
+#[async_trait]
+impl LnBackendSetup for config::Strike {
+    async fn setup(
+        &self,
+        routers: &mut Vec<Router>,
+        settings: &Settings,
+        unit: CurrencyUnit,
+    ) -> anyhow::Result<cdk_strike::Strike> {
+        let api_key = &self.api_key;
+
+        // Channel used for strike web hook
+        let (sender, receiver) = tokio::sync::mpsc::channel(8);
+        let webhook_endpoint = format!("/webhook/{}/invoice", unit);
+
+        let mint_url: MintUrl = settings.info.url.parse()?;
+        let webhook_url = mint_url.join(&webhook_endpoint)?;
+
+        let strike = cdk_strike::Strike::new(
+            api_key.clone(),
+            unit,
+            Arc::new(Mutex::new(Some(receiver))),
+            webhook_url.to_string(),
+        )
+        .await?;
+
+        let router = strike
+            .create_invoice_webhook(&webhook_endpoint, sender)
+            .await?;
+        routers.push(router);
+
+        Ok(strike)
+    }
+}
+
+#[async_trait]
+impl LnBackendSetup for config::LNbits {
+    async fn setup(
+        &self,
+        routers: &mut Vec<Router>,
+        settings: &Settings,
+        _unit: CurrencyUnit,
+    ) -> anyhow::Result<cdk_lnbits::LNbits> {
+        let admin_api_key = &self.admin_api_key;
+        let invoice_api_key = &self.invoice_api_key;
+
+        // Channel used for lnbits web hook
+        let (sender, receiver) = tokio::sync::mpsc::channel(8);
+        let webhook_endpoint = "/webhook/lnbits/sat/invoice";
+
+        let mint_url: MintUrl = settings.info.url.parse()?;
+        let webhook_url = mint_url.join(webhook_endpoint)?;
+
+        let fee_reserve = FeeReserve {
+            min_fee_reserve: self.reserve_fee_min,
+            percent_fee_reserve: self.fee_percent,
+        };
+
+        let lnbits = cdk_lnbits::LNbits::new(
+            admin_api_key.clone(),
+            invoice_api_key.clone(),
+            self.lnbits_api.clone(),
+            fee_reserve,
+            Arc::new(Mutex::new(Some(receiver))),
+            webhook_url.to_string(),
+        )
+        .await?;
+
+        let router = lnbits
+            .create_invoice_webhook_router(webhook_endpoint, sender)
+            .await?;
+
+        routers.push(router);
+
+        Ok(lnbits)
+    }
+}
+
+#[async_trait]
+impl LnBackendSetup for config::Phoenixd {
+    async fn setup(
+        &self,
+        routers: &mut Vec<Router>,
+        settings: &Settings,
+        _unit: CurrencyUnit,
+    ) -> anyhow::Result<cdk_phoenixd::Phoenixd> {
+        let api_password = &self.api_password;
+
+        let api_url = &self.api_url;
+
+        let fee_reserve = FeeReserve {
+            min_fee_reserve: self.reserve_fee_min,
+            percent_fee_reserve: self.fee_percent,
+        };
+
+        if fee_reserve.percent_fee_reserve < 0.04 {
+            bail!("Fee reserve is too low needs to be at least 0.02");
+        }
+
+        let webhook_endpoint = "/webhook/phoenixd";
+
+        let mint_url = Url::parse(&settings.info.url)?;
+
+        let webhook_url = mint_url.join(webhook_endpoint)?.to_string();
+
+        let (sender, receiver) = tokio::sync::mpsc::channel(8);
+
+        let phoenixd = cdk_phoenixd::Phoenixd::new(
+            api_password.to_string(),
+            api_url.to_string(),
+            fee_reserve,
+            Arc::new(Mutex::new(Some(receiver))),
+            webhook_url,
+        )?;
+
+        let router = phoenixd
+            .create_invoice_webhook(webhook_endpoint, sender)
+            .await?;
+
+        routers.push(router);
+
+        Ok(phoenixd)
+    }
+}
+
+#[async_trait]
+impl LnBackendSetup for config::Lnd {
+    async fn setup(
+        &self,
+        _routers: &mut Vec<Router>,
+        _settings: &Settings,
+        _unit: CurrencyUnit,
+    ) -> anyhow::Result<cdk_lnd::Lnd> {
+        let address = &self.address;
+        let cert_file = &self.cert_file;
+        let macaroon_file = &self.macaroon_file;
+
+        let fee_reserve = FeeReserve {
+            min_fee_reserve: self.reserve_fee_min,
+            percent_fee_reserve: self.fee_percent,
+        };
+
+        let lnd = cdk_lnd::Lnd::new(
+            address.to_string(),
+            cert_file.clone(),
+            macaroon_file.clone(),
+            fee_reserve,
+        )
+        .await?;
+
+        Ok(lnd)
+    }
+}
+
+#[async_trait]
+impl LnBackendSetup for config::FakeWallet {
+    async fn setup(
+        &self,
+        _router: &mut Vec<Router>,
+        _settings: &Settings,
+        _unit: CurrencyUnit,
+    ) -> anyhow::Result<cdk_fake_wallet::FakeWallet> {
+        let fee_reserve = FeeReserve {
+            min_fee_reserve: self.reserve_fee_min,
+            percent_fee_reserve: self.fee_percent,
+        };
+
+        let fake_wallet = cdk_fake_wallet::FakeWallet::new(
+            fee_reserve,
+            HashMap::default(),
+            HashSet::default(),
+            0,
+        );
+
+        Ok(fake_wallet)
+    }
+}

+ 1 - 12
crates/cdk-phoenixd/src/lib.rs

@@ -15,10 +15,7 @@ use cdk::cdk_lightning::{
     self, CreateInvoiceResponse, MintLightning, PayInvoiceResponse, PaymentQuoteResponse, Settings,
 };
 use cdk::mint::FeeReserve;
-use cdk::nuts::{
-    CurrencyUnit, MeltMethodSettings, MeltQuoteBolt11Request, MeltQuoteState, MintMethodSettings,
-    MintQuoteState,
-};
+use cdk::nuts::{CurrencyUnit, MeltQuoteBolt11Request, MeltQuoteState, MintQuoteState};
 use cdk::{mint, Bolt11Invoice};
 use error::Error;
 use futures::{Stream, StreamExt};
@@ -32,8 +29,6 @@ pub mod error;
 /// Phoenixd
 #[derive(Clone)]
 pub struct Phoenixd {
-    mint_settings: MintMethodSettings,
-    melt_settings: MeltMethodSettings,
     phoenixd_api: PhoenixdApi,
     fee_reserve: FeeReserve,
     receiver: Arc<Mutex<Option<tokio::sync::mpsc::Receiver<WebhookResponse>>>>,
@@ -47,16 +42,12 @@ impl Phoenixd {
     pub fn new(
         api_password: String,
         api_url: String,
-        mint_settings: MintMethodSettings,
-        melt_settings: MeltMethodSettings,
         fee_reserve: FeeReserve,
         receiver: Arc<Mutex<Option<tokio::sync::mpsc::Receiver<WebhookResponse>>>>,
         webhook_url: String,
     ) -> Result<Self, Error> {
         let phoenixd = PhoenixdApi::new(&api_password, &api_url)?;
         Ok(Self {
-            mint_settings,
-            melt_settings,
             phoenixd_api: phoenixd,
             fee_reserve,
             receiver,
@@ -86,8 +77,6 @@ impl MintLightning for Phoenixd {
         Settings {
             mpp: false,
             unit: CurrencyUnit::Sat,
-            mint_settings: self.mint_settings.clone(),
-            melt_settings: self.melt_settings.clone(),
             invoice_description: true,
         }
     }

+ 1 - 12
crates/cdk-strike/src/lib.rs

@@ -14,10 +14,7 @@ use cdk::amount::Amount;
 use cdk::cdk_lightning::{
     self, CreateInvoiceResponse, MintLightning, PayInvoiceResponse, PaymentQuoteResponse, Settings,
 };
-use cdk::nuts::{
-    CurrencyUnit, MeltMethodSettings, MeltQuoteBolt11Request, MeltQuoteState, MintMethodSettings,
-    MintQuoteState,
-};
+use cdk::nuts::{CurrencyUnit, MeltQuoteBolt11Request, MeltQuoteState, MintQuoteState};
 use cdk::util::unix_time;
 use cdk::{mint, Bolt11Invoice};
 use error::Error;
@@ -37,8 +34,6 @@ pub mod error;
 #[derive(Clone)]
 pub struct Strike {
     strike_api: StrikeApi,
-    mint_settings: MintMethodSettings,
-    melt_settings: MeltMethodSettings,
     unit: CurrencyUnit,
     receiver: Arc<Mutex<Option<tokio::sync::mpsc::Receiver<String>>>>,
     webhook_url: String,
@@ -50,8 +45,6 @@ impl Strike {
     /// Create new [`Strike`] wallet
     pub async fn new(
         api_key: String,
-        mint_settings: MintMethodSettings,
-        melt_settings: MeltMethodSettings,
         unit: CurrencyUnit,
         receiver: Arc<Mutex<Option<tokio::sync::mpsc::Receiver<String>>>>,
         webhook_url: String,
@@ -59,8 +52,6 @@ impl Strike {
         let strike = StrikeApi::new(&api_key, None)?;
         Ok(Self {
             strike_api: strike,
-            mint_settings,
-            melt_settings,
             receiver,
             unit,
             webhook_url,
@@ -78,8 +69,6 @@ impl MintLightning for Strike {
         Settings {
             mpp: false,
             unit: self.unit.clone(),
-            mint_settings: self.mint_settings.clone(),
-            melt_settings: self.melt_settings.clone(),
             invoice_description: true,
         }
     }

+ 1 - 8
crates/cdk/src/cdk_lightning/mod.rs

@@ -8,10 +8,7 @@ use lightning_invoice::{Bolt11Invoice, ParseOrSemanticError};
 use serde::{Deserialize, Serialize};
 use thiserror::Error;
 
-use crate::nuts::{
-    CurrencyUnit, MeltMethodSettings, MeltQuoteBolt11Request, MeltQuoteState, MintMethodSettings,
-    MintQuoteState,
-};
+use crate::nuts::{CurrencyUnit, MeltQuoteBolt11Request, MeltQuoteState, MintQuoteState};
 use crate::{mint, Amount};
 
 /// CDK Lightning Error
@@ -148,10 +145,6 @@ pub struct PaymentQuoteResponse {
 pub struct Settings {
     /// MPP supported
     pub mpp: bool,
-    /// Min amount to mint
-    pub mint_settings: MintMethodSettings,
-    /// Max amount to mint
-    pub melt_settings: MeltMethodSettings,
     /// Base unit of backend
     pub unit: CurrencyUnit,
     /// Invoice Description supported