Skip to main content

pyth_lazer_protocol/
lib.rs

1//! Lazer type definitions and utilities.
2
3/// Types describing Lazer HTTP and WebSocket APIs.
4pub mod api;
5/// Binary delivery format for WebSocket.
6pub mod binary_update;
7mod dynamic_value;
8mod feed_kind;
9/// Lazer Agent JSON-RPC API.
10pub mod jrpc;
11/// Types describing Lazer's verifiable messages containing signature and payload.
12pub mod message;
13/// Types describing Lazer's message payload.
14pub mod payload;
15mod price;
16/// Legacy Websocket API for publishers.
17pub mod publisher;
18mod rate;
19mod serde_price_as_i64;
20mod serde_str;
21mod symbol_state;
22/// Lazer's types for time representation.
23pub mod time;
24
25use serde::{Deserialize, Serialize};
26use utoipa::ToSchema;
27use {
28    derive_more::derive::{From, Into},
29    strum::FromRepr,
30};
31
32pub use crate::{
33    dynamic_value::DynamicValue,
34    feed_kind::FeedKind,
35    price::{Price, PriceError},
36    rate::{Rate, RateError},
37    symbol_state::SymbolState,
38};
39
40#[derive(
41    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, From, Into,
42)]
43pub struct AssetId(pub u32);
44
45#[derive(
46    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, From, Into,
47)]
48pub struct PublisherId(pub u16);
49
50#[derive(
51    Debug,
52    Clone,
53    Copy,
54    PartialEq,
55    Eq,
56    Hash,
57    PartialOrd,
58    Ord,
59    Serialize,
60    Deserialize,
61    From,
62    Into,
63    ToSchema,
64)]
65#[schema(value_type = u32)]
66pub struct PriceFeedId(pub u32);
67
68#[derive(
69    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, From, Into,
70)]
71pub struct ChannelId(pub u8);
72
73impl ChannelId {
74    pub const REAL_TIME: ChannelId = ChannelId(1);
75    pub const FIXED_RATE_50: ChannelId = ChannelId(2);
76    pub const FIXED_RATE_200: ChannelId = ChannelId(3);
77    pub const FIXED_RATE_1000: ChannelId = ChannelId(4);
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema, FromRepr)]
81#[serde(rename_all = "camelCase")]
82#[repr(u8)]
83pub enum PriceFeedProperty {
84    Price,
85    BestBidPrice,
86    BestAskPrice,
87    PublisherCount,
88    Exponent,
89    Confidence,
90    FundingRate,
91    FundingTimestamp,
92    FundingRateInterval,
93    MarketSession,
94    EmaPrice,
95    EmaConfidence,
96    FeedUpdateTimestamp,
97    // More fields may be added later.
98}
99
100#[derive(Debug, Clone, Deserialize)]
101#[serde(rename_all = "kebab-case")]
102pub enum AssetClass {
103    Crypto,
104    Fx,
105    Equity,
106    Metal,
107    Rates,
108    Nav,
109    Commodity,
110    FundingRate,
111    Eco,
112    Kalshi,
113}
114
115impl AssetClass {
116    pub fn as_str(&self) -> &'static str {
117        match self {
118            AssetClass::Crypto => "crypto",
119            AssetClass::Fx => "fx",
120            AssetClass::Equity => "equity",
121            AssetClass::Metal => "metal",
122            AssetClass::Rates => "rates",
123            AssetClass::Nav => "nav",
124            AssetClass::Commodity => "commodity",
125            AssetClass::FundingRate => "funding-rate",
126            AssetClass::Eco => "eco",
127            AssetClass::Kalshi => "kalshi",
128        }
129    }
130}
131
132// Operation and coefficient for converting value to mantissa.
133enum ExponentFactor {
134    // mantissa = value * factor
135    Mul(i64),
136    // mantissa = value / factor
137    Div(i64),
138}
139
140impl ExponentFactor {
141    fn get(exponent: i16) -> Option<Self> {
142        if exponent >= 0 {
143            let exponent: u32 = exponent.try_into().ok()?;
144            Some(ExponentFactor::Div(10_i64.checked_pow(exponent)?))
145        } else {
146            let minus_exponent: u32 = exponent.checked_neg()?.try_into().ok()?;
147            Some(ExponentFactor::Mul(10_i64.checked_pow(minus_exponent)?))
148        }
149    }
150}
151
152#[test]
153fn magics_in_big_endian() {
154    use crate::{
155        binary_update::BINARY_UPDATE_FORMAT_MAGIC,
156        message::format_magics_le::{
157            EVM_FORMAT_MAGIC, JSON_FORMAT_MAGIC, LE_ECDSA_FORMAT_MAGIC, LE_UNSIGNED_FORMAT_MAGIC,
158            SOLANA_FORMAT_MAGIC,
159        },
160        payload::PAYLOAD_FORMAT_MAGIC,
161    };
162
163    // The values listed in this test can be used when reading the magic headers in BE format
164    // (e.g., on EVM).
165
166    assert_eq!(u32::swap_bytes(BINARY_UPDATE_FORMAT_MAGIC), 1937213467);
167    assert_eq!(u32::swap_bytes(PAYLOAD_FORMAT_MAGIC), 1976813459);
168
169    assert_eq!(u32::swap_bytes(SOLANA_FORMAT_MAGIC), 3103857282);
170    assert_eq!(u32::swap_bytes(JSON_FORMAT_MAGIC), 2584795844);
171    assert_eq!(u32::swap_bytes(EVM_FORMAT_MAGIC), 706910618);
172    assert_eq!(u32::swap_bytes(LE_ECDSA_FORMAT_MAGIC), 3837609805);
173    assert_eq!(u32::swap_bytes(LE_UNSIGNED_FORMAT_MAGIC), 206398297);
174
175    for magic in [
176        BINARY_UPDATE_FORMAT_MAGIC,
177        PAYLOAD_FORMAT_MAGIC,
178        SOLANA_FORMAT_MAGIC,
179        JSON_FORMAT_MAGIC,
180        EVM_FORMAT_MAGIC,
181        LE_ECDSA_FORMAT_MAGIC,
182        LE_UNSIGNED_FORMAT_MAGIC,
183    ] {
184        // Required to distinguish between byte orders.
185        assert_ne!(u32::swap_bytes(magic), magic);
186    }
187}