mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-12-15 08:38:58 -05:00
Merge #953
953: Adjust quote based on Bitcoin balance r=lescuer97 a=lescuer97 Fixes #939 #963 Please comment in the new method for getting a Monero value, I had to allow clippy::cast_precision_loss to convert to a f64, for our use case I don't really thing we will lose much precision. Please comment on the implementation of the check. Co-authored-by: leonardo <leoescuer@protonmail.com>
This commit is contained in:
commit
c76abd48c7
5 changed files with 127 additions and 7 deletions
|
|
@ -319,13 +319,46 @@ where
|
|||
min_buy: bitcoin::Amount,
|
||||
max_buy: bitcoin::Amount,
|
||||
) -> Result<BidQuote> {
|
||||
let rate = self
|
||||
let ask_price = self
|
||||
.latest_rate
|
||||
.latest_rate()
|
||||
.context("Failed to get latest rate")?;
|
||||
.context("Failed to get latest rate")?
|
||||
.ask()
|
||||
.context("Failed to compute asking price")?;
|
||||
|
||||
let max_bitcoin_for_monero = self
|
||||
.monero_wallet
|
||||
.get_balance()
|
||||
.await?
|
||||
.max_bitcoin_for_price(ask_price);
|
||||
|
||||
if min_buy > max_bitcoin_for_monero {
|
||||
tracing::warn!(
|
||||
"Your Monero balance is too low to initiate a swap, as your minimum swap amount is {}. You could at most swap {}",
|
||||
min_buy, max_bitcoin_for_monero
|
||||
);
|
||||
|
||||
return Ok(BidQuote {
|
||||
price: ask_price,
|
||||
min_quantity: bitcoin::Amount::ZERO,
|
||||
max_quantity: bitcoin::Amount::ZERO,
|
||||
});
|
||||
}
|
||||
|
||||
if max_buy > max_bitcoin_for_monero {
|
||||
tracing::warn!(
|
||||
"Your Monero balance is too low to initiate a swap with the maximum swap amount {} that you have specified in your config. You can at most swap {}",
|
||||
max_buy, max_bitcoin_for_monero
|
||||
);
|
||||
return Ok(BidQuote {
|
||||
price: ask_price,
|
||||
min_quantity: min_buy,
|
||||
max_quantity: max_bitcoin_for_monero,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(BidQuote {
|
||||
price: rate.ask().context("Failed to compute asking price")?,
|
||||
price: ask_price,
|
||||
min_quantity: min_buy,
|
||||
max_quantity: max_buy,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ use swap::cli::{list_sellers, EventLoop, SellerStatus};
|
|||
use swap::database::open_db;
|
||||
use swap::env::Config;
|
||||
use swap::libp2p_ext::MultiAddrExt;
|
||||
use swap::network::quote::BidQuote;
|
||||
use swap::network::quote::{BidQuote, ZeroQuoteReceived};
|
||||
use swap::network::swarm;
|
||||
use swap::protocol::bob;
|
||||
use swap::protocol::bob::{BobState, Swap};
|
||||
|
|
@ -99,7 +99,7 @@ async fn main() -> Result<()> {
|
|||
let event_loop = tokio::spawn(event_loop.run());
|
||||
|
||||
let max_givable = || bitcoin_wallet.max_giveable(TxLock::script_size());
|
||||
let (amount, fees) = determine_btc_to_swap(
|
||||
let (amount, fees) = match determine_btc_to_swap(
|
||||
json,
|
||||
event_loop_handle.request_quote(),
|
||||
bitcoin_wallet.new_address(),
|
||||
|
|
@ -107,7 +107,16 @@ async fn main() -> Result<()> {
|
|||
max_givable,
|
||||
|| bitcoin_wallet.sync(),
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
{
|
||||
Ok(val) => val,
|
||||
Err(error) => match error.downcast::<ZeroQuoteReceived>() {
|
||||
Ok(_) => {
|
||||
bail!("Seller's XMR balance is currently too low to initiate a swap, please try again later")
|
||||
}
|
||||
Err(other) => bail!(other),
|
||||
},
|
||||
};
|
||||
|
||||
tracing::info!(%amount, %fees, "Determined swap amount");
|
||||
|
||||
|
|
@ -556,6 +565,11 @@ where
|
|||
{
|
||||
tracing::debug!("Requesting quote");
|
||||
let bid_quote = bid_quote.await?;
|
||||
|
||||
if bid_quote.max_quantity == bitcoin::Amount::ZERO {
|
||||
bail!(ZeroQuoteReceived)
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
price = %bid_quote.price,
|
||||
minimum_amount = %bid_quote.min_quantity,
|
||||
|
|
@ -915,6 +929,32 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn given_bid_quote_max_amount_0_return_errorq() {
|
||||
let givable = Arc::new(Mutex::new(MaxGiveable::new(vec![
|
||||
Amount::from_btc(0.0001).unwrap(),
|
||||
Amount::from_btc(0.01).unwrap(),
|
||||
])));
|
||||
|
||||
let determination_error = determine_btc_to_swap(
|
||||
true,
|
||||
async { Ok(quote_with_max(0.00)) },
|
||||
get_dummy_address(),
|
||||
|| async { Ok(Amount::from_btc(0.0101)?) },
|
||||
|| async {
|
||||
let mut result = givable.lock().unwrap();
|
||||
result.give()
|
||||
},
|
||||
|| async { Ok(()) },
|
||||
)
|
||||
.await
|
||||
.err()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
assert_eq!("Received quote of 0", determination_error);
|
||||
}
|
||||
|
||||
struct MaxGiveable {
|
||||
amounts: Vec<Amount>,
|
||||
call_counter: usize,
|
||||
|
|
|
|||
|
|
@ -99,6 +99,20 @@ impl Amount {
|
|||
self.0
|
||||
}
|
||||
|
||||
pub fn max_bitcoin_for_price(&self, ask_price: bitcoin::Amount) -> bitcoin::Amount {
|
||||
let piconero_minus_fee = self.as_piconero().saturating_sub(MONERO_FEE.as_piconero());
|
||||
|
||||
if piconero_minus_fee == 0 {
|
||||
return bitcoin::Amount::ZERO;
|
||||
}
|
||||
|
||||
// There needs to be an offset for difference in zeroes beetween Piconeros and
|
||||
// Satoshis
|
||||
let piconero_calc = (piconero_minus_fee * ask_price.as_sat()) / PICONERO_OFFSET;
|
||||
|
||||
bitcoin::Amount::from_sat(piconero_calc)
|
||||
}
|
||||
|
||||
pub fn from_monero(amount: f64) -> Result<Self> {
|
||||
let decimal = Decimal::try_from(amount)?;
|
||||
Self::from_decimal(decimal)
|
||||
|
|
@ -360,6 +374,30 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn geting_max_bitcoin_to_trade() {
|
||||
let amount = Amount::parse_monero("10").unwrap();
|
||||
let bitcoin_price_sats = bitcoin::Amount::from_sat(382_900);
|
||||
|
||||
let monero_max_from_bitcoin = amount.max_bitcoin_for_price(bitcoin_price_sats);
|
||||
|
||||
assert_eq!(
|
||||
bitcoin::Amount::from_sat(3_828_988),
|
||||
monero_max_from_bitcoin
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn geting_max_bitcoin_to_trade_with_balance_smaller_than_locking_fee() {
|
||||
let monero = "0.00001";
|
||||
let amount = Amount::parse_monero(monero).unwrap();
|
||||
let bitcoin_price_sats = bitcoin::Amount::from_sat(382_900);
|
||||
|
||||
let monero_max_from_bitcoin = amount.max_bitcoin_for_price(bitcoin_price_sats);
|
||||
|
||||
assert_eq!(bitcoin::Amount::ZERO, monero_max_from_bitcoin);
|
||||
}
|
||||
|
||||
use rand::rngs::OsRng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ pub struct BidQuote {
|
|||
pub max_quantity: bitcoin::Amount,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, thiserror::Error)]
|
||||
#[error("Received quote of 0")]
|
||||
pub struct ZeroQuoteReceived;
|
||||
|
||||
/// Constructs a new instance of the `quote` behaviour to be used by the ASB.
|
||||
///
|
||||
/// The ASB is always listening and only supports inbound connections, i.e.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue