From fa8fe70232d8129329ef4b496519bde7a9968f0a Mon Sep 17 00:00:00 2001 From: Manuel Haug Date: Sat, 7 Dec 2024 14:42:57 +0100 Subject: [PATCH] add rsn_xag oracle option - add ergodex as datasource --- core/src/datapoint_source.rs | 2 + core/src/datapoint_source/coingecko.rs | 56 +++++++++++++++++++- core/src/datapoint_source/ergodex.rs | 41 +++++++++++++++ core/src/datapoint_source/predef.rs | 4 ++ core/src/datapoint_source/rsn_xag.rs | 72 ++++++++++++++++++++++++++ core/src/pool_config.rs | 1 + 6 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 core/src/datapoint_source/ergodex.rs create mode 100644 core/src/datapoint_source/rsn_xag.rs diff --git a/core/src/datapoint_source.rs b/core/src/datapoint_source.rs index 89bc2b84..30237e34 100644 --- a/core/src/datapoint_source.rs +++ b/core/src/datapoint_source.rs @@ -11,6 +11,8 @@ mod erg_usd; mod erg_xau; mod predef; mod erg_xag; +mod rsn_xag; +mod ergodex; use crate::oracle_types::Rate; use crate::pool_config::PredefinedDataPointSource; diff --git a/core/src/datapoint_source/coingecko.rs b/core/src/datapoint_source/coingecko.rs index 35681386..ae0300e1 100644 --- a/core/src/datapoint_source/coingecko.rs +++ b/core/src/datapoint_source/coingecko.rs @@ -1,7 +1,7 @@ use crate::datapoint_source::assets_exchange_rate::AssetsExchangeRate; use crate::datapoint_source::assets_exchange_rate::NanoErg; use crate::datapoint_source::DataPointSourceError; - +use crate::datapoint_source::rsn_xag::Rsn; use super::ada_usd::Lovelace; use super::assets_exchange_rate::Btc; use super::assets_exchange_rate::Usd; @@ -180,6 +180,46 @@ pub async fn get_btc_nanoerg() -> Result, DataP Ok(rate) } +pub async fn get_kgag_rsn() -> Result, DataPointSourceError> { + let url = "https://api.coingecko.com/api/v3/simple/price?ids=rosen-bridge&vs_currencies=XAG"; + let resp = reqwest::get(url).await?; + let price_json = json::parse(&resp.text().await?)?; + if let Some(p) = price_json["rosen-bridge"]["xag"].as_f64() { + // Convert from price RSN/XAG + let rsn_per_ag = KgAg::from_troy_ounce(1.0 / p); + let rate = AssetsExchangeRate { + per1: KgAg {}, + get: Rsn {}, + rate: rsn_per_ag, + }; + Ok(rate) + } else { + Err(DataPointSourceError::JsonMissingField { + field: "rsn.xag as f64".to_string(), + json: price_json.dump(), + }) + } +} + +pub async fn get_rsn_usd() -> Result, DataPointSourceError> { + let url = "https://api.coingecko.com/api/v3/simple/price?ids=rosen-bridge&vs_currencies=USD"; + let resp = reqwest::get(url).await?; + let price_json = json::parse(&resp.text().await?)?; + if let Some(p) = price_json["rosen-bridge"]["usd"].as_f64() { + let rate = AssetsExchangeRate { + per1: Usd {}, + get: Rsn {}, + rate: 1.0 / p, + }; + Ok(rate) + } else { + Err(DataPointSourceError::JsonMissingField { + field: "rsn.usd as f64".to_string(), + json: price_json.dump(), + }) + } +} + #[cfg(test)] mod tests { use super::*; @@ -217,4 +257,18 @@ mod tests { tokio_test::block_on(get_btc_nanoerg()).unwrap(); assert!(pair.rate > 0.0); } + + #[test] + fn test_rsn_xag_price() { + let pair: AssetsExchangeRate = + tokio_test::block_on(get_kgag_rsn()).unwrap(); + assert!(pair.rate > 0.0); + } + + #[test] + fn test_rsn_usd_price() { + let pair: AssetsExchangeRate = + tokio_test::block_on(get_rsn_usd()).unwrap(); + assert!(pair.rate > 0.0); + } } diff --git a/core/src/datapoint_source/ergodex.rs b/core/src/datapoint_source/ergodex.rs new file mode 100644 index 00000000..76fbe90d --- /dev/null +++ b/core/src/datapoint_source/ergodex.rs @@ -0,0 +1,41 @@ +use crate::datapoint_source::assets_exchange_rate::{AssetsExchangeRate, NanoErg}; +use crate::datapoint_source::DataPointSourceError; +use crate::datapoint_source::rsn_xag::Rsn; + +pub async fn get_rsn_nanoerg() -> Result, DataPointSourceError> { + let url = "https://api.spectrum.fi/v1/amm/pool/1b694b15467c62f0cd4525e368dbdea2329c713aa200b73df4a622e950551b40/stats"; + let resp = reqwest::get(url).await?; + let pool_json = json::parse(&resp.text().await?)?; + let locked_erg = pool_json["lockedX"]["amount"].as_f64() + .ok_or_else(|| + DataPointSourceError::JsonMissingField { + field: "lockedX.amount as f64".to_string(), + json: pool_json.dump(), + })?; + + let locked_rsn = pool_json["lockedY"]["amount"].as_f64() + .ok_or_else(|| + DataPointSourceError::JsonMissingField { + field: "lockedY.amount as f64".to_string(), + json: pool_json.dump(), + })?; + let price = Rsn::from_rsn(Rsn::from_rsn(locked_rsn) / NanoErg::from_erg(locked_erg)); + let rate = AssetsExchangeRate { + per1: NanoErg {}, + get: Rsn {}, + rate: price + }; + Ok(rate) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_rsn_nanoerg_price() { + let pair: AssetsExchangeRate = + tokio_test::block_on(get_rsn_nanoerg()).unwrap(); + assert!(pair.rate > 0.0); + } +} \ No newline at end of file diff --git a/core/src/datapoint_source/predef.rs b/core/src/datapoint_source/predef.rs index 0578382b..d1c27653 100644 --- a/core/src/datapoint_source/predef.rs +++ b/core/src/datapoint_source/predef.rs @@ -1,3 +1,4 @@ +use crate::datapoint_source::rsn_xag::rsn_kgag_sources; use crate::oracle_types::Rate; use super::ada_usd::usd_lovelace_sources; @@ -35,6 +36,9 @@ async fn fetch_predef_source_aggregated( PredefinedDataPointSource::NanoErgBTC => { fetch_aggregated(nanoerg_btc_sources()).await?.rate } + PredefinedDataPointSource::RsnXag => { + fetch_aggregated(rsn_kgag_sources()).await?.rate + } }; Ok((rate_float as i64).into()) } diff --git a/core/src/datapoint_source/rsn_xag.rs b/core/src/datapoint_source/rsn_xag.rs new file mode 100644 index 00000000..38c182ca --- /dev/null +++ b/core/src/datapoint_source/rsn_xag.rs @@ -0,0 +1,72 @@ +use std::pin::Pin; +use futures::Future; + +use crate::datapoint_source::assets_exchange_rate::{convert_rate, Asset, AssetsExchangeRate}; +use crate::datapoint_source::{bitpanda, coingecko, ergodex, DataPointSourceError}; +use crate::datapoint_source::erg_xag::KgAg; + +#[derive(Debug, Clone, Copy)] +pub struct Rsn {} + +impl Asset for Rsn {} + +impl Rsn { + pub fn from_rsn(rsn: f64) -> f64 { + rsn * 1_000.0 + } +} + +#[allow(clippy::type_complexity)] +pub fn rsn_kgag_sources() -> Vec< + Pin, DataPointSourceError>>>>, +> { + vec![ + Box::pin(coingecko::get_kgag_rsn()), + Box::pin(get_rsn_kgag_erg()), + Box::pin(get_rsn_kgag_usd()) + ] +} + +// Calculate RSN/KGAG through RSN/USD and KGAG/USD +async fn get_rsn_kgag_usd() -> Result, DataPointSourceError> { + Ok(convert_rate( + coingecko::get_rsn_usd().await?, + bitpanda::get_kgag_usd().await?, + )) +} + +// Calculate KGAG/RSN through KGAG/ERG and ERG/RSN +async fn get_rsn_kgag_erg() -> Result, DataPointSourceError> { + Ok(convert_rate( + ergodex::get_rsn_nanoerg().await?, + coingecko::get_kgag_nanoerg().await?, + )) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_kgag_rsn_combined() { + let combined = tokio_test::block_on(get_rsn_kgag_usd()).unwrap(); + let coingecko = tokio_test::block_on(coingecko::get_kgag_rsn()).unwrap(); + let ergodex = tokio_test::block_on(get_rsn_kgag_erg()).unwrap(); + let deviation_from_coingecko = (combined.rate - coingecko.rate).abs() / coingecko.rate; + assert!( + deviation_from_coingecko < 0.05, + "up to 5% deviation is allowed" + ); + let ergodex_deviation_from_coingecko = + (ergodex.rate - coingecko.rate).abs() / coingecko.rate; + assert!( + ergodex_deviation_from_coingecko < 0.05, + "up to 5% deviation is allowed" + ); + let deviation_from_ergodex = (ergodex.rate - combined.rate).abs() / combined.rate; + assert!( + deviation_from_ergodex < 0.05, + "up to 5% deviation is allowed" + ); + } +} \ No newline at end of file diff --git a/core/src/pool_config.rs b/core/src/pool_config.rs index 04059168..a3a177d0 100644 --- a/core/src/pool_config.rs +++ b/core/src/pool_config.rs @@ -59,6 +59,7 @@ pub enum PredefinedDataPointSource { NanoErgXag, NanoAdaUsd, NanoErgBTC, + RsnXag, } /// Holds the token ids of every important token used by the oracle pool.