gmsol_sdk/utils/
token_map.rs1use std::{
2 collections::HashMap,
3 fmt,
4 iter::{Peekable, Zip},
5 slice::Iter,
6 sync::Arc,
7};
8
9use bytes::Bytes;
10use gmsol_programs::{
11 anchor_lang::{self, AccountDeserialize},
12 gmsol_store::accounts::TokenMapHeader,
13};
14use gmsol_utils::{
15 dynamic_access,
16 token_config::{TokenConfig, TokenMapAccess},
17};
18use solana_sdk::pubkey::Pubkey;
19
20use gmsol_utils::{oracle::PriceProviderKind, token_config::TokensWithFeed};
21use solana_sdk::instruction::AccountMeta;
22
23use crate::utils::zero_copy::{check_discriminator, try_deserialize_unchecked};
24
25#[derive(Debug, Clone)]
27pub struct TokenMap {
28 header: Arc<TokenMapHeader>,
29 configs: Bytes,
30}
31
32impl TokenMapAccess for TokenMap {
33 fn get(&self, token: &Pubkey) -> Option<&TokenConfig> {
34 let index = usize::from(*self.header.tokens.get(token)?);
35 dynamic_access::get(&self.configs, index)
36 }
37}
38
39impl TokenMap {
40 pub fn header(&self) -> &TokenMapHeader {
42 &self.header
43 }
44
45 pub fn is_empty(&self) -> bool {
47 self.header.tokens.is_empty()
48 }
49
50 pub fn len(&self) -> usize {
52 self.header.tokens.len()
53 }
54
55 pub fn tokens(&self) -> impl Iterator<Item = Pubkey> + '_ {
57 self.header
58 .tokens
59 .entries()
60 .map(|(k, _)| Pubkey::new_from_array(*k))
61 }
62
63 pub fn iter(&self) -> impl Iterator<Item = (Pubkey, &TokenConfig)> + '_ {
65 self.tokens()
66 .filter_map(|token| self.get(&token).map(|config| (token, config)))
67 }
68}
69
70impl AccountDeserialize for TokenMap {
71 fn try_deserialize(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
72 check_discriminator::<TokenMapHeader>(buf)?;
73 Self::try_deserialize_unchecked(buf)
74 }
75
76 fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
77 let header = Arc::new(try_deserialize_unchecked::<TokenMapHeader>(buf)?);
78 let (_disc, data) = buf.split_at(8);
79 let (_header, configs) = data.split_at(std::mem::size_of::<TokenMapHeader>());
80 Ok(Self {
81 header,
82 configs: Bytes::copy_from_slice(configs),
83 })
84 }
85}
86
87type Parser = Arc<dyn Fn(Pubkey) -> crate::Result<AccountMeta>>;
88
89pub type FeedAddressMap = std::collections::HashMap<Pubkey, Pubkey>;
91
92#[derive(Default, Clone)]
94pub struct FeedsParser {
95 parsers: HashMap<PriceProviderKind, Parser>,
96}
97
98impl fmt::Debug for FeedsParser {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 f.debug_struct("FeedsParser").finish_non_exhaustive()
101 }
102}
103
104impl FeedsParser {
105 pub fn parse<'a>(
107 &'a self,
108 tokens_with_feed: &'a TokensWithFeed,
109 ) -> impl Iterator<Item = crate::Result<AccountMeta>> + 'a {
110 Feeds::new(tokens_with_feed).map(|res| {
111 res.and_then(|FeedConfig { provider, feed, .. }| self.dispatch(&provider, &feed))
112 })
113 }
114
115 pub fn parse_and_sort_by_tokens(
117 &self,
118 tokens_with_feed: &TokensWithFeed,
119 ) -> crate::Result<Vec<AccountMeta>> {
120 let accounts = self
121 .parse(tokens_with_feed)
122 .collect::<crate::Result<Vec<_>>>()?;
123
124 let mut combined = tokens_with_feed
125 .tokens
126 .iter()
127 .zip(accounts)
128 .collect::<Vec<_>>();
129
130 combined.sort_by_key(|(key, _)| *key);
131
132 Ok(combined.into_iter().map(|(_, account)| account).collect())
133 }
134
135 fn dispatch(&self, provider: &PriceProviderKind, feed: &Pubkey) -> crate::Result<AccountMeta> {
136 let Some(parser) = self.parsers.get(provider) else {
137 return Ok(AccountMeta {
138 pubkey: *feed,
139 is_signer: false,
140 is_writable: false,
141 });
142 };
143 (parser)(*feed)
144 }
145
146 pub fn insert_pull_oracle_feed_parser(
148 &mut self,
149 provider: PriceProviderKind,
150 map: FeedAddressMap,
151 ) -> &mut Self {
152 self.parsers.insert(
153 provider,
154 Arc::new(move |feed_id| {
155 let price_update = map.get(&feed_id).ok_or_else(|| {
156 crate::Error::custom(format!("feed account for {feed_id} not provided"))
157 })?;
158
159 Ok(AccountMeta {
160 pubkey: *price_update,
161 is_signer: false,
162 is_writable: false,
163 })
164 }),
165 );
166 self
167 }
168}
169
170pub struct Feeds<'a> {
172 provider_with_lengths: Peekable<Zip<Iter<'a, u8>, Iter<'a, u16>>>,
173 tokens: Iter<'a, Pubkey>,
174 feeds: Iter<'a, Pubkey>,
175 current: usize,
176 failed: bool,
177}
178
179impl<'a> Feeds<'a> {
180 pub fn new(token_with_feeds: &'a TokensWithFeed) -> Self {
182 let providers = token_with_feeds.providers.iter();
183 let nums = token_with_feeds.nums.iter();
184 let provider_with_lengths = providers.zip(nums).peekable();
185 let tokens = token_with_feeds.tokens.iter();
186 let feeds = token_with_feeds.feeds.iter();
187 Self {
188 provider_with_lengths,
189 tokens,
190 feeds,
191 current: 0,
192 failed: false,
193 }
194 }
195}
196
197impl Iterator for Feeds<'_> {
198 type Item = crate::Result<FeedConfig>;
199
200 fn next(&mut self) -> Option<Self::Item> {
201 if self.failed {
202 return None;
203 }
204 loop {
205 let (provider, length) = self.provider_with_lengths.peek()?;
206 if self.current == (**length as usize) {
207 self.provider_with_lengths.next();
208 self.current = 0;
209 continue;
210 }
211 let Ok(provider) = PriceProviderKind::try_from(**provider) else {
212 self.failed = true;
213 return Some(Err(crate::Error::custom("invalid provider index")));
214 };
215 let Some(feed) = self.feeds.next() else {
216 return Some(Err(crate::Error::custom("not enough feeds")));
217 };
218 let Some(token) = self.tokens.next() else {
219 return Some(Err(crate::Error::custom("not enough tokens")));
220 };
221 self.current += 1;
222 return Some(Ok(FeedConfig {
223 token: *token,
224 provider,
225 feed: *feed,
226 }));
227 }
228 }
229}
230
231#[derive(Debug, Clone)]
233pub struct FeedConfig {
234 pub token: Pubkey,
236 pub provider: PriceProviderKind,
238 pub feed: Pubkey,
240}