From 4934f06ce2d4affb143a0b31b65da01f31d19297 Mon Sep 17 00:00:00 2001 From: Arkanoider Date: Sat, 6 Jan 2024 13:14:03 +0100 Subject: [PATCH 1/5] simple idea to have error message from mostro when currency is not on Yadio --- src/error.rs | 2 ++ src/util.rs | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/error.rs b/src/error.rs index 6dc0b477..755eb425 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,6 +9,7 @@ pub enum MostroError { MinAmountError, WrongAmountError, NoAPIResponse, + NoCurrency, } impl std::error::Error for MostroError {} @@ -23,6 +24,7 @@ impl fmt::Display for MostroError { MostroError::MinAmountError => write!(f, "Minimal payment amount"), MostroError::WrongAmountError => write!(f, "The amount on this invoice is wrong"), MostroError::NoAPIResponse => write!(f, "Price API not answered - retry"), + MostroError::NoCurrency => write!(f, "Currency requested is not present in the exchange list, please specify a fixed rate"), } } } diff --git a/src/util.rs b/src/util.rs index ce64f44a..3af634b2 100644 --- a/src/util.rs +++ b/src/util.rs @@ -25,6 +25,20 @@ use tracing::error; use tracing::info; use uuid::Uuid; +pub type FiatNames = std::collections::HashMap; + +pub async fn check_fiat_currency(fiat_code: &str) -> Result { + // Get Fiat list + let api_req_string = "https://api.yadio.io/currencies".to_string(); + let fiat_list_check = reqwest::get(api_req_string) + .await? + .json::() + .await? + .contains_key(fiat_code); + + Ok(fiat_list_check) +} + pub async fn retries_yadio_request(req_string: &str) -> Result { let res = reqwest::get(req_string) .await @@ -39,6 +53,25 @@ pub async fn get_market_quote( fiat_code: &str, premium: &i64, ) -> Result { + // Check if requested currency is supported from Yadio + // Retry for 4 times + for retries_num in 1..=4 { + match check_fiat_currency(fiat_code).await { + Ok(fiat_is_present) => { + if !fiat_is_present { + return Err(MostroError::NoCurrency); + } + } + Err(_e) => { + println!( + "API price request failed retrying - {} tentatives left.", + (4 - retries_num) + ); + thread::sleep(std::time::Duration::from_secs(2)); + } + }; + } + // Add here check for market price let req_string = format!( "https://api.yadio.io/convert/{}/{}/BTC", From e8646b83af59738ee62e7fe7fb89593ee30b0537 Mon Sep 17 00:00:00 2001 From: Arkanoider Date: Sat, 6 Jan 2024 15:21:40 +0100 Subject: [PATCH 2/5] some refactoring --- src/error.rs | 2 ++ src/util.rs | 57 ++++++++++++++++++++++++---------------------------- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/src/error.rs b/src/error.rs index 755eb425..ab3da7c0 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,6 +10,7 @@ pub enum MostroError { WrongAmountError, NoAPIResponse, NoCurrency, + MalformedAPIRes, } impl std::error::Error for MostroError {} @@ -25,6 +26,7 @@ impl fmt::Display for MostroError { MostroError::WrongAmountError => write!(f, "The amount on this invoice is wrong"), MostroError::NoAPIResponse => write!(f, "Price API not answered - retry"), MostroError::NoCurrency => write!(f, "Currency requested is not present in the exchange list, please specify a fixed rate"), + MostroError::MalformedAPIRes => write!(f, "Malformed answer from exchange quoting request"), } } } diff --git a/src/util.rs b/src/util.rs index 3af634b2..2fc2e68f 100644 --- a/src/util.rs +++ b/src/util.rs @@ -27,8 +27,11 @@ use uuid::Uuid; pub type FiatNames = std::collections::HashMap; -pub async fn check_fiat_currency(fiat_code: &str) -> Result { - // Get Fiat list +pub async fn retries_yadio_request( + req_string: &str, + fiat_code: &str, +) -> Result<(Option, bool)> { + // Get Fiat list and check if currency exchange is available let api_req_string = "https://api.yadio.io/currencies".to_string(); let fiat_list_check = reqwest::get(api_req_string) .await? @@ -36,15 +39,16 @@ pub async fn check_fiat_currency(fiat_code: &str) -> Result { .await? .contains_key(fiat_code); - Ok(fiat_list_check) -} + // Exit with error - no currency + if !fiat_list_check { + return Ok((None, fiat_list_check)); + } -pub async fn retries_yadio_request(req_string: &str) -> Result { let res = reqwest::get(req_string) .await .context("Something went wrong with API request, try again!")?; - Ok(res) + Ok((Some(res), fiat_list_check)) } /// Request market quote from Yadio to have sats amount at actual market price @@ -53,25 +57,6 @@ pub async fn get_market_quote( fiat_code: &str, premium: &i64, ) -> Result { - // Check if requested currency is supported from Yadio - // Retry for 4 times - for retries_num in 1..=4 { - match check_fiat_currency(fiat_code).await { - Ok(fiat_is_present) => { - if !fiat_is_present { - return Err(MostroError::NoCurrency); - } - } - Err(_e) => { - println!( - "API price request failed retrying - {} tentatives left.", - (4 - retries_num) - ); - thread::sleep(std::time::Duration::from_secs(2)); - } - }; - } - // Add here check for market price let req_string = format!( "https://api.yadio.io/convert/{}/{}/BTC", @@ -79,15 +64,20 @@ pub async fn get_market_quote( ); info!("Requesting API price: {}", req_string); - let mut req = None; + let mut req = (None, false); + let mut no_answer_api = false; + // Retry for 4 times for retries_num in 1..=4 { - match retries_yadio_request(&req_string).await { + match retries_yadio_request(&req_string, fiat_code).await { Ok(response) => { - req = Some(response); + req = response; break; } Err(_e) => { + if retries_num == 4 { + no_answer_api = true; + } println!( "API price request failed retrying - {} tentatives left.", (4 - retries_num) @@ -98,13 +88,18 @@ pub async fn get_market_quote( } // Case no answers from Yadio - if req.is_none() { + if no_answer_api { return Err(MostroError::NoAPIResponse); } - let quote = req.unwrap().json::().await; + // No currency present + if !req.1 { + return Err(MostroError::NoCurrency); + } + + let quote = req.0.unwrap().json::().await; if quote.is_err() { - return Err(MostroError::NoAPIResponse); + return Err(MostroError::MalformedAPIRes); } let quote = quote.unwrap(); From bba6565720bf3dc6a8ce4bd81f6f050da45b2bc6 Mon Sep 17 00:00:00 2001 From: Arkanoider Date: Sat, 6 Jan 2024 16:27:31 +0100 Subject: [PATCH 3/5] Added check for fixed amount rate on order creation, now we manage it --- src/app/order.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/app/order.rs b/src/app/order.rs index 7f4d8932..cb4cd5c9 100644 --- a/src/app/order.rs +++ b/src/app/order.rs @@ -16,13 +16,18 @@ pub async fn order_action( ) -> Result<()> { if let Some(order) = msg.get_inner_message_kind().get_order() { let mostro_settings = Settings::get_mostro(); - let quote = match get_market_quote(&order.fiat_amount, &order.fiat_code, &0).await { - Ok(amount) => amount, - Err(e) => { - error!("{:?}", e.to_string()); - return Ok(()); - } + + let quote = match order.amount { + 0 => match get_market_quote(&order.fiat_amount, &order.fiat_code, &0).await { + Ok(amount) => amount, + Err(e) => { + error!("{:?}", e.to_string()); + return Ok(()); + } + }, + _ => order.amount, }; + if quote > mostro_settings.max_order_amount as i64 { let message = Message::cant_do( order.id, From 4f13d673983209633ae8c51e9cc4f686271ccdcf Mon Sep 17 00:00:00 2001 From: Arkanoider Date: Sat, 6 Jan 2024 21:53:34 +0100 Subject: [PATCH 4/5] Added a const for max retries --- src/util.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/util.rs b/src/util.rs index 2fc2e68f..ab524904 100644 --- a/src/util.rs +++ b/src/util.rs @@ -26,6 +26,7 @@ use tracing::info; use uuid::Uuid; pub type FiatNames = std::collections::HashMap; +const MAX_RETRY: u16 = 4; pub async fn retries_yadio_request( req_string: &str, @@ -68,19 +69,19 @@ pub async fn get_market_quote( let mut no_answer_api = false; // Retry for 4 times - for retries_num in 1..=4 { + for retries_num in 1..=MAX_RETRY { match retries_yadio_request(&req_string, fiat_code).await { Ok(response) => { req = response; break; } Err(_e) => { - if retries_num == 4 { + if retries_num == MAX_RETRY { no_answer_api = true; } println!( "API price request failed retrying - {} tentatives left.", - (4 - retries_num) + (MAX_RETRY - retries_num) ); thread::sleep(std::time::Duration::from_secs(2)); } From 632a06c1d01a97c79d6feb0885aefc3d66dd3917 Mon Sep 17 00:00:00 2001 From: Arkanoider Date: Sun, 7 Jan 2024 12:16:48 +0100 Subject: [PATCH 5/5] Added an extra check on negative values on creating order --- src/app/order.rs | 16 ++++++++++++++++ src/error.rs | 2 ++ 2 files changed, 18 insertions(+) diff --git a/src/app/order.rs b/src/app/order.rs index cb4cd5c9..57cc0060 100644 --- a/src/app/order.rs +++ b/src/app/order.rs @@ -28,6 +28,22 @@ pub async fn order_action( _ => order.amount, }; + // Check amount is positive - extra safety check + if quote < 0 { + let message = Message::cant_do( + order.id, + None, + Some(Content::TextMessage(format!( + "Amount must be positive {} is not valid", + order.amount + ))), + ); + let message = message.as_json()?; + send_dm(client, my_keys, &event.pubkey, message).await?; + + return Ok(()); + } + if quote > mostro_settings.max_order_amount as i64 { let message = Message::cant_do( order.id, diff --git a/src/error.rs b/src/error.rs index ab3da7c0..594cc8ca 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,6 +11,7 @@ pub enum MostroError { NoAPIResponse, NoCurrency, MalformedAPIRes, + NegativeAmount, } impl std::error::Error for MostroError {} @@ -27,6 +28,7 @@ impl fmt::Display for MostroError { MostroError::NoAPIResponse => write!(f, "Price API not answered - retry"), MostroError::NoCurrency => write!(f, "Currency requested is not present in the exchange list, please specify a fixed rate"), MostroError::MalformedAPIRes => write!(f, "Malformed answer from exchange quoting request"), + MostroError::NegativeAmount => write!(f, "Negative amount is not valid"), } } }