1use std::{future::Future, ops::Deref};
2
3use gmsol_programs::gmsol_store::{
4 accounts::Oracle,
5 client::{accounts, args},
6};
7use gmsol_solana_utils::transaction_builder::TransactionBuilder;
8use gmsol_utils::oracle::PriceProviderKind;
9use solana_sdk::{
10 pubkey::Pubkey, signer::Signer, system_instruction::create_account, system_program,
11};
12
13#[cfg(feature = "gmsol-chainlink-datastreams")]
15pub struct ChainlinkPriceFeedUpdateArgs<'a> {
16 pub chainlink: &'a Pubkey,
18 pub access_controller: &'a Pubkey,
20 pub signed_report: &'a [u8],
22 pub idempotent: bool,
24}
25
26pub trait OracleOps<C> {
28 fn initialize_oracle<'a>(
30 &'a self,
31 store: &Pubkey,
32 oracle: &'a dyn Signer,
33 authority: Option<&Pubkey>,
34 ) -> impl Future<Output = crate::Result<(TransactionBuilder<'a, C>, Pubkey)>>;
35
36 fn initialize_price_feed(
38 &self,
39 store: &Pubkey,
40 index: u16,
41 provider: PriceProviderKind,
42 token: &Pubkey,
43 feed_id: &Pubkey,
44 ) -> (TransactionBuilder<C>, Pubkey);
45
46 #[cfg(feature = "gmsol-chainlink-datastreams")]
48 fn update_price_feed_with_chainlink_and_authority<'a>(
49 &'a self,
50 store: &Pubkey,
51 price_feed: &Pubkey,
52 args: ChainlinkPriceFeedUpdateArgs<'_>,
53 authority: Option<&'a dyn Signer>,
54 ) -> crate::Result<TransactionBuilder<'a, C>>;
55
56 #[cfg(feature = "gmsol-chainlink-datastreams")]
58 fn update_price_feed_with_chainlink(
59 &self,
60 store: &Pubkey,
61 price_feed: &Pubkey,
62 chainlink: &Pubkey,
63 access_controller: &Pubkey,
64 signed_report: &[u8],
65 ) -> crate::Result<TransactionBuilder<C>> {
66 self.update_price_feed_with_chainlink_and_authority(
67 store,
68 price_feed,
69 ChainlinkPriceFeedUpdateArgs {
70 chainlink,
71 access_controller,
72 signed_report,
73 idempotent: false,
74 },
75 None,
76 )
77 }
78}
79
80impl<C: Deref<Target = impl Signer> + Clone> OracleOps<C> for crate::Client<C> {
81 async fn initialize_oracle<'a>(
82 &'a self,
83 store: &Pubkey,
84 oracle: &'a dyn Signer,
85 authority: Option<&Pubkey>,
86 ) -> crate::Result<(TransactionBuilder<'a, C>, Pubkey)> {
87 let payer = self.payer();
88 let oracle_address = oracle.pubkey();
89
90 let size = 8 + std::mem::size_of::<Oracle>();
91 let lamports = self
92 .store_program()
93 .rpc()
94 .get_minimum_balance_for_rent_exemption(size)
95 .await
96 .map_err(crate::Error::custom)?;
97 let create = create_account(
98 &payer,
99 &oracle_address,
100 lamports,
101 size as u64,
102 self.store_program_id(),
103 );
104
105 let builder = self
106 .store_transaction()
107 .pre_instruction(create, false)
108 .anchor_accounts(accounts::InitializeOracle {
109 payer,
110 authority: authority.copied().unwrap_or(payer),
111 store: *store,
112 oracle: oracle_address,
113 system_program: system_program::ID,
114 })
115 .anchor_args(args::InitializeOracle {})
116 .signer(oracle);
117 Ok((builder, oracle_address))
118 }
119
120 fn initialize_price_feed(
121 &self,
122 store: &Pubkey,
123 index: u16,
124 provider: PriceProviderKind,
125 token: &Pubkey,
126 feed_id: &Pubkey,
127 ) -> (TransactionBuilder<C>, Pubkey) {
128 let authority = self.payer();
129 let price_feed = self.find_price_feed_address(store, &authority, index, provider, token);
130 let rpc = self
131 .store_transaction()
132 .anchor_accounts(accounts::InitializePriceFeed {
133 authority,
134 store: *store,
135 price_feed,
136 system_program: system_program::ID,
137 })
138 .anchor_args(args::InitializePriceFeed {
139 index,
140 provider: provider.into(),
141 token: *token,
142 feed_id: *feed_id,
143 });
144 (rpc, price_feed)
145 }
146
147 #[cfg(feature = "gmsol-chainlink-datastreams")]
148 fn update_price_feed_with_chainlink_and_authority<'a>(
149 &'a self,
150 store: &Pubkey,
151 price_feed: &Pubkey,
152 args: ChainlinkPriceFeedUpdateArgs<'_>,
153 authority: Option<&'a dyn Signer>,
154 ) -> crate::Result<TransactionBuilder<'a, C>> {
155 use gmsol_chainlink_datastreams::utils::{
156 find_config_account_pda, find_verifier_account_pda, Compressor,
157 };
158
159 let ChainlinkPriceFeedUpdateArgs {
160 chainlink,
161 access_controller,
162 signed_report,
163 idempotent,
164 } = args;
165
166 let (authority, rpc) = match authority {
167 Some(signer) => (signer.pubkey(), self.store_transaction().signer(signer)),
168 None => (self.payer(), self.store_transaction()),
169 };
170 let verifier_account = find_verifier_account_pda(chainlink);
171 let config_account = find_config_account_pda(signed_report, chainlink);
172
173 let builder = rpc.anchor_accounts(accounts::UpdatePriceFeedWithChainlink {
174 authority,
175 store: *store,
176 verifier_account,
177 access_controller: *access_controller,
178 config_account,
179 price_feed: *price_feed,
180 chainlink: *chainlink,
181 });
182
183 if idempotent {
184 Ok(
185 builder.anchor_args(args::UpdatePriceFeedWithChainlinkIdempotent {
186 compressed_report: Compressor::compress(signed_report)
187 .map_err(crate::Error::custom)?,
188 }),
189 )
190 } else {
191 Ok(builder.anchor_args(args::UpdatePriceFeedWithChainlink {
192 compressed_report: Compressor::compress(signed_report)
193 .map_err(crate::Error::custom)?,
194 }))
195 }
196 }
197}