1use borsh::{
2 BorshDeserialize,
3 BorshSerialize,
4};
5
6use hex::FromHexError;
7use schemars::JsonSchema;
8use std::fmt;
9
10pub mod utils;
11
12mod price;
13pub use price::Price;
14
15#[derive(
16 Copy,
17 Clone,
18 Default,
19 PartialEq,
20 Eq,
21 PartialOrd,
22 Ord,
23 Hash,
24 BorshSerialize,
25 BorshDeserialize,
26 serde::Serialize,
27 serde::Deserialize,
28 JsonSchema,
29)]
30#[repr(C)]
31pub struct Identifier(
32 #[serde(with = "hex")]
33 #[schemars(with = "String")]
34 [u8; 32],
35);
36
37impl Identifier {
38 pub fn new(bytes: [u8; 32]) -> Identifier {
39 Identifier(bytes)
40 }
41
42 pub fn to_bytes(&self) -> [u8; 32] {
43 self.0
44 }
45
46 pub fn to_hex(&self) -> String {
47 hex::encode(self.0)
48 }
49
50 pub fn from_hex<T: AsRef<[u8]>>(s: T) -> Result<Identifier, FromHexError> {
51 let mut bytes = [0u8; 32];
52 hex::decode_to_slice(s, &mut bytes)?;
53 Ok(Identifier::new(bytes))
54 }
55}
56
57impl fmt::Debug for Identifier {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 write!(f, "0x{}", self.to_hex())
60 }
61}
62
63impl fmt::Display for Identifier {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 write!(f, "0x{}", self.to_hex())
66 }
67}
68
69impl AsRef<[u8]> for Identifier {
70 fn as_ref(&self) -> &[u8] {
71 &self.0[..]
72 }
73}
74
75pub type PriceIdentifier = Identifier;
78
79pub type ProductIdentifier = Identifier;
82
83pub type UnixTimestamp = i64;
87pub type DurationInSeconds = u64;
88
89#[derive(
91 Copy,
92 Clone,
93 Debug,
94 Default,
95 PartialEq,
96 Eq,
97 BorshSerialize,
98 BorshDeserialize,
99 serde::Serialize,
100 serde::Deserialize,
101 JsonSchema,
102)]
103#[repr(C)]
104pub struct PriceFeed {
105 pub id: PriceIdentifier,
107 price: Price,
109 ema_price: Price,
111}
112
113impl PriceFeed {
114 #[allow(clippy::too_many_arguments)]
116 pub fn new(id: PriceIdentifier, price: Price, ema_price: Price) -> PriceFeed {
117 PriceFeed {
118 id,
119 price,
120 ema_price,
121 }
122 }
123
124
125 pub fn get_price_unchecked(&self) -> Price {
135 self.price
136 }
137
138
139 pub fn get_ema_price_unchecked(&self) -> Price {
150 self.ema_price
151 }
152
153 pub fn get_price_no_older_than(
164 &self,
165 current_time: UnixTimestamp,
166 age: DurationInSeconds,
167 ) -> Option<Price> {
168 let price = self.get_price_unchecked();
169
170 let time_diff_abs = (price.publish_time - current_time).abs() as u64;
171
172 if time_diff_abs > age {
173 return None;
174 }
175
176 Some(price)
177 }
178
179 pub fn get_ema_price_no_older_than(
190 &self,
191 current_time: UnixTimestamp,
192 age: DurationInSeconds,
193 ) -> Option<Price> {
194 let price = self.get_ema_price_unchecked();
195
196 let time_diff_abs = (price.publish_time - current_time).abs() as u64;
197
198 if time_diff_abs > age {
199 return None;
200 }
201
202 Some(price)
203 }
204}
205#[cfg(test)]
206mod test {
207 use super::*;
208
209 #[test]
210 pub fn test_ser_then_deser_default() {
211 let price_feed = PriceFeed::default();
212 let ser = serde_json::to_string(&price_feed).unwrap();
213 let deser: PriceFeed = serde_json::from_str(&ser).unwrap();
214 assert_eq!(price_feed, deser);
215 }
216
217 #[test]
218 pub fn test_ser_large_number() {
219 let price_feed = PriceFeed {
220 ema_price: Price {
221 conf: 1_234_567_000_000_000_789,
222 ..Price::default()
223 },
224 ..PriceFeed::default()
225 };
226 let price_feed_json = serde_json::to_value(price_feed).unwrap();
227 assert_eq!(
228 price_feed_json["ema_price"]["conf"].as_str(),
229 Some("1234567000000000789")
230 );
231 }
232
233 #[test]
234 pub fn test_deser_large_number() {
235 let mut price_feed_json = serde_json::to_value(PriceFeed::default()).unwrap();
236 price_feed_json["price"]["price"] =
237 serde_json::Value::String(String::from("1000000000000000123"));
238 let p: PriceFeed = serde_json::from_value(price_feed_json).unwrap();
239 assert_eq!(p.get_price_unchecked().price, 1_000_000_000_000_000_123);
240 }
241
242 #[test]
243 pub fn test_ser_id_length_32_bytes() {
244 let mut price_feed = PriceFeed::default();
245 price_feed.id.0[0] = 106; let price_feed_json = serde_json::to_value(price_feed).unwrap();
247 let id_str = price_feed_json["id"].as_str().unwrap();
248 assert_eq!(id_str.len(), 64);
249 assert_eq!(
250 id_str,
251 "6a00000000000000000000000000000000000000000000000000000000000000"
252 );
253 }
254
255 #[test]
256 pub fn test_deser_invalid_id_length_fails() {
257 let mut price_feed_json = serde_json::to_value(PriceFeed::default()).unwrap();
258 price_feed_json["id"] = serde_json::Value::String(String::from("1234567890"));
259 assert!(serde_json::from_value::<PriceFeed>(price_feed_json).is_err());
260 }
261
262 #[test]
263 pub fn test_identifier_from_hex_ok() {
264 let id = Identifier::from_hex(
265 "0a3f000000000000000000000000000000000000000000000000000000000000",
266 )
267 .unwrap();
268 assert_eq!(id.to_bytes()[0], 10);
269 }
270
271 #[test]
272 pub fn test_identifier_from_hex_invalid_err() {
273 let try_parse_odd = Identifier::from_hex("010"); assert_eq!(try_parse_odd, Err(FromHexError::OddLength));
275
276 let try_parse_invalid_len = Identifier::from_hex("0a"); assert_eq!(
278 try_parse_invalid_len,
279 Err(FromHexError::InvalidStringLength)
280 );
281 }
282
283 #[test]
284 pub fn test_identifier_debug_fmt() {
285 let mut id = Identifier::default();
286 id.0[0] = 10;
287
288 let id_str = format!("{:?}", id);
289 assert_eq!(
290 id_str,
291 "0x0a00000000000000000000000000000000000000000000000000000000000000"
292 );
293 }
294
295 #[test]
296 pub fn test_identifier_display_fmt() {
297 let mut id = Identifier::default();
298 id.0[0] = 10;
299
300 let id_str = format!("{}", id);
301 assert_eq!(
302 id_str,
303 "0x0a00000000000000000000000000000000000000000000000000000000000000"
304 );
305 }
306}