templar_common/market/
price_oracle_configuration.rs

1use near_sdk::{near, AccountId, Gas, Promise};
2
3use crate::{
4    oracle::pyth::{ext_pyth, OracleResponse, PriceIdentifier},
5    price::PricePair,
6};
7
8/// Oracle configuration.
9///
10/// Supports oracles that implement
11/// [Pyth Network](https://docs.pyth.network/)'s functionality.
12#[derive(Clone, Debug, PartialEq, Eq)]
13#[near(serializers = [json, borsh])]
14pub struct PriceOracleConfiguration {
15    /// Account ID of the oracle contract.
16    pub account_id: AccountId,
17    /// Price identifier of the collateral asset in the oracle contract.
18    pub collateral_asset_price_id: PriceIdentifier,
19    /// Collateral asset decimals, to convert the oracle price.
20    pub collateral_asset_decimals: i32,
21    /// Price identifier of the borrow asset in the oracle contract.
22    pub borrow_asset_price_id: PriceIdentifier,
23    /// Borrow asset decimals, to convert the oracle price.
24    pub borrow_asset_decimals: i32,
25    /// Maximum price age to accept from the oracle, after which the price
26    /// will be considered stale and rejected.
27    pub price_maximum_age_s: u32,
28}
29
30impl PriceOracleConfiguration {
31    // Usually seems to take 1.64 TGas, but LST adapter contract may require as much as 14.
32    pub const GAS_RETRIEVE_PRICE_PAIR: Gas = Gas::from_tgas(15);
33
34    pub fn retrieve_price_pair(&self) -> Promise {
35        ext_pyth::ext(self.account_id.clone())
36            .with_static_gas(Self::GAS_RETRIEVE_PRICE_PAIR)
37            .list_ema_prices_no_older_than(
38                vec![self.borrow_asset_price_id, self.collateral_asset_price_id],
39                u64::from(self.price_maximum_age_s),
40            )
41    }
42
43    /// # Errors
44    ///
45    /// If the response from the oracle does not contain valid prices for the
46    /// configured asset pair.
47    pub fn create_price_pair(
48        &self,
49        oracle_response: &OracleResponse,
50    ) -> Result<PricePair, error::RetrievalError> {
51        Ok(PricePair::new(
52            oracle_response
53                .get(&self.collateral_asset_price_id)
54                .and_then(|o| o.as_ref())
55                .ok_or(error::RetrievalError::MissingPrice)?,
56            self.collateral_asset_decimals,
57            oracle_response
58                .get(&self.borrow_asset_price_id)
59                .and_then(|o| o.as_ref())
60                .ok_or(error::RetrievalError::MissingPrice)?,
61            self.borrow_asset_decimals,
62        )?)
63    }
64}
65
66pub mod error {
67    use thiserror::Error;
68
69    #[derive(Clone, Debug, Error)]
70    #[error("Error retrieving price: {0}")]
71    pub enum RetrievalError {
72        #[error("Missing price")]
73        MissingPrice,
74        #[error(transparent)]
75        PriceData(#[from] crate::price::error::PriceDataError),
76    }
77}