Skip to content

Commit

Permalink
Merge pull request #35 from MutinyWallet/send-max-onchain
Browse files Browse the repository at this point in the history
Send max on-chain
  • Loading branch information
TonyGiorgio authored May 16, 2024
2 parents c0c360e + dbbca06 commit 92ecad6
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 14 deletions.
7 changes: 5 additions & 2 deletions src/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use tokio::sync::mpsc;
pub enum UICoreMsg {
SendLightning(Bolt11Invoice),
ReceiveLightning(Amount),
SendOnChain { address: Address, amount_sats: u64 },
SendOnChain {
address: Address,
amount_sats: Option<u64>,
},
ReceiveOnChain,
GetFederationInfo(InviteCode),
AddFederation(InviteCode),
Expand Down Expand Up @@ -71,7 +74,7 @@ impl UIHandle {
self.msg_send(UICoreMsg::SendLightning(invoice)).await;
}

pub async fn send_onchain(&self, address: Address, amount_sats: u64) {
pub async fn send_onchain(&self, address: Address, amount_sats: Option<u64>) {
self.msg_send(UICoreMsg::SendOnChain {
address,
amount_sats,
Expand Down
41 changes: 34 additions & 7 deletions src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,19 +191,46 @@ impl HarborCore {
Ok(invoice)
}

async fn send_onchain(&self, address: Address, sats: u64) -> anyhow::Result<()> {
/// Sends a given amount of sats to a given address, if the amount is None, send all funds
async fn send_onchain(&self, address: Address, sats: Option<u64>) -> anyhow::Result<()> {
// todo go through all clients and select the first one that has enough balance
let client = self.get_client().await.fedimint_client;
let onchain = client.get_first_module::<WalletClientModule>();

let amount = bitcoin::Amount::from_sat(sats);

// todo add manual fee selection
let fees = onchain.get_withdraw_fees(address.clone(), amount).await?;
let (fees, amount) = match sats {
Some(sats) => {
let amount = bitcoin::Amount::from_sat(sats);
let fees = onchain.get_withdraw_fees(address.clone(), amount).await?;
(fees, amount)
}
None => {
let balance = client.get_balance().await;

let op_id = onchain
.withdraw(address.clone(), bitcoin::Amount::from_sat(sats), fees, ())
.await?;
if balance.sats_round_down() == 0 {
return Err(anyhow!("No funds in wallet"));
}

// get fees for the entire balance
let fees = onchain
.get_withdraw_fees(
address.clone(),
bitcoin::Amount::from_sat(balance.sats_round_down()),
)
.await?;

let fees_paid = Amount::from_sats(fees.amount().to_sat());
let amount = balance.saturating_sub(fees_paid);

if amount.sats_round_down() < 546 {
return Err(anyhow!("Not enough funds to send"));
}

(fees, bitcoin::Amount::from_sat(amount.sats_round_down()))
}
};

let op_id = onchain.withdraw(address.clone(), amount, fees, ()).await?;

self.storage.create_onchain_payment(
op_id,
Expand Down
18 changes: 15 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ pub enum Message {
ReceiveStateReset,
SendDestInputChanged(String),
SendAmountInputChanged(String),
SetIsMax(bool),
SendStateReset,
PasswordInputChanged(String),
MintInviteCodeInputChanged(String),
Expand Down Expand Up @@ -117,6 +118,7 @@ pub struct HarborWallet {
send_success_msg: Option<SendSuccessMsg>,
send_dest_input_str: String,
send_amount_input_str: String,
is_max: bool,
password_input_str: String,
unlock_status: UnlockStatus,
unlock_failure_reason: Option<String>,
Expand Down Expand Up @@ -156,7 +158,7 @@ impl HarborWallet {
async fn async_send_onchain(
ui_handle: Option<Arc<bridge::UIHandle>>,
address: Address,
amount_sats: u64,
amount_sats: Option<u64>,
) {
println!("Got to async_send");
if let Some(ui_handle) = ui_handle {
Expand Down Expand Up @@ -236,6 +238,10 @@ impl HarborWallet {
self.send_amount_input_str = input;
Command::none()
}
Message::SetIsMax(is_max) => {
self.is_max = is_max;
Command::none()
}
Message::PasswordInputChanged(input) => {
self.password_input_str = input;
Command::none()
Expand All @@ -253,6 +259,7 @@ impl HarborWallet {
self.send_success_msg = None;
self.send_dest_input_str = String::new();
self.send_amount_input_str = String::new();
self.is_max = false;
self.send_status = SendStatus::Idle;
Command::none()
}
Expand Down Expand Up @@ -282,7 +289,12 @@ impl HarborWallet {
|_| Message::Noop,
)
} else if let Ok(address) = Address::from_str(&invoice_str) {
let amount = self.send_amount_input_str.parse::<u64>().unwrap(); // TODO: error handling
let amount = if self.is_max {
None
} else {
// TODO: error handling
Some(self.send_amount_input_str.parse::<u64>().unwrap())
};
Command::perform(
Self::async_send_onchain(self.ui_handle.clone(), address, amount),
|_| Message::Noop,
Expand Down Expand Up @@ -326,7 +338,7 @@ impl HarborWallet {
let address = Address::from_str(hardcoded_donation_address).unwrap();

Command::perform(
Self::async_send_onchain(self.ui_handle.clone(), address, amount),
Self::async_send_onchain(self.ui_handle.clone(), address, Some(amount)),
|_| Message::Noop,
)
}
Expand Down
7 changes: 5 additions & 2 deletions src/routes/send.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use iced::widget::{column, text};
use iced::widget::{column, text, Checkbox};
use iced::Color;
use iced::Element;

Expand All @@ -8,6 +8,7 @@ use crate::{HarborWallet, Message, SendStatus};
pub fn send(harbor: &HarborWallet) -> Element<Message> {
let header = h_header("Send", "Send to an on-chain address or lightning invoice.");

// todo disable amount input if max is selected
let amount_input = h_input(
"Amount",
"420",
Expand Down Expand Up @@ -48,6 +49,8 @@ pub fn send(harbor: &HarborWallet) -> Element<Message> {
.color(Color::from_rgb(0., 255., 0.))
});

let checkbox = Checkbox::new("Send Max", harbor.is_max).on_toggle(Message::SetIsMax);

let column = if let Some(failure_message) = failure_message {
let dangit_button =
h_button("Dangit", SvgIcon::Squirrel, false).on_press(Message::SendStateReset);
Expand All @@ -56,7 +59,7 @@ pub fn send(harbor: &HarborWallet) -> Element<Message> {
let nice_button = h_button("Nice", SvgIcon::Heart, false).on_press(Message::SendStateReset);
column![header, success_message, nice_button]
} else {
column![header, amount_input, dest_input, send_button]
column![header, amount_input, checkbox, dest_input, send_button]
};

column![
Expand Down

0 comments on commit 92ecad6

Please sign in to comment.