From 93f1c19c4727cdd51703dbfbcefd05f6803da7e4 Mon Sep 17 00:00:00 2001 From: Nelson Date: Sun, 14 Jul 2024 21:07:40 -0500 Subject: [PATCH] Introduces a in-memory price cache --- Cargo.lock | 1 + Cargo.toml | 1 + src/app/order.rs | 11 +++++++---- src/bitcoin_price.rs | 38 ++++++++++++++++++++++++++++++++++++++ src/main.rs | 1 + src/scheduler.rs | 14 ++++++++++++++ src/util.rs | 6 ++++++ 7 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 src/bitcoin_price.rs diff --git a/Cargo.lock b/Cargo.lock index a33245c..0d0a148 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1845,6 +1845,7 @@ dependencies = [ "lnurl-rs", "mostro-core", "nostr-sdk", + "once_cell", "openssl", "reqwest", "serde", diff --git a/Cargo.toml b/Cargo.toml index e80abc0..90fae7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,3 +45,4 @@ config = "0.14.0" clap = { version = "4.5.3", features = ["derive"] } lnurl-rs = "0.5.0" openssl = { version = "0.10", features = ["vendored"] } +once_cell = "1.19.0" diff --git a/src/app/order.rs b/src/app/order.rs index 1354a3a..db98ab4 100644 --- a/src/app/order.rs +++ b/src/app/order.rs @@ -1,6 +1,6 @@ use crate::cli::settings::Settings; use crate::lightning::invoice::decode_invoice; -use crate::util::{get_market_quote, publish_order, send_cant_do_msg}; +use crate::util::{get_bitcoin_price, publish_order, send_cant_do_msg}; use anyhow::Result; use mostro_core::message::Message; @@ -49,10 +49,13 @@ pub async fn order_action( } } - for fiat_amount in amount_vec { + for (_, fiat_amount) in amount_vec.iter().enumerate() { let quote = match order.amount { - 0 => match get_market_quote(&fiat_amount, &order.fiat_code, 0).await { - Ok(amount) => amount, + 0 => match get_bitcoin_price(&order.fiat_code) { + Ok(price) => { + let quote = *fiat_amount as f64 / price; + (quote * 1E8) as i64 + }, Err(e) => { error!("{:?}", e.to_string()); return Ok(()); diff --git a/src/bitcoin_price.rs b/src/bitcoin_price.rs new file mode 100644 index 0000000..1d37ae5 --- /dev/null +++ b/src/bitcoin_price.rs @@ -0,0 +1,38 @@ +use std::collections::HashMap; +use std::sync::RwLock; +use serde::Deserialize; +use anyhow::Result; +use tracing::info; +use once_cell::sync::Lazy; + +const YADIO_API_URL: &str = "https://api.yadio.io/exrates/BTC"; + +#[derive(Debug, Deserialize)] +struct YadioResponse { + #[serde(rename = "BTC")] + btc: HashMap, +} + +static BITCOIN_PRICES: Lazy>> = Lazy::new(|| RwLock::new(HashMap::new())); + +pub struct BitcoinPriceManager; + +impl BitcoinPriceManager { + pub async fn update_prices() -> Result<()> { + let response = reqwest::get(YADIO_API_URL).await?; + let yadio_response: YadioResponse = response.json().await?; + info!( + "Bitcoin prices updated. Got BTC price in {} fiat currencies", + yadio_response.btc.keys().collect::>().len() + ); + + let mut prices_write = BITCOIN_PRICES.write().unwrap(); + *prices_write = yadio_response.btc; + Ok(()) + } + + pub fn get_price(currency: &str) -> Option { + let prices_read = BITCOIN_PRICES.read().unwrap(); + prices_read.get(currency).cloned() + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 3d25a98..b75c95f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ pub mod models; pub mod nip33; pub mod scheduler; pub mod util; +mod bitcoin_price; use crate::app::run; use crate::cli::settings::{init_global_settings, Settings}; diff --git a/src/scheduler.rs b/src/scheduler.rs index 9989220..4150878 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -4,6 +4,7 @@ use crate::db::*; use crate::lightning::LndConnector; use crate::util; use crate::NOSTR_CLIENT; +use crate::bitcoin_price::BitcoinPriceManager; use chrono::{TimeDelta, Utc}; use mostro_core::order::{Kind, Status}; @@ -25,6 +26,7 @@ pub async fn start_scheduler(rate_list: Arc>>) { job_retry_failed_payments().await; job_info_event_send().await; job_relay_list().await; + job_update_bitcoin_prices().await; info!("Scheduler Started"); } @@ -334,3 +336,15 @@ async fn job_expire_pending_older_orders() { } }); } + +async fn job_update_bitcoin_prices() { + tokio::spawn(async { + loop { + info!("Updating Bitcoin prices"); + if let Err(e) = BitcoinPriceManager::update_prices().await { + error!("Failed to update Bitcoin prices: {}", e); + } + tokio::time::sleep(tokio::time::Duration::from_secs(300)).await; + } + }); +} diff --git a/src/util.rs b/src/util.rs index c37b920..cbf2bea 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,4 +1,5 @@ use crate::app::rate_user::get_user_reputation; +use crate::bitcoin_price::BitcoinPriceManager; use crate::cli::settings::Settings; use crate::db; use crate::error::MostroError; @@ -55,6 +56,11 @@ pub async fn retries_yadio_request( Ok((Some(res), fiat_list_check)) } +pub fn get_bitcoin_price(fiat_code: &str) -> Result { + BitcoinPriceManager::get_price(fiat_code) + .ok_or_else(|| anyhow::anyhow!("Failed to get Bitcoin price")) +} + /// Request market quote from Yadio to have sats amount at actual market price pub async fn get_market_quote( fiat_amount: &i64,