2021-02-16 16:37:44 +11:00
|
|
|
use crate::{bitcoin, monero};
|
|
|
|
use anyhow::{anyhow, Result};
|
2021-03-04 11:28:58 +11:00
|
|
|
use rust_decimal::prelude::ToPrimitive;
|
|
|
|
use rust_decimal::Decimal;
|
2021-02-19 10:25:59 +11:00
|
|
|
use std::fmt::{Debug, Display, Formatter};
|
2021-02-16 16:37:44 +11:00
|
|
|
|
|
|
|
/// Prices at which 1 XMR will be traded, in BTC (XMR/BTC pair)
|
|
|
|
/// The `ask` represents the minimum price in BTC for which we are willing to
|
|
|
|
/// sell 1 XMR.
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
|
|
pub struct Rate {
|
|
|
|
pub ask: bitcoin::Amount,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Rate {
|
|
|
|
pub const ZERO: Rate = Rate {
|
|
|
|
ask: bitcoin::Amount::ZERO,
|
|
|
|
};
|
|
|
|
|
|
|
|
// This function takes the quote amount as it is what Bob sends to Alice in the
|
|
|
|
// swap request
|
|
|
|
pub fn sell_quote(&self, quote: bitcoin::Amount) -> Result<monero::Amount> {
|
|
|
|
Self::quote(self.ask, quote)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn quote(rate: bitcoin::Amount, quote: bitcoin::Amount) -> Result<monero::Amount> {
|
|
|
|
// quote (btc) = rate * base (xmr)
|
|
|
|
// base = quote / rate
|
|
|
|
|
|
|
|
let quote_in_sats = quote.as_sat();
|
|
|
|
let quote_in_btc = Decimal::from(quote_in_sats)
|
|
|
|
.checked_div(Decimal::from(bitcoin::Amount::ONE_BTC.as_sat()))
|
|
|
|
.ok_or_else(|| anyhow!("division overflow"))?;
|
|
|
|
|
|
|
|
let rate_in_btc = Decimal::from(rate.as_sat())
|
|
|
|
.checked_div(Decimal::from(bitcoin::Amount::ONE_BTC.as_sat()))
|
|
|
|
.ok_or_else(|| anyhow!("division overflow"))?;
|
|
|
|
|
|
|
|
let base_in_xmr = quote_in_btc
|
|
|
|
.checked_div(rate_in_btc)
|
|
|
|
.ok_or_else(|| anyhow!("division overflow"))?;
|
|
|
|
let base_in_piconero = base_in_xmr * Decimal::from(monero::Amount::ONE_XMR.as_piconero());
|
|
|
|
|
|
|
|
let base_in_piconero = base_in_piconero
|
|
|
|
.to_u64()
|
|
|
|
.ok_or_else(|| anyhow!("decimal cannot be represented as u64"))?;
|
|
|
|
|
|
|
|
Ok(monero::Amount::from_piconero(base_in_piconero))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-19 10:25:59 +11:00
|
|
|
impl Display for Rate {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
|
|
write!(f, "{}", self.ask)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-16 16:37:44 +11:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn sell_quote() {
|
|
|
|
let rate = Rate {
|
|
|
|
ask: bitcoin::Amount::from_btc(0.002_500).unwrap(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let btc_amount = bitcoin::Amount::from_btc(2.5).unwrap();
|
|
|
|
|
|
|
|
let xmr_amount = rate.sell_quote(btc_amount).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(xmr_amount, monero::Amount::from_monero(1000.0).unwrap())
|
|
|
|
}
|
|
|
|
}
|