pyth_lazer_protocol/
payload.rs1use {
4 super::router::{PriceFeedId, PriceFeedProperty, TimestampUs},
5 crate::router::{ChannelId, Price},
6 anyhow::bail,
7 byteorder::{ByteOrder, ReadBytesExt, WriteBytesExt, BE, LE},
8 serde::{Deserialize, Serialize},
9 std::{
10 io::{Cursor, Read, Write},
11 num::NonZeroI64,
12 },
13};
14
15#[derive(Debug, Clone, PartialEq, Eq, Hash)]
17pub struct PayloadData {
18 pub timestamp_us: TimestampUs,
19 pub channel_id: ChannelId,
20 pub feeds: Vec<PayloadFeedData>,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Hash)]
25pub struct PayloadFeedData {
26 pub feed_id: PriceFeedId,
27 pub properties: Vec<PayloadPropertyValue>,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Hash)]
32pub enum PayloadPropertyValue {
33 Price(Option<Price>),
34 BestBidPrice(Option<Price>),
35 BestAskPrice(Option<Price>),
36 PublisherCount(Option<u16>),
37 Exponent(i16),
38 Confidence(Option<Price>),
39}
40
41#[derive(Debug, Clone, Default, Serialize, Deserialize)]
42pub struct AggregatedPriceFeedData {
43 pub price: Option<Price>,
44 pub best_bid_price: Option<Price>,
45 pub best_ask_price: Option<Price>,
46 pub publisher_count: Option<u16>,
47 pub confidence: Option<Price>,
48}
49
50pub const PAYLOAD_FORMAT_MAGIC: u32 = 2479346549;
51
52impl PayloadData {
53 pub fn new(
54 timestamp_us: TimestampUs,
55 channel_id: ChannelId,
56 feeds: &[(PriceFeedId, i16, AggregatedPriceFeedData)],
57 requested_properties: &[PriceFeedProperty],
58 ) -> Self {
59 Self {
60 timestamp_us,
61 channel_id,
62 feeds: feeds
63 .iter()
64 .map(|(feed_id, exponent, feed)| PayloadFeedData {
65 feed_id: *feed_id,
66 properties: requested_properties
67 .iter()
68 .map(|property| match property {
69 PriceFeedProperty::Price => PayloadPropertyValue::Price(feed.price),
70 PriceFeedProperty::BestBidPrice => {
71 PayloadPropertyValue::BestBidPrice(feed.best_bid_price)
72 }
73 PriceFeedProperty::BestAskPrice => {
74 PayloadPropertyValue::BestAskPrice(feed.best_ask_price)
75 }
76 PriceFeedProperty::PublisherCount => {
77 PayloadPropertyValue::PublisherCount(feed.publisher_count)
78 }
79 PriceFeedProperty::Exponent => {
80 PayloadPropertyValue::Exponent(*exponent)
81 }
82 PriceFeedProperty::Confidence => {
83 PayloadPropertyValue::Confidence(feed.confidence)
84 }
85 })
86 .collect(),
87 })
88 .collect(),
89 }
90 }
91
92 pub fn serialize<BO: ByteOrder>(&self, mut writer: impl Write) -> anyhow::Result<()> {
93 writer.write_u32::<BO>(PAYLOAD_FORMAT_MAGIC)?;
94 writer.write_u64::<BO>(self.timestamp_us.0)?;
95 writer.write_u8(self.channel_id.0)?;
96 writer.write_u8(self.feeds.len().try_into()?)?;
97 for feed in &self.feeds {
98 writer.write_u32::<BO>(feed.feed_id.0)?;
99 writer.write_u8(feed.properties.len().try_into()?)?;
100 for property in &feed.properties {
101 match property {
102 PayloadPropertyValue::Price(price) => {
103 writer.write_u8(PriceFeedProperty::Price as u8)?;
104 write_option_price::<BO>(&mut writer, *price)?;
105 }
106 PayloadPropertyValue::BestBidPrice(price) => {
107 writer.write_u8(PriceFeedProperty::BestBidPrice as u8)?;
108 write_option_price::<BO>(&mut writer, *price)?;
109 }
110 PayloadPropertyValue::BestAskPrice(price) => {
111 writer.write_u8(PriceFeedProperty::BestAskPrice as u8)?;
112 write_option_price::<BO>(&mut writer, *price)?;
113 }
114 PayloadPropertyValue::PublisherCount(count) => {
115 writer.write_u8(PriceFeedProperty::PublisherCount as u8)?;
116 write_option_u16::<BO>(&mut writer, *count)?;
117 }
118 PayloadPropertyValue::Exponent(exponent) => {
119 writer.write_u8(PriceFeedProperty::Exponent as u8)?;
120 writer.write_i16::<BO>(*exponent)?;
121 }
122 PayloadPropertyValue::Confidence(confidence) => {
123 writer.write_u8(PriceFeedProperty::Confidence as u8)?;
124 write_option_price::<BO>(&mut writer, *confidence)?;
125 }
126 }
127 }
128 }
129 Ok(())
130 }
131
132 pub fn deserialize_slice_le(data: &[u8]) -> anyhow::Result<Self> {
133 Self::deserialize::<LE>(Cursor::new(data))
134 }
135
136 pub fn deserialize_slice_be(data: &[u8]) -> anyhow::Result<Self> {
137 Self::deserialize::<BE>(Cursor::new(data))
138 }
139
140 pub fn deserialize<BO: ByteOrder>(mut reader: impl Read) -> anyhow::Result<Self> {
141 let magic = reader.read_u32::<BO>()?;
142 if magic != PAYLOAD_FORMAT_MAGIC {
143 bail!("magic mismatch");
144 }
145 let timestamp_us = TimestampUs(reader.read_u64::<BO>()?);
146 let channel_id = ChannelId(reader.read_u8()?);
147 let num_feeds = reader.read_u8()?;
148 let mut feeds = Vec::with_capacity(num_feeds.into());
149 for _ in 0..num_feeds {
150 let feed_id = PriceFeedId(reader.read_u32::<BO>()?);
151 let num_properties = reader.read_u8()?;
152 let mut feed = PayloadFeedData {
153 feed_id,
154 properties: Vec::with_capacity(num_properties.into()),
155 };
156 for _ in 0..num_properties {
157 let property = reader.read_u8()?;
158 let value = if property == PriceFeedProperty::Price as u8 {
159 PayloadPropertyValue::Price(read_option_price::<BO>(&mut reader)?)
160 } else if property == PriceFeedProperty::BestBidPrice as u8 {
161 PayloadPropertyValue::BestBidPrice(read_option_price::<BO>(&mut reader)?)
162 } else if property == PriceFeedProperty::BestAskPrice as u8 {
163 PayloadPropertyValue::BestAskPrice(read_option_price::<BO>(&mut reader)?)
164 } else if property == PriceFeedProperty::PublisherCount as u8 {
165 PayloadPropertyValue::PublisherCount(read_option_u16::<BO>(&mut reader)?)
166 } else if property == PriceFeedProperty::Exponent as u8 {
167 PayloadPropertyValue::Exponent(reader.read_i16::<BO>()?)
168 } else if property == PriceFeedProperty::Confidence as u8 {
169 PayloadPropertyValue::Confidence(read_option_price::<BO>(&mut reader)?)
170 } else {
171 bail!("unknown property");
172 };
173 feed.properties.push(value);
174 }
175 feeds.push(feed);
176 }
177 Ok(Self {
178 timestamp_us,
179 channel_id,
180 feeds,
181 })
182 }
183}
184
185fn write_option_price<BO: ByteOrder>(
186 mut writer: impl Write,
187 value: Option<Price>,
188) -> std::io::Result<()> {
189 writer.write_i64::<BO>(value.map_or(0, |v| v.0.get()))
190}
191
192fn read_option_price<BO: ByteOrder>(mut reader: impl Read) -> std::io::Result<Option<Price>> {
193 let value = NonZeroI64::new(reader.read_i64::<BO>()?);
194 Ok(value.map(Price))
195}
196
197fn write_option_u16<BO: ByteOrder>(
198 mut writer: impl Write,
199 value: Option<u16>,
200) -> std::io::Result<()> {
201 writer.write_u16::<BO>(value.unwrap_or(0))
202}
203
204fn read_option_u16<BO: ByteOrder>(mut reader: impl Read) -> std::io::Result<Option<u16>> {
205 let value = reader.read_u16::<BO>()?;
206 Ok(Some(value))
207}
208
209pub const BINARY_UPDATE_FORMAT_MAGIC: u32 = 1937213467;
210
211pub const PARSED_FORMAT_MAGIC: u32 = 2584795844;
212pub const EVM_FORMAT_MAGIC: u32 = 706910618;
213pub const SOLANA_FORMAT_MAGIC_BE: u32 = 3103857282;
214pub const SOLANA_FORMAT_MAGIC_LE: u32 = u32::swap_bytes(SOLANA_FORMAT_MAGIC_BE);