1#[cfg(feature = "solana-program")]
2use anchor_lang::{
3 AnchorDeserialize,
4 AnchorSerialize,
5};
6#[cfg(not(feature = "solana-program"))]
7use borsh::{
8 BorshDeserialize,
9 BorshSerialize,
10};
11#[cfg(feature = "quickcheck")]
12use quickcheck::Arbitrary;
13use {
14 borsh::BorshSchema,
15 serde::{
16 Deserialize,
17 Serialize,
18 },
19};
20
21#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
34#[cfg_attr(
35 feature = "strum",
36 derive(strum::EnumDiscriminants),
37 strum_discriminants(name(MessageType)),
38 strum_discriminants(vis(pub)),
39 strum_discriminants(derive(
40 Hash,
41 strum::EnumIter,
42 strum::EnumString,
43 strum::IntoStaticStr,
44 strum::Display,
45 Serialize,
46 Deserialize
47 ))
48)]
49
50pub enum Message {
51 PriceFeedMessage(PriceFeedMessage),
52 TwapMessage(TwapMessage),
53}
54
55impl Message {
56 pub fn publish_time(&self) -> i64 {
57 match self {
58 Self::PriceFeedMessage(msg) => msg.publish_time,
59 Self::TwapMessage(msg) => msg.publish_time,
60 }
61 }
62
63 pub fn feed_id(&self) -> FeedId {
64 match self {
65 Self::PriceFeedMessage(msg) => msg.feed_id,
66 Self::TwapMessage(msg) => msg.feed_id,
67 }
68 }
69}
70
71#[cfg(feature = "quickcheck")]
72impl Arbitrary for Message {
73 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
74 match u8::arbitrary(g) % 2 {
75 0 => Message::PriceFeedMessage(Arbitrary::arbitrary(g)),
76 _ => Message::TwapMessage(Arbitrary::arbitrary(g)),
77 }
78 }
79}
80
81pub type FeedId = [u8; 32];
83
84#[repr(C)]
85#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, BorshSchema)]
86#[cfg_attr(feature = "solana-program", derive(AnchorSerialize, AnchorDeserialize))]
87#[cfg_attr(
88 not(feature = "solana-program"),
89 derive(BorshSerialize, BorshDeserialize)
90)]
91
92pub struct PriceFeedMessage {
93 pub feed_id: FeedId,
94 pub price: i64,
95 pub conf: u64,
96 pub exponent: i32,
97 pub publish_time: i64,
99 pub prev_publish_time: i64,
112 pub ema_price: i64,
113 pub ema_conf: u64,
114}
115
116#[cfg(feature = "quickcheck")]
117impl Arbitrary for PriceFeedMessage {
118 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
119 let mut id = [0u8; 32];
120 for item in &mut id {
121 *item = u8::arbitrary(g);
122 }
123
124 let publish_time = i64::arbitrary(g);
125
126 PriceFeedMessage {
127 id,
128 price: i64::arbitrary(g),
129 conf: u64::arbitrary(g),
130 exponent: i32::arbitrary(g),
131 publish_time,
132 prev_publish_time: publish_time.saturating_sub(i64::arbitrary(g)),
133 ema_price: i64::arbitrary(g),
134 ema_conf: u64::arbitrary(g),
135 }
136 }
137}
138
139#[repr(C)]
141#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
142pub struct TwapMessage {
143 pub feed_id: FeedId,
144 pub cumulative_price: i128,
145 pub cumulative_conf: u128,
146 pub num_down_slots: u64,
147 pub exponent: i32,
148 pub publish_time: i64,
149 pub prev_publish_time: i64,
150 pub publish_slot: u64,
151}
152
153#[cfg(feature = "quickcheck")]
154impl Arbitrary for TwapMessage {
155 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
156 let mut id = [0u8; 32];
157 for item in &mut id {
158 *item = u8::arbitrary(g);
159 }
160
161 let publish_time = i64::arbitrary(g);
162
163 TwapMessage {
164 id,
165 cumulative_price: i128::arbitrary(g),
166 cumulative_conf: u128::arbitrary(g),
167 num_down_slots: u64::arbitrary(g),
168 exponent: i32::arbitrary(g),
169 publish_time,
170 prev_publish_time: publish_time.saturating_sub(i64::arbitrary(g)),
171 publish_slot: u64::arbitrary(g),
172 }
173 }
174}
175
176#[cfg(test)]
177mod tests {
178
179 use crate::{
180 messages::{
181 Message,
182 PriceFeedMessage,
183 },
184 wire::Serializer,
185 };
186
187 #[test]
189 fn test_forward_compatibility() {
190 use {
191 serde::Serialize,
192 std::iter,
193 };
194 let msg = Message::PriceFeedMessage(PriceFeedMessage {
195 feed_id: [1u8; 32],
196 price: 1,
197 conf: 1,
198 exponent: 1,
199 publish_time: 1,
200 prev_publish_time: 1,
201 ema_price: 1,
202 ema_conf: 1,
203 });
204 let mut buffer = Vec::new();
205 let mut cursor = std::io::Cursor::new(&mut buffer);
206 let mut serializer: Serializer<_, byteorder::LE> = Serializer::new(&mut cursor);
207 msg.serialize(&mut serializer).unwrap();
208 buffer.extend(iter::repeat(0).take(10));
209 let deserialized = crate::wire::from_slice::<byteorder::LE, Message>(&buffer).unwrap();
210 assert_eq!(deserialized, msg);
211 }
212}