Skip to content

Commit fa8fe70

Browse files
committed
add rsn_xag oracle option
- add ergodex as datasource
1 parent 1c3cb36 commit fa8fe70

File tree

6 files changed

+175
-1
lines changed

6 files changed

+175
-1
lines changed

core/src/datapoint_source.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ mod erg_usd;
1111
mod erg_xau;
1212
mod predef;
1313
mod erg_xag;
14+
mod rsn_xag;
15+
mod ergodex;
1416

1517
use crate::oracle_types::Rate;
1618
use crate::pool_config::PredefinedDataPointSource;

core/src/datapoint_source/coingecko.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::datapoint_source::assets_exchange_rate::AssetsExchangeRate;
22
use crate::datapoint_source::assets_exchange_rate::NanoErg;
33
use crate::datapoint_source::DataPointSourceError;
4-
4+
use crate::datapoint_source::rsn_xag::Rsn;
55
use super::ada_usd::Lovelace;
66
use super::assets_exchange_rate::Btc;
77
use super::assets_exchange_rate::Usd;
@@ -180,6 +180,46 @@ pub async fn get_btc_nanoerg() -> Result<AssetsExchangeRate<Btc, NanoErg>, DataP
180180
Ok(rate)
181181
}
182182

183+
pub async fn get_kgag_rsn() -> Result<AssetsExchangeRate<KgAg, Rsn>, DataPointSourceError> {
184+
let url = "https://api.coingecko.com/api/v3/simple/price?ids=rosen-bridge&vs_currencies=XAG";
185+
let resp = reqwest::get(url).await?;
186+
let price_json = json::parse(&resp.text().await?)?;
187+
if let Some(p) = price_json["rosen-bridge"]["xag"].as_f64() {
188+
// Convert from price RSN/XAG
189+
let rsn_per_ag = KgAg::from_troy_ounce(1.0 / p);
190+
let rate = AssetsExchangeRate {
191+
per1: KgAg {},
192+
get: Rsn {},
193+
rate: rsn_per_ag,
194+
};
195+
Ok(rate)
196+
} else {
197+
Err(DataPointSourceError::JsonMissingField {
198+
field: "rsn.xag as f64".to_string(),
199+
json: price_json.dump(),
200+
})
201+
}
202+
}
203+
204+
pub async fn get_rsn_usd() -> Result<AssetsExchangeRate<Usd, Rsn>, DataPointSourceError> {
205+
let url = "https://api.coingecko.com/api/v3/simple/price?ids=rosen-bridge&vs_currencies=USD";
206+
let resp = reqwest::get(url).await?;
207+
let price_json = json::parse(&resp.text().await?)?;
208+
if let Some(p) = price_json["rosen-bridge"]["usd"].as_f64() {
209+
let rate = AssetsExchangeRate {
210+
per1: Usd {},
211+
get: Rsn {},
212+
rate: 1.0 / p,
213+
};
214+
Ok(rate)
215+
} else {
216+
Err(DataPointSourceError::JsonMissingField {
217+
field: "rsn.usd as f64".to_string(),
218+
json: price_json.dump(),
219+
})
220+
}
221+
}
222+
183223
#[cfg(test)]
184224
mod tests {
185225
use super::*;
@@ -217,4 +257,18 @@ mod tests {
217257
tokio_test::block_on(get_btc_nanoerg()).unwrap();
218258
assert!(pair.rate > 0.0);
219259
}
260+
261+
#[test]
262+
fn test_rsn_xag_price() {
263+
let pair: AssetsExchangeRate<KgAg, Rsn> =
264+
tokio_test::block_on(get_kgag_rsn()).unwrap();
265+
assert!(pair.rate > 0.0);
266+
}
267+
268+
#[test]
269+
fn test_rsn_usd_price() {
270+
let pair: AssetsExchangeRate<Usd, Rsn> =
271+
tokio_test::block_on(get_rsn_usd()).unwrap();
272+
assert!(pair.rate > 0.0);
273+
}
220274
}

core/src/datapoint_source/ergodex.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use crate::datapoint_source::assets_exchange_rate::{AssetsExchangeRate, NanoErg};
2+
use crate::datapoint_source::DataPointSourceError;
3+
use crate::datapoint_source::rsn_xag::Rsn;
4+
5+
pub async fn get_rsn_nanoerg() -> Result<AssetsExchangeRate<NanoErg,Rsn>, DataPointSourceError> {
6+
let url = "https://api.spectrum.fi/v1/amm/pool/1b694b15467c62f0cd4525e368dbdea2329c713aa200b73df4a622e950551b40/stats";
7+
let resp = reqwest::get(url).await?;
8+
let pool_json = json::parse(&resp.text().await?)?;
9+
let locked_erg = pool_json["lockedX"]["amount"].as_f64()
10+
.ok_or_else(||
11+
DataPointSourceError::JsonMissingField {
12+
field: "lockedX.amount as f64".to_string(),
13+
json: pool_json.dump(),
14+
})?;
15+
16+
let locked_rsn = pool_json["lockedY"]["amount"].as_f64()
17+
.ok_or_else(||
18+
DataPointSourceError::JsonMissingField {
19+
field: "lockedY.amount as f64".to_string(),
20+
json: pool_json.dump(),
21+
})?;
22+
let price = Rsn::from_rsn(Rsn::from_rsn(locked_rsn) / NanoErg::from_erg(locked_erg));
23+
let rate = AssetsExchangeRate {
24+
per1: NanoErg {},
25+
get: Rsn {},
26+
rate: price
27+
};
28+
Ok(rate)
29+
}
30+
31+
#[cfg(test)]
32+
mod tests {
33+
use super::*;
34+
35+
#[test]
36+
fn test_rsn_nanoerg_price() {
37+
let pair: AssetsExchangeRate<NanoErg, Rsn> =
38+
tokio_test::block_on(get_rsn_nanoerg()).unwrap();
39+
assert!(pair.rate > 0.0);
40+
}
41+
}

core/src/datapoint_source/predef.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::datapoint_source::rsn_xag::rsn_kgag_sources;
12
use crate::oracle_types::Rate;
23

34
use super::ada_usd::usd_lovelace_sources;
@@ -35,6 +36,9 @@ async fn fetch_predef_source_aggregated(
3536
PredefinedDataPointSource::NanoErgBTC => {
3637
fetch_aggregated(nanoerg_btc_sources()).await?.rate
3738
}
39+
PredefinedDataPointSource::RsnXag => {
40+
fetch_aggregated(rsn_kgag_sources()).await?.rate
41+
}
3842
};
3943
Ok((rate_float as i64).into())
4044
}

core/src/datapoint_source/rsn_xag.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use std::pin::Pin;
2+
use futures::Future;
3+
4+
use crate::datapoint_source::assets_exchange_rate::{convert_rate, Asset, AssetsExchangeRate};
5+
use crate::datapoint_source::{bitpanda, coingecko, ergodex, DataPointSourceError};
6+
use crate::datapoint_source::erg_xag::KgAg;
7+
8+
#[derive(Debug, Clone, Copy)]
9+
pub struct Rsn {}
10+
11+
impl Asset for Rsn {}
12+
13+
impl Rsn {
14+
pub fn from_rsn(rsn: f64) -> f64 {
15+
rsn * 1_000.0
16+
}
17+
}
18+
19+
#[allow(clippy::type_complexity)]
20+
pub fn rsn_kgag_sources() -> Vec<
21+
Pin<Box<dyn Future<Output = Result<AssetsExchangeRate<KgAg, Rsn>, DataPointSourceError>>>>,
22+
> {
23+
vec![
24+
Box::pin(coingecko::get_kgag_rsn()),
25+
Box::pin(get_rsn_kgag_erg()),
26+
Box::pin(get_rsn_kgag_usd())
27+
]
28+
}
29+
30+
// Calculate RSN/KGAG through RSN/USD and KGAG/USD
31+
async fn get_rsn_kgag_usd() -> Result<AssetsExchangeRate<KgAg, Rsn>, DataPointSourceError> {
32+
Ok(convert_rate(
33+
coingecko::get_rsn_usd().await?,
34+
bitpanda::get_kgag_usd().await?,
35+
))
36+
}
37+
38+
// Calculate KGAG/RSN through KGAG/ERG and ERG/RSN
39+
async fn get_rsn_kgag_erg() -> Result<AssetsExchangeRate<KgAg, Rsn>, DataPointSourceError> {
40+
Ok(convert_rate(
41+
ergodex::get_rsn_nanoerg().await?,
42+
coingecko::get_kgag_nanoerg().await?,
43+
))
44+
}
45+
46+
#[cfg(test)]
47+
mod tests {
48+
use super::*;
49+
50+
#[test]
51+
fn test_kgag_rsn_combined() {
52+
let combined = tokio_test::block_on(get_rsn_kgag_usd()).unwrap();
53+
let coingecko = tokio_test::block_on(coingecko::get_kgag_rsn()).unwrap();
54+
let ergodex = tokio_test::block_on(get_rsn_kgag_erg()).unwrap();
55+
let deviation_from_coingecko = (combined.rate - coingecko.rate).abs() / coingecko.rate;
56+
assert!(
57+
deviation_from_coingecko < 0.05,
58+
"up to 5% deviation is allowed"
59+
);
60+
let ergodex_deviation_from_coingecko =
61+
(ergodex.rate - coingecko.rate).abs() / coingecko.rate;
62+
assert!(
63+
ergodex_deviation_from_coingecko < 0.05,
64+
"up to 5% deviation is allowed"
65+
);
66+
let deviation_from_ergodex = (ergodex.rate - combined.rate).abs() / combined.rate;
67+
assert!(
68+
deviation_from_ergodex < 0.05,
69+
"up to 5% deviation is allowed"
70+
);
71+
}
72+
}

core/src/pool_config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pub enum PredefinedDataPointSource {
5959
NanoErgXag,
6060
NanoAdaUsd,
6161
NanoErgBTC,
62+
RsnXag,
6263
}
6364

6465
/// Holds the token ids of every important token used by the oracle pool.

0 commit comments

Comments
 (0)