|
17 | 17 | Pod,
|
18 | 18 | Zeroable,
|
19 | 19 | },
|
| 20 | + byteorder::{ |
| 21 | + BigEndian, |
| 22 | + ReadBytesExt, |
| 23 | + }, |
20 | 24 | solana_program::pubkey::Pubkey,
|
21 |
| - std::mem::size_of, |
| 25 | + std::{ |
| 26 | + io::{ |
| 27 | + Cursor, |
| 28 | + Read, |
| 29 | + }, |
| 30 | + mem::size_of, |
| 31 | + }, |
22 | 32 | };
|
23 | 33 |
|
24 | 34 | #[repr(C)]
|
@@ -213,6 +223,39 @@ impl PythAccount for PriceAccountV2 {
|
213 | 223 | /// Messages are forward-compatible. You may add new fields to messages after all previously
|
214 | 224 | /// defined fields. All code for parsing messages must ignore any extraneous bytes at the end of
|
215 | 225 | /// the message (which could be fields that the code does not yet understand).
|
| 226 | +/// |
| 227 | +/// The oracle is not using the Message enum due to the contract size limit and |
| 228 | +/// some of the methods for PriceFeedMessage and TwapMessage are not used by the oracle |
| 229 | +/// for the same reason. Rust compiler doesn't include the unused methods in the contract. |
| 230 | +/// Once we start using the unused structs and methods, the contract size will increase. |
| 231 | +
|
| 232 | +#[derive(Debug, Copy, Clone, PartialEq)] |
| 233 | +pub enum Message { |
| 234 | + PriceFeedMessage(PriceFeedMessage), |
| 235 | + TwapMessage(TwapMessage), |
| 236 | +} |
| 237 | + |
| 238 | +#[allow(dead_code)] |
| 239 | +impl Message { |
| 240 | + pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, OracleError> { |
| 241 | + match bytes[0] { |
| 242 | + PriceFeedMessage::DISCRIMINATOR => Ok(Self::PriceFeedMessage( |
| 243 | + PriceFeedMessage::try_from_bytes(bytes)?, |
| 244 | + )), |
| 245 | + TwapMessage::DISCRIMINATOR => { |
| 246 | + Ok(Self::TwapMessage(TwapMessage::try_from_bytes(bytes)?)) |
| 247 | + } |
| 248 | + _ => Err(OracleError::DeserializationError), |
| 249 | + } |
| 250 | + } |
| 251 | + pub fn to_bytes(self) -> Vec<u8> { |
| 252 | + match self { |
| 253 | + Self::PriceFeedMessage(msg) => msg.to_bytes().to_vec(), |
| 254 | + Self::TwapMessage(msg) => msg.to_bytes().to_vec(), |
| 255 | + } |
| 256 | + } |
| 257 | +} |
| 258 | + |
216 | 259 | #[repr(C)]
|
217 | 260 | #[derive(Debug, Copy, Clone, PartialEq)]
|
218 | 261 | pub struct PriceFeedMessage {
|
@@ -273,7 +316,7 @@ impl PriceFeedMessage {
|
273 | 316 | /// Note that it would be more idiomatic to return a `Vec`, but that approach adds
|
274 | 317 | /// to the size of the compiled binary (which is already close to the size limit).
|
275 | 318 | #[allow(unused_assignments)]
|
276 |
| - pub fn as_bytes(&self) -> [u8; Self::MESSAGE_SIZE] { |
| 319 | + pub fn to_bytes(self) -> [u8; Self::MESSAGE_SIZE] { |
277 | 320 | let mut bytes = [0u8; Self::MESSAGE_SIZE];
|
278 | 321 |
|
279 | 322 | let mut i: usize = 0;
|
@@ -307,6 +350,60 @@ impl PriceFeedMessage {
|
307 | 350 |
|
308 | 351 | bytes
|
309 | 352 | }
|
| 353 | + |
| 354 | + /// Try to deserialize a message from an array of bytes (including the discriminator). |
| 355 | + /// This method is forward-compatible and allows the size to be larger than the |
| 356 | + /// size of the struct. As a side-effect, it will ignore newer fields that are |
| 357 | + /// not yet present in the struct. |
| 358 | + pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, OracleError> { |
| 359 | + let mut cursor = Cursor::new(bytes); |
| 360 | + |
| 361 | + let discriminator = cursor |
| 362 | + .read_u8() |
| 363 | + .map_err(|_| OracleError::DeserializationError)?; |
| 364 | + if discriminator != 0 { |
| 365 | + return Err(OracleError::DeserializationError); |
| 366 | + } |
| 367 | + |
| 368 | + let mut id = [0u8; 32]; |
| 369 | + cursor |
| 370 | + .read_exact(&mut id) |
| 371 | + .map_err(|_| OracleError::DeserializationError)?; |
| 372 | + |
| 373 | + let price = cursor |
| 374 | + .read_i64::<BigEndian>() |
| 375 | + .map_err(|_| OracleError::DeserializationError)?; |
| 376 | + let conf = cursor |
| 377 | + .read_u64::<BigEndian>() |
| 378 | + .map_err(|_| OracleError::DeserializationError)?; |
| 379 | + let exponent = cursor |
| 380 | + .read_i32::<BigEndian>() |
| 381 | + .map_err(|_| OracleError::DeserializationError)?; |
| 382 | + let publish_time = cursor |
| 383 | + .read_i64::<BigEndian>() |
| 384 | + .map_err(|_| OracleError::DeserializationError)?; |
| 385 | + let prev_publish_time = cursor |
| 386 | + .read_i64::<BigEndian>() |
| 387 | + .map_err(|_| OracleError::DeserializationError)?; |
| 388 | + let ema_price = cursor |
| 389 | + .read_i64::<BigEndian>() |
| 390 | + .map_err(|_| OracleError::DeserializationError)?; |
| 391 | + let ema_conf = cursor |
| 392 | + .read_u64::<BigEndian>() |
| 393 | + .map_err(|_| OracleError::DeserializationError)?; |
| 394 | + |
| 395 | + |
| 396 | + Ok(Self { |
| 397 | + id, |
| 398 | + price, |
| 399 | + conf, |
| 400 | + exponent, |
| 401 | + publish_time, |
| 402 | + prev_publish_time, |
| 403 | + ema_price, |
| 404 | + ema_conf, |
| 405 | + }) |
| 406 | + } |
310 | 407 | }
|
311 | 408 |
|
312 | 409 | /// Message format for sending Twap data via the accumulator program
|
@@ -353,7 +450,7 @@ impl TwapMessage {
|
353 | 450 | /// Note that it would be more idiomatic to return a `Vec`, but that approach adds
|
354 | 451 | /// to the size of the compiled binary (which is already close to the size limit).
|
355 | 452 | #[allow(unused_assignments)]
|
356 |
| - pub fn as_bytes(&self) -> [u8; Self::MESSAGE_SIZE] { |
| 453 | + pub fn to_bytes(self) -> [u8; Self::MESSAGE_SIZE] { |
357 | 454 | let mut bytes = [0u8; Self::MESSAGE_SIZE];
|
358 | 455 |
|
359 | 456 | let mut i: usize = 0;
|
@@ -387,4 +484,57 @@ impl TwapMessage {
|
387 | 484 |
|
388 | 485 | bytes
|
389 | 486 | }
|
| 487 | + |
| 488 | + /// Try to deserialize a message from an array of bytes (including the discriminator). |
| 489 | + /// This method is forward-compatible and allows the size to be larger than the |
| 490 | + /// size of the struct. As a side-effect, it will ignore newer fields that are |
| 491 | + /// not yet present in the struct. |
| 492 | + pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, OracleError> { |
| 493 | + let mut cursor = Cursor::new(bytes); |
| 494 | + |
| 495 | + let discriminator = cursor |
| 496 | + .read_u8() |
| 497 | + .map_err(|_| OracleError::DeserializationError)?; |
| 498 | + if discriminator != 1 { |
| 499 | + return Err(OracleError::DeserializationError); |
| 500 | + } |
| 501 | + |
| 502 | + let mut id = [0u8; 32]; |
| 503 | + cursor |
| 504 | + .read_exact(&mut id) |
| 505 | + .map_err(|_| OracleError::DeserializationError)?; |
| 506 | + |
| 507 | + let cumulative_price = cursor |
| 508 | + .read_i128::<BigEndian>() |
| 509 | + .map_err(|_| OracleError::DeserializationError)?; |
| 510 | + let cumulative_conf = cursor |
| 511 | + .read_u128::<BigEndian>() |
| 512 | + .map_err(|_| OracleError::DeserializationError)?; |
| 513 | + let num_down_slots = cursor |
| 514 | + .read_u64::<BigEndian>() |
| 515 | + .map_err(|_| OracleError::DeserializationError)?; |
| 516 | + let exponent = cursor |
| 517 | + .read_i32::<BigEndian>() |
| 518 | + .map_err(|_| OracleError::DeserializationError)?; |
| 519 | + let publish_time = cursor |
| 520 | + .read_i64::<BigEndian>() |
| 521 | + .map_err(|_| OracleError::DeserializationError)?; |
| 522 | + let prev_publish_time = cursor |
| 523 | + .read_i64::<BigEndian>() |
| 524 | + .map_err(|_| OracleError::DeserializationError)?; |
| 525 | + let publish_slot = cursor |
| 526 | + .read_u64::<BigEndian>() |
| 527 | + .map_err(|_| OracleError::DeserializationError)?; |
| 528 | + |
| 529 | + Ok(Self { |
| 530 | + id, |
| 531 | + cumulative_price, |
| 532 | + cumulative_conf, |
| 533 | + num_down_slots, |
| 534 | + exponent, |
| 535 | + publish_time, |
| 536 | + prev_publish_time, |
| 537 | + publish_slot, |
| 538 | + }) |
| 539 | + } |
390 | 540 | }
|
0 commit comments