diff --git a/swap/src/monero.rs b/swap/src/monero.rs index 38b091b0..3e7f69ed 100644 --- a/swap/src/monero.rs +++ b/swap/src/monero.rs @@ -206,12 +206,18 @@ pub trait WatchForTransfer { } #[derive(Debug, Clone, Copy, thiserror::Error)] -#[error("transaction does not pay enough: expected {expected:?}, got {actual:?}")] +#[error("transaction does not pay enough: expected {expected}, got {actual}")] pub struct InsufficientFunds { pub expected: Amount, pub actual: Amount, } +#[derive(Debug, Clone, Copy, thiserror::Error)] +#[error("The balance is too low, current balance: {balance}")] +pub struct BalanceTooLow { + pub balance: Amount, +} + #[async_trait] pub trait CreateWalletForOutput { async fn create_and_load_wallet_for_output( diff --git a/swap/src/monero/wallet.rs b/swap/src/monero/wallet.rs index c8e3577d..a21ebe6b 100644 --- a/swap/src/monero/wallet.rs +++ b/swap/src/monero/wallet.rs @@ -68,6 +68,11 @@ impl Wallet { pub async fn refresh(&self) -> Result { self.inner.lock().await.refresh().await } + + pub fn static_tx_fee_estimate(&self) -> Amount { + // Median tx fees on Monero as found here: https://www.monero.how/monero-transaction-fees, 0.000_015 * 2 (to be on the safe side) + Amount::from_monero(0.000_03f64).expect("static fee to be convertible without problems") + } } #[async_trait] diff --git a/swap/src/protocol/alice/event_loop.rs b/swap/src/protocol/alice/event_loop.rs index 57c6f546..e061d6bf 100644 --- a/swap/src/protocol/alice/event_loop.rs +++ b/swap/src/protocol/alice/event_loop.rs @@ -3,7 +3,9 @@ use crate::{ bitcoin, database::Database, execution_params::ExecutionParams, - monero, network, + monero, + monero::BalanceTooLow, + network, network::{transport, TokioExecutor}, protocol::{ alice, @@ -14,7 +16,7 @@ use crate::{ }, seed::Seed, }; -use anyhow::{Context, Result}; +use anyhow::{anyhow, Context, Result}; use futures::future::RemoteHandle; use libp2p::{ core::Multiaddr, futures::FutureExt, request_response::ResponseChannel, PeerId, Swarm, @@ -164,7 +166,7 @@ where debug!("Connection Established with {}", alice); } OutEvent::QuoteRequest { msg, channel, bob_peer_id } => { - if let Err(error) = self.handle_quote_request(msg, channel, bob_peer_id).await { + if let Err(error) = self.handle_quote_request(msg, channel, bob_peer_id, self.monero_wallet.clone()).await { error!("Failed to handle quote request: {:#}", error); } } @@ -201,6 +203,7 @@ where quote_request: QuoteRequest, channel: ResponseChannel, bob_peer_id: PeerId, + monero_wallet: Arc, ) -> Result<()> { // 1. Check if acceptable request // 2. Send response @@ -212,6 +215,16 @@ where let btc_amount = quote_request.btc_amount; let xmr_amount = rate.sell_quote(btc_amount)?; + + let xmr_balance = monero_wallet.get_balance().await?; + let xmr_lock_fees = monero_wallet.static_tx_fee_estimate(); + + if xmr_balance < xmr_amount + xmr_lock_fees { + anyhow!(BalanceTooLow { + balance: xmr_balance + }); + } + let quote_response = QuoteResponse { xmr_amount }; self.swarm