Skip to main content

gmsol_sdk/serde/
serde_token_map.rs

1use gmsol_utils::{
2    oracle::PriceProviderKind,
3    token_config::{FeedConfig, TokenConfig},
4};
5use indexmap::IndexMap;
6use strum::IntoEnumIterator;
7
8use crate::utils::Value;
9
10/// Serializable version of [`TokenConfig`].
11#[derive(Debug, Clone)]
12#[cfg_attr(serde, derive(serde::Serialize, serde::Deserialize))]
13pub struct SerdeTokenConfig {
14    /// Token name.
15    pub name: String,
16    /// Indicates whether the token is enabled.
17    pub is_enabled: bool,
18    /// Indicates whether the token is synthetic.
19    pub is_synthetic: bool,
20    /// The decimals of token amount.
21    pub token_decimals: u8,
22    /// The precision of the price.
23    pub price_precision: u8,
24    /// Expected price provider.
25    pub expected_provider: PriceProviderKind,
26    /// Feeds.
27    pub feeds: IndexMap<PriceProviderKind, SerdeFeedConfig>,
28    /// Heartbeat duration.
29    pub heartbeat_duration: u32,
30}
31
32impl<'a> TryFrom<&'a TokenConfig> for SerdeTokenConfig {
33    type Error = crate::Error;
34
35    fn try_from(config: &'a TokenConfig) -> Result<Self, Self::Error> {
36        let feeds = PriceProviderKind::iter()
37            .filter_map(|kind| {
38                config
39                    .get_feed_config(&kind)
40                    .ok()
41                    .map(|config| (kind, SerdeFeedConfig::from_feed_config(kind, config)))
42            })
43            .collect();
44        Ok(Self {
45            name: config.name().map_err(crate::Error::custom)?.to_string(),
46            is_enabled: config.is_enabled(),
47            is_synthetic: config.is_synthetic(),
48            token_decimals: config.token_decimals(),
49            price_precision: config.precision(),
50            expected_provider: config.expected_provider().map_err(crate::Error::custom)?,
51            feeds,
52            heartbeat_duration: config.heartbeat_duration(),
53        })
54    }
55}
56
57/// Encoding.
58#[derive(Debug, Clone, Copy)]
59#[cfg_attr(serde, derive(serde::Serialize, serde::Deserialize))]
60#[cfg_attr(serde, serde(rename_all = "snake_case"))]
61pub enum Encoding {
62    /// Hex.
63    Hex,
64    /// Base58,
65    Base58,
66}
67
68/// Serializable version of [`FeedConfig`]
69#[derive(Debug, Clone)]
70#[cfg_attr(serde, derive(serde::Serialize, serde::Deserialize))]
71pub struct SerdeFeedConfig {
72    /// Feed ID
73    pub feed_id: String,
74    /// The encoding type of Feed ID.
75    pub feed_id_encoding: Encoding,
76    /// Timestamp adjustment.
77    pub timestamp_adjustment: u32,
78    /// Max deviation factor.
79    #[cfg_attr(serde, serde(default))]
80    pub max_deviation_factor: Option<Value>,
81}
82
83impl SerdeFeedConfig {
84    /// Create from [`FeedConfig`].
85    pub fn from_feed_config(kind: PriceProviderKind, config: &FeedConfig) -> Self {
86        let max_deviation_factor = config.max_deviation_factor().map(Value::from_u128);
87        match kind {
88            PriceProviderKind::Pyth | PriceProviderKind::ChainlinkDataStreams => Self {
89                feed_id_encoding: Encoding::Hex,
90                feed_id: format!("0x{}", hex::encode(config.feed())),
91                timestamp_adjustment: config.timestamp_adjustment(),
92                max_deviation_factor,
93            },
94            _ => Self {
95                feed_id_encoding: Encoding::Base58,
96                feed_id: config.feed().to_string(),
97                timestamp_adjustment: config.timestamp_adjustment(),
98                max_deviation_factor,
99            },
100        }
101    }
102
103    /// Get formatted feed id.
104    pub fn formatted_feed_id(&self) -> String {
105        match self.feed_id_encoding {
106            Encoding::Hex => format!("0x{}", self.feed_id),
107            Encoding::Base58 => self.feed_id.clone(),
108        }
109    }
110}