Skip to content

Commit

Permalink
chore: users can leave federations with zero balance
Browse files Browse the repository at this point in the history
  • Loading branch information
tvolk131 committed Oct 3, 2024
1 parent f5b0860 commit 2d83f73
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 3 deletions.
40 changes: 38 additions & 2 deletions src/fedimint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ impl Wallet {

pub async fn connect_to_joined_federations(&self) -> anyhow::Result<()> {
// Note: We're intentionally locking the clients mutex earlier than
// necessary so that the lock is held while we're reading the data directory.
// necessary so that the lock is held while we're accessing the data directory.
let mut clients = self.clients.lock().await;

// List all files in the data directory.
Expand Down Expand Up @@ -217,7 +217,7 @@ impl Wallet {

pub async fn join_federation(&self, invite_code: InviteCode) -> anyhow::Result<()> {
// Note: We're intentionally locking the clients mutex earlier than
// necessary so that the lock is held while we're reading the data directory.
// necessary so that the lock is held while we're accessing the data directory.
let mut clients = self.clients.lock().await;

let federation_id = invite_code.federation_id();
Expand All @@ -242,6 +242,42 @@ impl Wallet {
Ok(())
}

// TODO: Call `ClientModule::leave()` for every module.
// https://docs.rs/fedimint-client/0.4.2/fedimint_client/module/trait.ClientModule.html#method.leave
// Currently it isn't implemented for the `LightningClientModule`, so for now we're just checking
// that the client has a zero balance.
pub async fn leave_federation(&self, federation_id: FederationId) -> anyhow::Result<()> {
// Note: We're intentionally locking the clients mutex earlier than
// necessary so that the lock is held while we're accessing the data directory.
let mut clients = self.clients.lock().await;

if let Some(client) = clients.remove(&federation_id) {
if client.get_balance().await.msats != 0 {
// Re-insert the client back into the clients map.
clients.insert(federation_id, client);

return Err(anyhow::anyhow!(
"Cannot leave federation with non-zero balance: {}",
federation_id
));
}

client.shutdown().await;

let federation_data_dir = self
.fedimint_clients_data_dir
.join(federation_id.to_string());

if federation_data_dir.is_dir() {
std::fs::remove_dir_all(federation_data_dir)?;
}
}

self.force_update_view(clients).await;

Ok(())
}

/// Constructs the current view of the wallet.
/// SHOULD ONLY BE CALLED FROM THE `view_update_task`.
/// This way, `view_update_task` can only yield values
Expand Down
64 changes: 63 additions & 1 deletion src/routes/bitcoin_wallet.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::str::FromStr;

use fedimint_core::{
config::{ClientConfig, META_FEDERATION_NAME_KEY},
config::{ClientConfig, FederationId, META_FEDERATION_NAME_KEY},
invite_code::InviteCode,
Amount,
};
Expand Down Expand Up @@ -42,6 +42,9 @@ pub enum Message {
JoinFederation(InviteCode),
JoinedFederation(InviteCode),

LeaveFederation(FederationId),
LeftFederation(FederationId),

Send(send::Message),
Receive(receive::Message),

Expand Down Expand Up @@ -196,6 +199,44 @@ impl Page {

Task::none()
}
Message::LeaveFederation(federation_id) => {
let wallet = self.connected_state.wallet.clone();

Task::stream(async_stream::stream! {
match wallet.leave_federation(federation_id).await {
Ok(()) => {
yield app::Message::AddToast(Toast {
title: "Left federation".to_string(),
body: "You have successfully left the federation.".to_string(),
status: ToastStatus::Good,
});

yield app::Message::Routes(super::Message::BitcoinWalletPage(
Message::LeftFederation(federation_id)
));
}
Err(err) => {
yield app::Message::AddToast(Toast {
title: "Failed to leave federation".to_string(),
body: format!("Failed to leave the federation: {err}"),
status: ToastStatus::Bad,
});
}
}
})
}
Message::LeftFederation(federation_id) => {
// A verbose way of saying "if the user is currently on the FederationDetails page and the federation ID matches the one that was just left, navigate back to the List page".
if let Subroute::FederationDetails(federation_details) = &self.subroute {
if federation_details.view.federation_id == federation_id {
return Task::done(app::Message::Routes(super::Message::Navigate(
RouteName::BitcoinWallet(SubrouteName::List),
)));
}
}

Task::none()
}
Message::Send(send_message) => {
if let Subroute::Send(send_page) = &mut self.subroute {
send_page.update(send_message)
Expand Down Expand Up @@ -444,6 +485,27 @@ impl FederationDetails {
);
}

// TODO: Add a function to `Wallet` to check whether we can safely leave a federation.
// Call it here rather and get rid of `has_zero_balance`.
let has_zero_balance = self.view.balance.msats == 0;

if !has_zero_balance {
container = container.push(
Text::new("Must have a zero balance in this federation in order to leave.")
.size(20),
);
}

container = container.push(
icon_button("Leave Federation", SvgIcon::Delete, PaletteColor::Danger).on_press_maybe(
has_zero_balance.then(|| {
app::Message::Routes(super::Message::BitcoinWalletPage(
Message::LeaveFederation(self.view.federation_id),
))
}),
),
);

container = container.push(
icon_button("Back", SvgIcon::ArrowBack, PaletteColor::Background).on_press(
app::Message::Routes(super::Message::Navigate(RouteName::BitcoinWallet(
Expand Down

0 comments on commit 2d83f73

Please sign in to comment.