1use anchor_lang::{prelude::*, Bumps};
2
3use crate::{
4 cpi::accounts::{CheckRole, ClearAllPrices, SetPricesFromPriceFeed},
5 states::RoleKey,
6};
7
8pub trait WithStore<'info> {
10 fn store_program(&self) -> AccountInfo<'info>;
12
13 fn store(&self) -> AccountInfo<'info>;
15}
16
17pub trait CpiAuthentication<'info>: Bumps + Sized + WithStore<'info> {
19 fn authority(&self) -> AccountInfo<'info>;
24
25 fn check_role_ctx(&self) -> CpiContext<'_, '_, '_, 'info, CheckRole<'info>> {
27 CpiContext::new(
28 self.store_program(),
29 CheckRole {
30 authority: self.authority(),
31 store: self.store(),
32 },
33 )
34 }
35
36 fn on_error(&self) -> Result<()>;
38}
39
40pub trait CpiAuthenticate<'info>: CpiAuthentication<'info> {
42 fn only(ctx: &Context<Self>, role: &str) -> Result<()> {
44 let has_role =
45 crate::cpi::check_role(ctx.accounts.check_role_ctx(), role.to_string())?.get();
46 if has_role {
47 Ok(())
48 } else {
49 ctx.accounts.on_error()
50 }
51 }
52
53 fn only_admin(ctx: &Context<Self>) -> Result<()> {
55 let is_admin = crate::cpi::check_admin(ctx.accounts.check_role_ctx())?.get();
56 if is_admin {
57 Ok(())
58 } else {
59 ctx.accounts.on_error()
60 }
61 }
62
63 fn only_market_keeper(ctx: &Context<Self>) -> Result<()> {
65 Self::only(ctx, RoleKey::MARKET_KEEPER)
66 }
67
68 fn only_order_keeper(ctx: &Context<Self>) -> Result<()> {
70 Self::only(ctx, RoleKey::ORDER_KEEPER)
71 }
72}
73
74impl<'info, T> CpiAuthenticate<'info> for T where T: CpiAuthentication<'info> {}
75
76pub trait WithOracle<'info>: WithStore<'info> {
78 fn chainlink_program(&self) -> Option<AccountInfo<'info>>;
80
81 fn oracle(&self) -> AccountInfo<'info>;
83
84 fn token_map(&self) -> AccountInfo<'info>;
86
87 fn controller(&self) -> AccountInfo<'info>;
89}
90
91pub trait WithOracleExt<'info>: WithOracle<'info> {
93 fn set_prices_from_price_feed_ctx(
95 &self,
96 feeds: Vec<AccountInfo<'info>>,
97 ) -> CpiContext<'_, '_, '_, 'info, SetPricesFromPriceFeed<'info>> {
98 CpiContext::new(
99 self.store_program().to_account_info(),
100 SetPricesFromPriceFeed {
101 authority: self.controller(),
102 store: self.store(),
103 token_map: self.token_map(),
104 oracle: self.oracle(),
105 chainlink_program: self.chainlink_program(),
106 },
107 )
108 .with_remaining_accounts(feeds)
109 }
110
111 fn clear_all_prices_ctx(&self) -> CpiContext<'_, '_, '_, 'info, ClearAllPrices<'info>> {
113 CpiContext::new(
114 self.store_program().to_account_info(),
115 ClearAllPrices {
116 authority: self.controller(),
117 store: self.store(),
118 oracle: self.oracle(),
119 },
120 )
121 }
122
123 fn with_oracle_prices<T>(
125 &mut self,
126 tokens: Vec<Pubkey>,
127 remaining_accounts: &'info [AccountInfo<'info>],
128 signer_seeds: &[&[u8]],
129 f: impl FnOnce(&mut Self, &'info [AccountInfo<'info>]) -> Result<T>,
130 ) -> Result<T> {
131 require_gte!(
132 remaining_accounts.len(),
133 tokens.len(),
134 ErrorCode::AccountNotEnoughKeys
135 );
136 let feeds = remaining_accounts[..tokens.len()].to_vec();
137 let remaining_accounts = &remaining_accounts[tokens.len()..];
138 crate::cpi::set_prices_from_price_feed(
139 self.set_prices_from_price_feed_ctx(feeds)
140 .with_signer(&[signer_seeds]),
141 tokens,
142 )?;
143 let output = f(self, remaining_accounts)?;
144 crate::cpi::clear_all_prices(self.clear_all_prices_ctx().with_signer(&[signer_seeds]))?;
145 Ok(output)
146 }
147}
148
149impl<'info, T> WithOracleExt<'info> for T where T: WithOracle<'info> {}