From ff6f3840f368fa30a4717f1002e9840b2ee1b853 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 | 4 +- core/src/datapoint_source/bitpanda.rs | 6 +-- core/src/datapoint_source/coingecko.rs | 66 ++++++++++++++++++++--- core/src/datapoint_source/erg_xag.rs | 10 ++-- core/src/datapoint_source/ergodex.rs | 41 +++++++++++++++ core/src/datapoint_source/predef.rs | 2 + core/src/datapoint_source/rsn_xag.rs | 72 ++++++++++++++++++++++++++ core/src/pool_config.rs | 1 + 8 files changed, 187 insertions(+), 15 deletions(-) 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..20c3a108 100644 --- a/core/src/datapoint_source.rs +++ b/core/src/datapoint_source.rs @@ -8,9 +8,11 @@ mod coingecko; mod custom_ext_script; mod erg_btc; mod erg_usd; +mod erg_xag; mod erg_xau; +mod ergodex; mod predef; -mod erg_xag; +mod rsn_xag; use crate::oracle_types::Rate; use crate::pool_config::PredefinedDataPointSource; diff --git a/core/src/datapoint_source/bitpanda.rs b/core/src/datapoint_source/bitpanda.rs index c08c738f..af57a2cb 100644 --- a/core/src/datapoint_source/bitpanda.rs +++ b/core/src/datapoint_source/bitpanda.rs @@ -1,8 +1,8 @@ use super::assets_exchange_rate::AssetsExchangeRate; use super::assets_exchange_rate::Btc; use super::assets_exchange_rate::Usd; -use super::erg_xau::KgAu; use super::erg_xag::KgAg; +use super::erg_xau::KgAu; use super::DataPointSourceError; #[derive(Debug, Clone)] @@ -139,13 +139,13 @@ mod tests { let pair: AssetsExchangeRate = tokio_test::block_on(get_kgau_usd()).unwrap(); assert!(pair.rate > 0.0); } - + #[test] fn test_kgag_usd_price() { let pair: AssetsExchangeRate = tokio_test::block_on(get_kgag_usd()).unwrap(); assert!(pair.rate > 0.0); } - + #[test] fn test_btc_usd_price() { let pair: AssetsExchangeRate = tokio_test::block_on(get_btc_usd()).unwrap(); diff --git a/core/src/datapoint_source/coingecko.rs b/core/src/datapoint_source/coingecko.rs index 35681386..1dc3ab17 100644 --- a/core/src/datapoint_source/coingecko.rs +++ b/core/src/datapoint_source/coingecko.rs @@ -1,12 +1,12 @@ -use crate::datapoint_source::assets_exchange_rate::AssetsExchangeRate; -use crate::datapoint_source::assets_exchange_rate::NanoErg; -use crate::datapoint_source::DataPointSourceError; - use super::ada_usd::Lovelace; use super::assets_exchange_rate::Btc; use super::assets_exchange_rate::Usd; -use super::erg_xau::KgAu; use super::erg_xag::KgAg; +use super::erg_xau::KgAu; +use crate::datapoint_source::assets_exchange_rate::AssetsExchangeRate; +use crate::datapoint_source::assets_exchange_rate::NanoErg; +use crate::datapoint_source::rsn_xag::Rsn; +use crate::datapoint_source::DataPointSourceError; #[cfg(not(test))] pub async fn get_kgau_nanoerg() -> Result, DataPointSourceError> { @@ -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::*; @@ -190,10 +230,10 @@ mod tests { tokio_test::block_on(get_kgau_nanoerg()).unwrap(); assert!(pair.rate > 0.0); } - + #[test] fn test_erg_xag_price() { - let pair: AssetsExchangeRate = + let pair: AssetsExchangeRate = tokio_test::block_on(get_kgag_nanoerg()).unwrap(); assert!(pair.rate > 0.0); } @@ -217,4 +257,16 @@ 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/erg_xag.rs b/core/src/datapoint_source/erg_xag.rs index 8197c7e7..57eccfa6 100644 --- a/core/src/datapoint_source/erg_xag.rs +++ b/core/src/datapoint_source/erg_xag.rs @@ -4,10 +4,12 @@ use std::pin::Pin; use futures::Future; -use crate::datapoint_source::assets_exchange_rate::{convert_rate, Asset, AssetsExchangeRate, NanoErg}; -use crate::datapoint_source::{bitpanda, coingecko, DataPointSourceError}; use crate::datapoint_source::aggregator::fetch_aggregated; +use crate::datapoint_source::assets_exchange_rate::{ + convert_rate, Asset, AssetsExchangeRate, NanoErg, +}; use crate::datapoint_source::erg_usd::nanoerg_usd_sources; +use crate::datapoint_source::{bitpanda, coingecko, DataPointSourceError}; #[derive(Debug, Clone, Copy)] pub struct KgAg {} @@ -25,7 +27,7 @@ impl KgAg { // troy ounces per kg oz * 32.150746568627 } - + pub fn from_gram(g: f64) -> f64 { g * 1000.0 } @@ -33,7 +35,7 @@ impl KgAg { #[allow(clippy::type_complexity)] pub fn nanoerg_kgag_sources() -> Vec< -Pin, DataPointSourceError>>>>, + Pin, DataPointSourceError>>>>, > { vec![ Box::pin(coingecko::get_kgag_nanoerg()), diff --git a/core/src/datapoint_source/ergodex.rs b/core/src/datapoint_source/ergodex.rs new file mode 100644 index 00000000..d0d2a08f --- /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::rsn_xag::Rsn; +use crate::datapoint_source::DataPointSourceError; + +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); + } +} diff --git a/core/src/datapoint_source/predef.rs b/core/src/datapoint_source/predef.rs index 0578382b..e4602cdb 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,7 @@ 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..e0380800 --- /dev/null +++ b/core/src/datapoint_source/rsn_xag.rs @@ -0,0 +1,72 @@ +use futures::Future; +use std::pin::Pin; + +use crate::datapoint_source::assets_exchange_rate::{convert_rate, Asset, AssetsExchangeRate}; +use crate::datapoint_source::erg_xag::KgAg; +use crate::datapoint_source::{bitpanda, coingecko, ergodex, DataPointSourceError}; + +#[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, 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" + ); + } +} 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.