gmsol_store/instructions/oracle/
custom.rs1use anchor_lang::prelude::*;
2use gmsol_chainlink_datastreams::interface::ChainlinkDataStreamsInterface;
3use gmsol_utils::InitSpace;
4
5use crate::{
6 states::{AmountKey, PriceFeed, PriceFeedPrice, PriceProviderKind, Seed, Store},
7 utils::internal,
8 CoreError,
9};
10
11#[derive(Accounts)]
13#[instruction(index: u16, provider: u8, token: Pubkey)]
14pub struct InitializePriceFeed<'info> {
15 #[account(mut)]
17 pub authority: Signer<'info>,
18 pub store: AccountLoader<'info, Store>,
20 #[account(
22 init,
23 payer = authority,
24 space = 8 + PriceFeed::INIT_SPACE,
25 seeds = [
26 PriceFeed::SEED,
27 store.key().as_ref(),
28 authority.key().as_ref(),
29 &index.to_le_bytes(),
30 &[provider],
31 token.as_ref(),
32 ],
33 bump,
34 )]
35 pub price_feed: AccountLoader<'info, PriceFeed>,
36 pub system_program: Program<'info, System>,
38}
39
40pub(crate) fn unchecked_initialize_price_feed(
42 ctx: Context<InitializePriceFeed>,
43 index: u16,
44 provider: PriceProviderKind,
45 token: &Pubkey,
46 feed_id: &Pubkey,
47) -> Result<()> {
48 require!(
49 matches!(provider, PriceProviderKind::ChainlinkDataStreams),
50 CoreError::NotSupportedCustomPriceProvider
51 );
52 let mut feed = ctx.accounts.price_feed.load_init()?;
53 feed.init(
54 ctx.bumps.price_feed,
55 index,
56 provider,
57 &ctx.accounts.store.key(),
58 &ctx.accounts.authority.key(),
59 token,
60 feed_id,
61 )?;
62 Ok(())
63}
64
65impl<'info> internal::Authentication<'info> for InitializePriceFeed<'info> {
66 fn authority(&self) -> &Signer<'info> {
67 &self.authority
68 }
69
70 fn store(&self) -> &AccountLoader<'info, Store> {
71 &self.store
72 }
73}
74
75#[derive(Accounts)]
77pub struct UpdatePriceFeedWithChainlink<'info> {
78 pub authority: Signer<'info>,
80 pub store: AccountLoader<'info, Store>,
82 pub verifier_account: UncheckedAccount<'info>,
85 pub access_controller: UncheckedAccount<'info>,
88 pub config_account: UncheckedAccount<'info>,
91 #[account(mut, has_one = store, has_one = authority)]
93 pub price_feed: AccountLoader<'info, PriceFeed>,
94 pub chainlink: Interface<'info, ChainlinkDataStreamsInterface>,
96}
97
98pub(crate) fn unchecked_update_price_feed_with_chainlink(
100 ctx: Context<UpdatePriceFeedWithChainlink>,
101 compressed_report: Vec<u8>,
102) -> Result<()> {
103 let accounts = ctx.accounts;
104
105 require_eq!(
106 accounts.price_feed.load()?.provider()?,
107 PriceProviderKind::ChainlinkDataStreams,
108 CoreError::InvalidArgument
109 );
110
111 let price = accounts.decode_and_validate_report(&compressed_report)?;
112 accounts.verify_report(compressed_report)?;
113
114 accounts.price_feed.load_mut()?.update(
115 &price,
116 *accounts
117 .store
118 .load()?
119 .get_amount_by_key(AmountKey::OracleMaxFutureTimestampExcess)
120 .ok_or_else(|| error!(CoreError::Unimplemented))?,
121 )?;
122
123 Ok(())
124}
125
126impl<'info> internal::Authentication<'info> for UpdatePriceFeedWithChainlink<'info> {
127 fn authority(&self) -> &Signer<'info> {
128 &self.authority
129 }
130
131 fn store(&self) -> &AccountLoader<'info, Store> {
132 &self.store
133 }
134}
135
136impl UpdatePriceFeedWithChainlink<'_> {
137 fn decode_and_validate_report(&self, compressed_full_report: &[u8]) -> Result<PriceFeedPrice> {
138 use gmsol_chainlink_datastreams::report::decode_compressed_full_report;
139
140 let report = decode_compressed_full_report(compressed_full_report).map_err(|err| {
141 msg!("[Decode Error] {}", err);
142 error!(CoreError::InvalidPriceReport)
143 })?;
144
145 require_keys_eq!(
146 Pubkey::new_from_array(report.feed_id.0),
147 self.price_feed.load()?.feed_id,
148 CoreError::InvalidPriceReport
149 );
150
151 PriceFeedPrice::from_chainlink_report(&report)
152 }
153
154 fn verify_report(&self, signed_report: Vec<u8>) -> Result<()> {
155 use gmsol_chainlink_datastreams::interface::{verify, VerifyContext};
156
157 let ctx = CpiContext::new(
158 self.chainlink.to_account_info(),
159 VerifyContext {
160 verifier_account: self.verifier_account.to_account_info(),
161 access_controller: self.access_controller.to_account_info(),
162 user: self.store.to_account_info(),
163 config_account: self.config_account.to_account_info(),
164 },
165 );
166
167 verify(
168 ctx.with_signer(&[&self.store.load()?.signer_seeds()]),
169 signed_report,
170 )?;
171
172 Ok(())
173 }
174}