feat(wallet): wire fmcd as core app + dual-ecash receive
Fedimint never appeared in Wallet > Settings > Fedimint because the fmcd (fedimint-clientd) sidecar was never installed: ensure_default_ federation() needs the fmcd password to reach the daemon, found none, and silently no-oped, leaving the registry empty. - prod_orchestrator: add fedimint-clientd to the baseline auto-install set so it self-heals onto every node and auto-joins the default federation; generate the fmcd-password secret before secret_env resolves. - fedimint_client: ensure_fmcd_password (random hex, 0600) shared with the container's secret_env; from_node reads the same secret (legacy fmcd/password kept as fallback); reissue_into_any redeems received notes into the first joined federation that accepts them. - wallet.ecash-receive: dual-token — cashu* tokens redeem at the mint, anything else is reissued via fmcd; returns the kind + federation_id. - UI: receive box advertises "Cashu or Fedimint" and reports which kind. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
298595069d
commit
c4855526fe
@@ -1,7 +1,14 @@
|
||||
use super::RpcHandler;
|
||||
use crate::wallet::{ecash, profits};
|
||||
use crate::wallet::{ecash, fedimint_client, profits};
|
||||
use anyhow::Result;
|
||||
|
||||
/// A Cashu token (NUT-00 `cashuA`/`cashuB`, or our legacy `cashuSend_` form)
|
||||
/// always starts with `cashu`. Fedimint ecash notes never do, so a non-`cashu`
|
||||
/// string is routed to the Fedimint reissue path.
|
||||
fn is_cashu_token(token: &str) -> bool {
|
||||
token.trim_start().starts_with("cashu")
|
||||
}
|
||||
|
||||
impl RpcHandler {
|
||||
pub(super) async fn handle_wallet_ecash_balance(&self) -> Result<serde_json::Value> {
|
||||
let wallet = ecash::load_wallet(&self.config.data_dir).await?;
|
||||
@@ -129,11 +136,27 @@ impl RpcHandler {
|
||||
let token = params
|
||||
.get("token")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(str::trim)
|
||||
.filter(|s| !s.is_empty())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing token"))?;
|
||||
|
||||
let amount = ecash::receive_token(&self.config.data_dir, token).await?;
|
||||
// Dual-ecash: one "Receive ecash" box accepts either a Cashu token
|
||||
// (redeemed at the mint) or Fedimint notes (reissued via the fmcd
|
||||
// sidecar). Detect by prefix and route accordingly.
|
||||
if is_cashu_token(token) {
|
||||
let amount = ecash::receive_token(&self.config.data_dir, token).await?;
|
||||
return Ok(serde_json::json!({
|
||||
"received_sats": amount,
|
||||
"kind": "cashu",
|
||||
}));
|
||||
}
|
||||
|
||||
let (amount, federation_id) =
|
||||
fedimint_client::reissue_into_any(&self.config.data_dir, token).await?;
|
||||
Ok(serde_json::json!({
|
||||
"received_sats": amount,
|
||||
"kind": "fedimint",
|
||||
"federation_id": federation_id,
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user