templar_common/market/
price_oracle_configuration.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
use near_sdk::{near, AccountId, Gas, Promise};

use crate::{
    oracle::pyth::{ext_pyth, OracleResponse, PriceIdentifier},
    price::PricePair,
};

/// Oracle configuration.
///
/// Supports oracles that implement
/// [Pyth Network](https://docs.pyth.network/)'s functionality.
#[derive(Clone, Debug, PartialEq, Eq)]
#[near(serializers = [json, borsh])]
pub struct PriceOracleConfiguration {
    /// Account ID of the oracle contract.
    pub account_id: AccountId,
    /// Price identifier of the collateral asset in the oracle contract.
    pub collateral_asset_price_id: PriceIdentifier,
    /// Collateral asset decimals, to convert the oracle price.
    pub collateral_asset_decimals: i32,
    /// Price identifier of the borrow asset in the oracle contract.
    pub borrow_asset_price_id: PriceIdentifier,
    /// Borrow asset decimals, to convert the oracle price.
    pub borrow_asset_decimals: i32,
    /// Maximum price age to accept from the oracle, after which the price
    /// will be considered stale and rejected.
    pub price_maximum_age_s: u32,
}

impl PriceOracleConfiguration {
    // Usually seems to take 1.64 TGas, but LST adapter contract may require as much as 14.
    pub const GAS_RETRIEVE_PRICE_PAIR: Gas = Gas::from_tgas(15);

    pub fn retrieve_price_pair(&self) -> Promise {
        ext_pyth::ext(self.account_id.clone())
            .with_static_gas(Self::GAS_RETRIEVE_PRICE_PAIR)
            .list_ema_prices_no_older_than(
                vec![self.borrow_asset_price_id, self.collateral_asset_price_id],
                u64::from(self.price_maximum_age_s),
            )
    }

    /// # Errors
    ///
    /// If the response from the oracle does not contain valid prices for the
    /// configured asset pair.
    pub fn create_price_pair(
        &self,
        oracle_response: &OracleResponse,
    ) -> Result<PricePair, error::RetrievalError> {
        Ok(PricePair::new(
            oracle_response
                .get(&self.collateral_asset_price_id)
                .and_then(|o| o.as_ref())
                .ok_or(error::RetrievalError::MissingPrice)?,
            self.collateral_asset_decimals,
            oracle_response
                .get(&self.borrow_asset_price_id)
                .and_then(|o| o.as_ref())
                .ok_or(error::RetrievalError::MissingPrice)?,
            self.borrow_asset_decimals,
        )?)
    }
}

pub mod error {
    use thiserror::Error;

    #[derive(Clone, Debug, Error)]
    #[error("Error retrieving price: {0}")]
    pub enum RetrievalError {
        #[error("Missing price")]
        MissingPrice,
        #[error(transparent)]
        PriceData(#[from] crate::price::error::PriceDataError),
    }
}