gmsol_store/instructions/
token_config.rs1use anchor_lang::prelude::*;
2use anchor_spl::token::Mint;
3
4use crate::{
5 states::{
6 FeedConfig, PriceProviderKind, Store, TokenConfigExt, TokenMapAccess, TokenMapAccessMut,
7 TokenMapHeader, TokenMapLoader, UpdateTokenConfigParams,
8 },
9 utils::internal,
10 CoreError,
11};
12
13#[derive(Accounts)]
17pub struct InitializeTokenMap<'info> {
18 #[account(mut)]
20 pub payer: Signer<'info>,
21 pub store: AccountLoader<'info, Store>,
23 #[account(
25 init,
26 payer = payer,
27 space = 8 + TokenMapHeader::space(0),
28 )]
29 pub token_map: AccountLoader<'info, TokenMapHeader>,
30 pub system_program: Program<'info, System>,
32}
33
34pub(crate) fn initialize_token_map(ctx: Context<InitializeTokenMap>) -> Result<()> {
36 ctx.accounts.token_map.load_init()?.store = ctx.accounts.store.key();
37 Ok(())
38}
39
40#[derive(Accounts)]
42pub struct PushToTokenMap<'info> {
43 #[account(mut)]
45 pub authority: Signer<'info>,
46 pub store: AccountLoader<'info, Store>,
48 #[account(
50 mut,
51 has_one = store,
52 )]
53 pub token_map: AccountLoader<'info, TokenMapHeader>,
54 pub token: Account<'info, Mint>,
56 pub system_program: Program<'info, System>,
58}
59
60pub(crate) fn unchecked_push_to_token_map(
65 ctx: Context<PushToTokenMap>,
66 name: &str,
67 builder: UpdateTokenConfigParams,
68 enable: bool,
69 new: bool,
70) -> Result<()> {
71 let token = ctx.accounts.token.key();
72 let token_decimals = ctx.accounts.token.decimals;
73 do_push_token_map(
74 ctx.accounts.authority.to_account_info(),
75 &ctx.accounts.token_map,
76 ctx.accounts.system_program.to_account_info(),
77 false,
78 name,
79 &token,
80 token_decimals,
81 builder,
82 enable,
83 new,
84 )
85}
86
87impl<'info> internal::Authentication<'info> for PushToTokenMap<'info> {
88 fn authority(&self) -> &Signer<'info> {
89 &self.authority
90 }
91
92 fn store(&self) -> &AccountLoader<'info, Store> {
93 &self.store
94 }
95}
96
97#[derive(Accounts)]
102pub struct PushToTokenMapSynthetic<'info> {
103 #[account(mut)]
105 pub authority: Signer<'info>,
106 pub store: AccountLoader<'info, Store>,
108 #[account(mut, has_one = store)]
110 pub token_map: AccountLoader<'info, TokenMapHeader>,
111 pub system_program: Program<'info, System>,
113}
114
115pub(crate) fn unchecked_push_to_token_map_synthetic(
120 ctx: Context<PushToTokenMapSynthetic>,
121 name: &str,
122 token: Pubkey,
123 token_decimals: u8,
124 builder: UpdateTokenConfigParams,
125 enable: bool,
126 new: bool,
127) -> Result<()> {
128 do_push_token_map(
129 ctx.accounts.authority.to_account_info(),
130 &ctx.accounts.token_map,
131 ctx.accounts.system_program.to_account_info(),
132 true,
133 name,
134 &token,
135 token_decimals,
136 builder,
137 enable,
138 new,
139 )
140}
141
142impl<'info> internal::Authentication<'info> for PushToTokenMapSynthetic<'info> {
143 fn authority(&self) -> &Signer<'info> {
144 &self.authority
145 }
146
147 fn store(&self) -> &AccountLoader<'info, Store> {
148 &self.store
149 }
150}
151
152#[derive(Accounts)]
156pub struct ToggleTokenConfig<'info> {
157 pub authority: Signer<'info>,
159 pub store: AccountLoader<'info, Store>,
161 #[account(
163 mut,
164 has_one = store,
165 )]
166 pub token_map: AccountLoader<'info, TokenMapHeader>,
167}
168
169pub(crate) fn unchecked_toggle_token_config(
174 ctx: Context<ToggleTokenConfig>,
175 token: Pubkey,
176 enable: bool,
177) -> Result<()> {
178 ctx.accounts
179 .token_map
180 .load_token_map_mut()?
181 .get_mut(&token)
182 .ok_or_else(|| error!(CoreError::NotFound))?
183 .set_enabled(enable);
184 Ok(())
185}
186
187impl<'info> internal::Authentication<'info> for ToggleTokenConfig<'info> {
188 fn authority(&self) -> &Signer<'info> {
189 &self.authority
190 }
191
192 fn store(&self) -> &AccountLoader<'info, Store> {
193 &self.store
194 }
195}
196
197#[derive(Accounts)]
201pub struct SetExpectedProvider<'info> {
202 pub authority: Signer<'info>,
204 pub store: AccountLoader<'info, Store>,
206 #[account(mut, has_one = store)]
208 pub token_map: AccountLoader<'info, TokenMapHeader>,
209}
210
211pub(crate) fn unchecked_set_expected_provider(
216 ctx: Context<SetExpectedProvider>,
217 token: Pubkey,
218 provider: PriceProviderKind,
219) -> Result<()> {
220 let mut token_map = ctx.accounts.token_map.load_token_map_mut()?;
221
222 let config = token_map
223 .get_mut(&token)
224 .ok_or_else(|| error!(CoreError::NotFound))?;
225
226 require_neq!(
227 config.expected_provider().map_err(CoreError::from)?,
228 provider,
229 CoreError::PreconditionsAreNotMet
230 );
231
232 config.set_expected_provider(provider);
233 Ok(())
234}
235
236impl<'info> internal::Authentication<'info> for SetExpectedProvider<'info> {
237 fn authority(&self) -> &Signer<'info> {
238 &self.authority
239 }
240
241 fn store(&self) -> &AccountLoader<'info, Store> {
242 &self.store
243 }
244}
245
246#[derive(Accounts)]
250pub struct SetFeedConfig<'info> {
251 pub authority: Signer<'info>,
253 pub store: AccountLoader<'info, Store>,
255 #[account(mut, has_one = store)]
257 pub token_map: AccountLoader<'info, TokenMapHeader>,
258}
259
260pub(crate) fn unchecked_set_feed_config(
265 ctx: Context<SetFeedConfig>,
266 token: Pubkey,
267 provider: &PriceProviderKind,
268 feed: Pubkey,
269 timestamp_adjustment: u32,
270) -> Result<()> {
271 ctx.accounts
272 .token_map
273 .load_token_map_mut()?
274 .get_mut(&token)
275 .ok_or_else(|| error!(CoreError::NotFound))?
276 .set_feed_config(
277 provider,
278 FeedConfig::new(feed).with_timestamp_adjustment(timestamp_adjustment),
279 )
280 .map_err(CoreError::from)
281 .map_err(|err| error!(err))
282}
283
284impl<'info> internal::Authentication<'info> for SetFeedConfig<'info> {
285 fn authority(&self) -> &Signer<'info> {
286 &self.authority
287 }
288
289 fn store(&self) -> &AccountLoader<'info, Store> {
290 &self.store
291 }
292}
293
294#[derive(Accounts)]
296pub struct ReadTokenMap<'info> {
297 pub token_map: AccountLoader<'info, TokenMapHeader>,
299}
300
301pub(crate) fn is_token_config_enabled(ctx: Context<ReadTokenMap>, token: &Pubkey) -> Result<bool> {
303 ctx.accounts
304 .token_map
305 .load_token_map()?
306 .get(token)
307 .map(|config| config.is_enabled())
308 .ok_or_else(|| error!(CoreError::NotFound))
309}
310
311pub(crate) fn token_expected_provider(
313 ctx: Context<ReadTokenMap>,
314 token: &Pubkey,
315) -> Result<PriceProviderKind> {
316 ctx.accounts
317 .token_map
318 .load_token_map()?
319 .get(token)
320 .ok_or_else(|| error!(CoreError::NotFound))?
321 .expected_provider()
322 .map_err(CoreError::from)
323 .map_err(|err| error!(err))
324}
325
326pub(crate) fn token_feed(
328 ctx: Context<ReadTokenMap>,
329 token: &Pubkey,
330 provider: &PriceProviderKind,
331) -> Result<Pubkey> {
332 ctx.accounts
333 .token_map
334 .load_token_map()?
335 .get(token)
336 .ok_or_else(|| error!(CoreError::NotFound))?
337 .get_feed(provider)
338 .map_err(CoreError::from)
339 .map_err(|err| error!(err))
340}
341
342pub(crate) fn token_timestamp_adjustment(
344 ctx: Context<ReadTokenMap>,
345 token: &Pubkey,
346 provider: &PriceProviderKind,
347) -> Result<u32> {
348 ctx.accounts
349 .token_map
350 .load_token_map()?
351 .get(token)
352 .ok_or_else(|| error!(CoreError::NotFound))?
353 .timestamp_adjustment(provider)
354 .map_err(CoreError::from)
355 .map_err(|err| error!(err))
356}
357
358pub(crate) fn token_name(ctx: Context<ReadTokenMap>, token: &Pubkey) -> Result<String> {
360 ctx.accounts
361 .token_map
362 .load_token_map()?
363 .get(token)
364 .ok_or_else(|| error!(CoreError::NotFound))?
365 .name()
366 .map(|s| s.to_owned())
367 .map_err(CoreError::from)
368 .map_err(|err| error!(err))
369}
370
371pub(crate) fn token_decimals(ctx: Context<ReadTokenMap>, token: &Pubkey) -> Result<u8> {
373 Ok(ctx
374 .accounts
375 .token_map
376 .load_token_map()?
377 .get(token)
378 .ok_or_else(|| error!(CoreError::NotFound))?
379 .token_decimals())
380}
381
382pub(crate) fn token_precision(ctx: Context<ReadTokenMap>, token: &Pubkey) -> Result<u8> {
384 Ok(ctx
385 .accounts
386 .token_map
387 .load_token_map()?
388 .get(token)
389 .ok_or_else(|| error!(CoreError::NotFound))?
390 .precision())
391}
392
393#[allow(clippy::too_many_arguments)]
394fn do_push_token_map<'info>(
395 authority: AccountInfo<'info>,
396 token_map_loader: &AccountLoader<'info, TokenMapHeader>,
397 system_program: AccountInfo<'info>,
398 synthetic: bool,
399 name: &str,
400 token: &Pubkey,
401 token_decimals: u8,
402 builder: UpdateTokenConfigParams,
403 enable: bool,
404 new: bool,
405) -> Result<()> {
406 {
412 let new_space = 8 + token_map_loader.load()?.space_after_push()?;
413 let current_space = token_map_loader.as_ref().data_len();
414 let current_lamports = token_map_loader.as_ref().lamports();
415 let new_rent_minimum = Rent::get()?.minimum_balance(new_space);
416 if new_space > current_space {
418 if current_lamports < new_rent_minimum {
419 anchor_lang::system_program::transfer(
420 CpiContext::new(
421 system_program,
422 anchor_lang::system_program::Transfer {
423 from: authority,
424 to: token_map_loader.to_account_info(),
425 },
426 ),
427 new_rent_minimum.saturating_sub(current_lamports),
428 )?;
429 }
430 token_map_loader.as_ref().realloc(new_space, false)?;
431 }
432 }
433
434 let mut token_map = token_map_loader.load_token_map_mut()?;
435 token_map.push_with(
436 token,
437 |config| config.update(name, synthetic, token_decimals, builder, enable, new),
438 new,
439 )?;
440 Ok(())
441}