Skip to main content

pyth_lazer_protocol/
publisher.rs

1use {
2    crate::{price::Price, rate::Rate, time::TimestampUs, PriceFeedId},
3    derive_more::From,
4    serde::{Deserialize, Serialize},
5};
6
7/// Represents a binary (bincode-serialized) stream update sent
8/// from the publisher to the router.
9#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[serde(rename_all = "camelCase")]
11pub struct PriceFeedDataV2 {
12    pub price_feed_id: PriceFeedId,
13    /// Timestamp of the last update provided by the source of the prices
14    /// (like an exchange). If unavailable, this value is set to `publisher_timestamp_us`.
15    pub source_timestamp_us: TimestampUs,
16    /// Timestamp of the last update provided by the publisher.
17    pub publisher_timestamp_us: TimestampUs,
18    /// Last known value of the best executable price of this price feed.
19    /// `None` if no value is currently available.
20    pub price: Option<Price>,
21    /// Last known value of the best bid price of this price feed.
22    /// `None` if no value is currently available.
23    pub best_bid_price: Option<Price>,
24    /// Last known value of the best ask price of this price feed.
25    /// `None` if no value is currently available.
26    pub best_ask_price: Option<Price>,
27    /// Last known value of the funding rate of this feed.
28    /// `None` if no value is currently available.
29    pub funding_rate: Option<Rate>,
30}
31
32/// Old Represents a binary (bincode-serialized) stream update sent
33/// from the publisher to the router.
34/// Superseded by `PriceFeedData`.
35#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
36#[serde(rename_all = "camelCase")]
37pub struct PriceFeedDataV1 {
38    pub price_feed_id: PriceFeedId,
39    /// Timestamp of the last update provided by the source of the prices
40    /// (like an exchange). If unavailable, this value is set to `publisher_timestamp_us`.
41    pub source_timestamp_us: TimestampUs,
42    /// Timestamp of the last update provided by the publisher.
43    pub publisher_timestamp_us: TimestampUs,
44    /// Last known value of the best executable price of this price feed.
45    /// `None` if no value is currently available.
46    #[serde(with = "crate::serde_price_as_i64")]
47    pub price: Option<Price>,
48    /// Last known value of the best bid price of this price feed.
49    /// `None` if no value is currently available.
50    #[serde(with = "crate::serde_price_as_i64")]
51    pub best_bid_price: Option<Price>,
52    /// Last known value of the best ask price of this price feed.
53    /// `None` if no value is currently available.
54    #[serde(with = "crate::serde_price_as_i64")]
55    pub best_ask_price: Option<Price>,
56}
57
58impl From<PriceFeedDataV1> for PriceFeedDataV2 {
59    fn from(v0: PriceFeedDataV1) -> Self {
60        Self {
61            price_feed_id: v0.price_feed_id,
62            source_timestamp_us: v0.source_timestamp_us,
63            publisher_timestamp_us: v0.publisher_timestamp_us,
64            price: v0.price,
65            best_bid_price: v0.best_bid_price,
66            best_ask_price: v0.best_ask_price,
67            funding_rate: None,
68        }
69    }
70}
71
72/// A response sent from the server to the publisher client.
73/// Currently only serde errors are reported back to the client.
74#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, From)]
75#[serde(tag = "type")]
76#[serde(rename_all = "camelCase")]
77pub enum ServerResponse {
78    UpdateDeserializationError(UpdateDeserializationErrorResponse),
79}
80/// Sent to the publisher if the binary data could not be parsed
81#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
82#[serde(rename_all = "camelCase")]
83pub struct UpdateDeserializationErrorResponse {
84    pub error: String,
85}
86
87#[test]
88fn price_feed_data_v1_serde() {
89    let data = [
90        1, 0, 0, 0, // price_feed_id
91        2, 0, 0, 0, 0, 0, 0, 0, // source_timestamp_us
92        3, 0, 0, 0, 0, 0, 0, 0, // publisher_timestamp_us
93        4, 0, 0, 0, 0, 0, 0, 0, // price
94        5, 0, 0, 0, 0, 0, 0, 0, // best_bid_price
95        6, 2, 0, 0, 0, 0, 0, 0, // best_ask_price
96    ];
97
98    let expected = PriceFeedDataV1 {
99        price_feed_id: PriceFeedId(1),
100        source_timestamp_us: TimestampUs::from_micros(2),
101        publisher_timestamp_us: TimestampUs::from_micros(3),
102        price: Some(Price::from_nonzero_mantissa(4.try_into().unwrap())),
103        best_bid_price: Some(Price::from_nonzero_mantissa(5.try_into().unwrap())),
104        best_ask_price: Some(Price::from_nonzero_mantissa(
105            (2 * 256 + 6).try_into().unwrap(),
106        )),
107    };
108    assert_eq!(
109        bincode::serde::decode_from_slice::<PriceFeedDataV1, _>(&data, bincode::config::legacy())
110            .unwrap()
111            .0,
112        expected
113    );
114    assert_eq!(
115        bincode::serde::encode_to_vec(&expected, bincode::config::legacy()).unwrap(),
116        data
117    );
118
119    let data2 = [
120        1, 0, 0, 0, // price_feed_id
121        2, 0, 0, 0, 0, 0, 0, 0, // source_timestamp_us
122        3, 0, 0, 0, 0, 0, 0, 0, // publisher_timestamp_us
123        4, 0, 0, 0, 0, 0, 0, 0, // price
124        0, 0, 0, 0, 0, 0, 0, 0, // best_bid_price
125        0, 0, 0, 0, 0, 0, 0, 0, // best_ask_price
126    ];
127    let expected2 = PriceFeedDataV1 {
128        price_feed_id: PriceFeedId(1),
129        source_timestamp_us: TimestampUs::from_micros(2),
130        publisher_timestamp_us: TimestampUs::from_micros(3),
131        price: Some(Price::from_nonzero_mantissa(4.try_into().unwrap())),
132        best_bid_price: None,
133        best_ask_price: None,
134    };
135    assert_eq!(
136        bincode::serde::decode_from_slice::<PriceFeedDataV1, _>(&data2, bincode::config::legacy())
137            .unwrap()
138            .0,
139        expected2
140    );
141    assert_eq!(
142        bincode::serde::encode_to_vec(&expected2, bincode::config::legacy()).unwrap(),
143        data2
144    );
145}
146
147#[test]
148fn price_feed_data_v2_serde() {
149    let data = [
150        1, 0, 0, 0, // price_feed_id
151        2, 0, 0, 0, 0, 0, 0, 0, // source_timestamp_us
152        3, 0, 0, 0, 0, 0, 0, 0, // publisher_timestamp_us
153        1, 4, 0, 0, 0, 0, 0, 0, 0, // price
154        1, 5, 0, 0, 0, 0, 0, 0, 0, // best_bid_price
155        1, 6, 2, 0, 0, 0, 0, 0, 0, // best_ask_price
156        0, // funding_rate
157    ];
158
159    let expected = PriceFeedDataV2 {
160        price_feed_id: PriceFeedId(1),
161        source_timestamp_us: TimestampUs::from_micros(2),
162        publisher_timestamp_us: TimestampUs::from_micros(3),
163        price: Some(Price::from_nonzero_mantissa(4.try_into().unwrap())),
164        best_bid_price: Some(Price::from_nonzero_mantissa(5.try_into().unwrap())),
165        best_ask_price: Some(Price::from_nonzero_mantissa(
166            (2 * 256 + 6).try_into().unwrap(),
167        )),
168        funding_rate: None,
169    };
170    assert_eq!(
171        bincode::serde::decode_from_slice::<PriceFeedDataV2, _>(&data, bincode::config::legacy())
172            .unwrap()
173            .0,
174        expected
175    );
176    assert_eq!(
177        bincode::serde::encode_to_vec(&expected, bincode::config::legacy()).unwrap(),
178        data
179    );
180
181    let data2 = [
182        1, 0, 0, 0, // price_feed_id
183        2, 0, 0, 0, 0, 0, 0, 0, // source_timestamp_us
184        3, 0, 0, 0, 0, 0, 0, 0, // publisher_timestamp_us
185        1, 4, 0, 0, 0, 0, 0, 0, 0, // price
186        0, // best_bid_price
187        0, // best_ask_price
188        1, 7, 3, 0, 0, 0, 0, 0, 0, // funding_rate
189    ];
190    let expected2 = PriceFeedDataV2 {
191        price_feed_id: PriceFeedId(1),
192        source_timestamp_us: TimestampUs::from_micros(2),
193        publisher_timestamp_us: TimestampUs::from_micros(3),
194        price: Some(Price::from_nonzero_mantissa(4.try_into().unwrap())),
195        best_bid_price: None,
196        best_ask_price: None,
197        funding_rate: Some(Rate::from_mantissa(3 * 256 + 7)),
198    };
199    assert_eq!(
200        bincode::serde::decode_from_slice::<PriceFeedDataV2, _>(&data2, bincode::config::legacy())
201            .unwrap()
202            .0,
203        expected2
204    );
205    assert_eq!(
206        bincode::serde::encode_to_vec(&expected2, bincode::config::legacy()).unwrap(),
207        data2
208    );
209}