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