gmsol/pyth/pull_oracle/
utils.rs1use gmsol_store::states::{common::TokensWithFeed, PriceProviderKind};
2use pyth_sdk::Identifier;
3use pythnet_sdk::{
4 messages::PriceFeedMessage,
5 wire::{
6 from_slice,
7 v1::{AccumulatorUpdateData, MerklePriceUpdate, Proof},
8 },
9};
10
11use crate::store::utils::Feeds;
12
13use super::hermes::{BinaryPriceUpdate, EncodingType};
14
15pub fn parse_accumulator_update_datas(
17 update: &BinaryPriceUpdate,
18) -> crate::Result<Vec<AccumulatorUpdateData>> {
19 let datas = match update.encoding {
20 EncodingType::Base64 => {
21 use base64::{engine::general_purpose::STANDARD, Engine};
22
23 update
24 .data
25 .iter()
26 .map(|data| {
27 STANDARD
28 .decode(data)
29 .map_err(crate::Error::from)
30 .and_then(|data| parse_accumulator_update_data(&data))
31 })
32 .collect::<crate::Result<Vec<_>>>()?
33 }
34 EncodingType::Hex => {
35 unimplemented!()
36 }
37 };
38 Ok(datas)
39}
40
41#[inline]
42fn parse_accumulator_update_data(data: &[u8]) -> crate::Result<AccumulatorUpdateData> {
43 AccumulatorUpdateData::try_from_slice(data).map_err(crate::Error::unknown)
44}
45
46pub fn get_guardian_set_index(proof: &Proof) -> crate::Result<i32> {
48 let vaa = get_vaa_buffer(proof);
49 if vaa.len() < 5 {
50 return Err(crate::Error::unknown("invalid vaa"));
51 }
52 let index: &[u8; 4] = (&vaa[1..5]).try_into().map_err(crate::Error::unknown)?;
53 Ok(i32::from_be_bytes(*index))
54}
55
56pub fn get_vaa_buffer(proof: &Proof) -> &[u8] {
58 match proof {
59 Proof::WormholeMerkle { vaa, .. } => vaa.as_ref(),
60 }
61}
62
63pub fn get_merkle_price_updates(proof: &Proof) -> &[MerklePriceUpdate] {
65 match proof {
66 Proof::WormholeMerkle { updates, .. } => updates,
67 }
68}
69
70pub const PRICE_FEED_MESSAGE_VARIANT: u8 = 0;
72
73pub fn parse_price_feed_message(update: &MerklePriceUpdate) -> crate::Result<PriceFeedMessage> {
75 const PRICE_FEED_MESSAGE_VARIANT: u8 = 0;
76 let data = update.message.as_ref().as_slice();
77 if data.is_empty() {
78 return Err(crate::Error::invalid_argument("empty message"));
79 }
80 if data[0] != PRICE_FEED_MESSAGE_VARIANT {
81 return Err(crate::Error::invalid_argument(
82 "it is not a price feed message",
83 ));
84 }
85 from_slice::<byteorder::BE, _>(&data[1..]).map_err(|err| {
86 crate::Error::invalid_argument(format!("deserialize price feed message error: {err}"))
87 })
88}
89
90pub fn parse_feed_id(update: &MerklePriceUpdate) -> crate::Result<Identifier> {
92 let feed_id = parse_price_feed_message(update)?.feed_id;
93 Ok(Identifier::new(feed_id))
94}
95
96pub fn extract_pyth_feed_ids(feeds: &TokensWithFeed) -> crate::Result<Vec<Identifier>> {
98 Feeds::new(feeds)
99 .filter_map(|res| {
100 res.map(|config| {
101 if matches!(config.provider, PriceProviderKind::Pyth) {
102 Some(Identifier::new(config.feed.to_bytes()))
103 } else {
104 None
105 }
106 })
107 .transpose()
108 })
109 .collect()
110}