mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-10-01 01:45:40 -04:00
fix(asb): use unlocked monero balance for quotes
This commit is contained in:
parent
c8f3fcebe7
commit
f224c49584
@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed ASB to quote on Monero unlocked balance instead of total balance
|
||||
|
||||
### Added
|
||||
|
||||
## [0.11.0] - 2022-08-11
|
||||
|
||||
### Changed
|
||||
@ -320,7 +326,7 @@ It is possible to migrate critical data from the old db to the sqlite but there
|
||||
- Fixed an issue where Alice would not verify if Bob's Bitcoin lock transaction is semantically correct, i.e. pays the agreed upon amount to an output owned by both of them.
|
||||
Fixing this required a **breaking change** on the network layer and hence old versions are not compatible with this version.
|
||||
|
||||
[Unreleased]: https://github.com/comit-network/xmr-btc-swap/compare/0.11.0...HEAD
|
||||
[unreleased]: https://github.com/comit-network/xmr-btc-swap/compare/0.11.0...HEAD
|
||||
[0.11.0]: https://github.com/comit-network/xmr-btc-swap/compare/0.10.2...0.11.0
|
||||
[0.10.2]: https://github.com/comit-network/xmr-btc-swap/compare/0.10.1...0.10.2
|
||||
[0.10.1]: https://github.com/comit-network/xmr-btc-swap/compare/0.10.0...0.10.1
|
||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2278,6 +2278,7 @@ dependencies = [
|
||||
"monero-epee-bin-serde",
|
||||
"rand 0.7.3",
|
||||
"reqwest",
|
||||
"rust_decimal",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
|
@ -138,15 +138,30 @@ impl<'c> Monero {
|
||||
let wallet = self.wallet(name)?;
|
||||
let address = wallet.address().await?.address;
|
||||
|
||||
let mut expected_total = 0;
|
||||
let mut expected_unlocked = 0;
|
||||
let mut unlocked = 0;
|
||||
for amount in amount_in_outputs {
|
||||
if amount > 0 {
|
||||
miner_wallet.transfer(&address, amount).await?;
|
||||
expected_total += amount;
|
||||
tracing::info!("Funded {} wallet with {}", wallet.name, amount);
|
||||
|
||||
// sanity checks for total/unlocked balance
|
||||
let total = wallet.balance().await?;
|
||||
assert_eq!(total, expected_total);
|
||||
assert_eq!(unlocked, expected_unlocked);
|
||||
|
||||
monerod
|
||||
.client()
|
||||
.generateblocks(10, miner_address.clone())
|
||||
.await?;
|
||||
wallet.refresh().await?;
|
||||
expected_unlocked += amount;
|
||||
|
||||
unlocked = wallet.unlocked_balance().await?;
|
||||
assert_eq!(unlocked, expected_unlocked);
|
||||
assert_eq!(total, expected_total);
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,10 +325,18 @@ impl<'c> MoneroWalletRpc {
|
||||
Ok(balance)
|
||||
}
|
||||
|
||||
pub async fn unlocked_balance(&self) -> Result<u64> {
|
||||
self.client().refresh().await?;
|
||||
let balance = self.client().get_balance(0).await?.unlocked_balance;
|
||||
|
||||
Ok(balance)
|
||||
}
|
||||
|
||||
pub async fn refresh(&self) -> Result<Refreshed> {
|
||||
Ok(self.client().refresh().await?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Mine a block ever BLOCK_TIME_SECS seconds.
|
||||
async fn mine(monerod: monerod::Client, reward_address: String) -> Result<()> {
|
||||
loop {
|
||||
|
@ -13,6 +13,7 @@ monero = "0.12"
|
||||
monero-epee-bin-serde = "1"
|
||||
rand = "0.7"
|
||||
reqwest = { version = "0.11", default-features = false, features = [ "json" ] }
|
||||
rust_decimal = { version = "1", features = [ "serde-float" ] }
|
||||
serde = { version = "1.0", features = [ "derive" ] }
|
||||
serde_json = "1.0"
|
||||
tracing = "0.1"
|
||||
|
@ -1,4 +1,7 @@
|
||||
use std::fmt;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use rust_decimal::Decimal;
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
|
||||
@ -86,10 +89,30 @@ pub struct GetAddress {
|
||||
#[derive(Deserialize, Debug, Clone, Copy)]
|
||||
pub struct GetBalance {
|
||||
pub balance: u64,
|
||||
pub blocks_to_unlock: u32,
|
||||
pub multisig_import_needed: bool,
|
||||
pub time_to_unlock: u32,
|
||||
pub unlocked_balance: u64,
|
||||
pub multisig_import_needed: bool,
|
||||
pub blocks_to_unlock: u32,
|
||||
pub time_to_unlock: u32,
|
||||
}
|
||||
|
||||
impl fmt::Display for GetBalance {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut total = Decimal::from(self.balance);
|
||||
total
|
||||
.set_scale(12)
|
||||
.expect("12 is smaller than max precision of 28");
|
||||
|
||||
let mut unlocked = Decimal::from(self.unlocked_balance);
|
||||
unlocked
|
||||
.set_scale(12)
|
||||
.expect("12 is smaller than max precision of 28");
|
||||
|
||||
write!(
|
||||
f,
|
||||
"total balance: {}, unlocked balance: {}",
|
||||
total, unlocked
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::asb::{Behaviour, OutEvent, Rate};
|
||||
use crate::monero::Amount;
|
||||
use crate::network::quote::BidQuote;
|
||||
use crate::network::swap_setup::alice::WalletSnapshot;
|
||||
use crate::network::transfer_proof;
|
||||
@ -326,7 +327,10 @@ where
|
||||
.ask()
|
||||
.context("Failed to compute asking price")?;
|
||||
|
||||
let xmr = self.monero_wallet.get_balance().await?;
|
||||
let balance = self.monero_wallet.get_balance().await?;
|
||||
|
||||
// use unlocked monero balance for quote
|
||||
let xmr = Amount::from_piconero(balance.unlocked_balance);
|
||||
|
||||
let max_bitcoin_for_monero = xmr.max_bitcoin_for_price(ask_price).ok_or_else(|| {
|
||||
anyhow::anyhow!("Bitcoin price ({}) x Monero ({}) overflow", ask_price, xmr)
|
||||
|
@ -31,7 +31,6 @@ use swap::asb::config::{
|
||||
use swap::asb::{cancel, punish, redeem, refund, safely_abort, EventLoop, Finality, KrakenRate};
|
||||
use swap::common::check_latest_version;
|
||||
use swap::database::open_db;
|
||||
use swap::monero::Amount;
|
||||
use swap::network::rendezvous::XmrBtcNamespace;
|
||||
use swap::network::swarm;
|
||||
use swap::protocol::alice::{run, AliceState};
|
||||
@ -103,24 +102,35 @@ async fn main() -> Result<()> {
|
||||
|
||||
match cmd {
|
||||
Command::Start { resume_only } => {
|
||||
let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?;
|
||||
|
||||
let monero_wallet = init_monero_wallet(&config, env_config).await?;
|
||||
|
||||
let bitcoin_balance = bitcoin_wallet.balance().await?;
|
||||
tracing::info!(%bitcoin_balance, "Initialized Bitcoin wallet");
|
||||
|
||||
let monero_balance = monero_wallet.get_balance().await?;
|
||||
if monero_balance == Amount::ZERO {
|
||||
let monero_address = monero_wallet.get_main_address();
|
||||
tracing::warn!(
|
||||
%monero_address,
|
||||
"The Monero balance is 0, make sure to deposit funds at",
|
||||
)
|
||||
} else {
|
||||
tracing::info!(%monero_balance, "Initialized Monero wallet");
|
||||
let monero_address = monero_wallet.get_main_address();
|
||||
tracing::info!(%monero_address, "Monero wallet address");
|
||||
let monero = monero_wallet.get_balance().await?;
|
||||
match (monero.balance, monero.unlocked_balance) {
|
||||
(0, _) => {
|
||||
tracing::warn!(
|
||||
%monero_address,
|
||||
"The Monero balance is 0, make sure to deposit funds at",
|
||||
)
|
||||
}
|
||||
(total, 0) => {
|
||||
let total = monero::Amount::from_piconero(total);
|
||||
tracing::warn!(
|
||||
%total,
|
||||
"Unlocked Monero balance is 0, total balance is",
|
||||
)
|
||||
}
|
||||
(total, unlocked) => {
|
||||
let total = monero::Amount::from_piconero(total);
|
||||
let unlocked = monero::Amount::from_piconero(unlocked);
|
||||
tracing::info!(%total, %unlocked, "Monero wallet balance");
|
||||
}
|
||||
}
|
||||
|
||||
let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?;
|
||||
let bitcoin_balance = bitcoin_wallet.balance().await?;
|
||||
tracing::info!(%bitcoin_balance, "Bitcoin wallet balance");
|
||||
|
||||
let kraken_price_updates = kraken::connect(config.maker.price_ticker_ws_url.clone())?;
|
||||
|
||||
// setup Tor hidden services
|
||||
@ -236,16 +246,13 @@ async fn main() -> Result<()> {
|
||||
bitcoin_wallet.broadcast(signed_tx, "withdraw").await?;
|
||||
}
|
||||
Command::Balance => {
|
||||
let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?;
|
||||
let monero_wallet = init_monero_wallet(&config, env_config).await?;
|
||||
|
||||
let bitcoin_balance = bitcoin_wallet.balance().await?;
|
||||
let monero_balance = monero_wallet.get_balance().await?;
|
||||
tracing::info!(%monero_balance);
|
||||
|
||||
tracing::info!(
|
||||
%bitcoin_balance,
|
||||
%monero_balance,
|
||||
"Current balance");
|
||||
let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?;
|
||||
let bitcoin_balance = bitcoin_wallet.balance().await?;
|
||||
tracing::info!(%bitcoin_balance);
|
||||
}
|
||||
Command::Cancel { swap_id } => {
|
||||
let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?;
|
||||
|
@ -48,10 +48,10 @@ mod test {
|
||||
#[tokio::test]
|
||||
#[ignore = "For local testing, makes http requests to github."]
|
||||
async fn it_compares_with_github() {
|
||||
let result = check_latest_version("0.10.1").await.unwrap();
|
||||
let result = check_latest_version("0.11.0").await.unwrap();
|
||||
assert_eq!(result, Version::Available);
|
||||
|
||||
let result = check_latest_version("0.10.2").await.unwrap();
|
||||
let result = check_latest_version("0.11.1").await.unwrap();
|
||||
assert_eq!(result, Version::Current);
|
||||
}
|
||||
}
|
||||
|
@ -253,10 +253,8 @@ impl Wallet {
|
||||
}
|
||||
|
||||
/// Get the balance of the primary account.
|
||||
pub async fn get_balance(&self) -> Result<Amount> {
|
||||
let amount = self.inner.lock().await.get_balance(0).await?.balance;
|
||||
|
||||
Ok(Amount::from_piconero(amount))
|
||||
pub async fn get_balance(&self) -> Result<wallet::GetBalance> {
|
||||
Ok(self.inner.lock().await.get_balance(0).await?)
|
||||
}
|
||||
|
||||
pub async fn block_height(&self) -> Result<BlockHeight> {
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::asb::LatestRate;
|
||||
use crate::monero::Amount;
|
||||
use crate::network::swap_setup;
|
||||
use crate::network::swap_setup::{
|
||||
protocol, BlockchainNetwork, SpotPriceError, SpotPriceRequest, SpotPriceResponse,
|
||||
@ -42,7 +43,7 @@ pub enum OutEvent {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WalletSnapshot {
|
||||
balance: monero::Amount,
|
||||
balance: monero_rpc::wallet::GetBalance,
|
||||
lock_fee: monero::Amount,
|
||||
|
||||
// TODO: Consider using the same address for punish and redeem (they are mutually exclusive, so
|
||||
@ -323,7 +324,8 @@ where
|
||||
.sell_quote(btc)
|
||||
.map_err(Error::SellQuoteCalculationFailed)?;
|
||||
|
||||
if wallet_snapshot.balance < xmr + wallet_snapshot.lock_fee {
|
||||
let unlocked = Amount::from_piconero(wallet_snapshot.balance.unlocked_balance);
|
||||
if unlocked < xmr + wallet_snapshot.lock_fee {
|
||||
return Err(Error::BalanceTooLow {
|
||||
balance: wallet_snapshot.balance,
|
||||
buy: btc,
|
||||
@ -479,9 +481,9 @@ pub enum Error {
|
||||
max: bitcoin::Amount,
|
||||
buy: bitcoin::Amount,
|
||||
},
|
||||
#[error("Balance {balance} too low to fulfill swapping {buy}")]
|
||||
#[error("Unlocked balance ({balance}) too low to fulfill swapping {buy}")]
|
||||
BalanceTooLow {
|
||||
balance: monero::Amount,
|
||||
balance: monero_rpc::wallet::GetBalance,
|
||||
buy: bitcoin::Amount,
|
||||
},
|
||||
#[error("Failed to fetch latest rate")]
|
||||
|
@ -867,7 +867,9 @@ impl Wallet for monero::Wallet {
|
||||
}
|
||||
|
||||
async fn get_balance(&self) -> Result<Self::Amount> {
|
||||
self.get_balance().await
|
||||
let total = self.get_balance().await?;
|
||||
let balance = Self::Amount::from_piconero(total.balance);
|
||||
Ok(balance)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user