1use std::{future::Future, ops::Deref};
2
3use anchor_client::{
4 anchor_lang::system_program,
5 solana_sdk::{pubkey::Pubkey, signer::Signer},
6};
7use gmsol_solana_utils::transaction_builder::TransactionBuilder;
8use gmsol_store::{
9 accounts, instruction,
10 states::{Oracle, PriceProviderKind},
11};
12use gmsol_utils::InitSpace;
13
14pub trait OracleOps<C> {
16 fn initialize_oracle<'a>(
18 &'a self,
19 store: &Pubkey,
20 oracle: &'a dyn Signer,
21 authority: Option<&Pubkey>,
22 ) -> impl Future<Output = crate::Result<(TransactionBuilder<'a, C>, Pubkey)>>;
23
24 fn initialize_price_feed(
26 &self,
27 store: &Pubkey,
28 index: u16,
29 provider: PriceProviderKind,
30 token: &Pubkey,
31 feed_id: &Pubkey,
32 ) -> (TransactionBuilder<C>, Pubkey);
33
34 #[cfg(feature = "gmsol-chainlink-datastreams")]
36 fn update_price_feed_with_chainlink(
37 &self,
38 store: &Pubkey,
39 price_feed: &Pubkey,
40 chainlink: &Pubkey,
41 access_controller: &Pubkey,
42 signed_report: &[u8],
43 ) -> crate::Result<TransactionBuilder<C>>;
44}
45
46impl<C, S> OracleOps<C> for crate::Client<C>
47where
48 C: Deref<Target = S> + Clone,
49 S: Signer,
50{
51 async fn initialize_oracle<'a>(
52 &'a self,
53 store: &Pubkey,
54 oracle: &'a dyn Signer,
55 authority: Option<&Pubkey>,
56 ) -> crate::Result<(TransactionBuilder<'a, C>, Pubkey)> {
57 use anchor_client::solana_sdk::system_instruction::create_account;
58
59 let payer = self.payer();
60 let oracle_address = oracle.pubkey();
61
62 let size = 8 + Oracle::INIT_SPACE;
63 let lamports = self
64 .store_program()
65 .rpc()
66 .get_minimum_balance_for_rent_exemption(size)
67 .await
68 .map_err(anchor_client::ClientError::from)?;
69 let create = create_account(
70 &payer,
71 &oracle_address,
72 lamports,
73 size as u64,
74 self.store_program_id(),
75 );
76
77 let builder = self
78 .store_transaction()
79 .pre_instruction(create)
80 .anchor_accounts(accounts::InitializeOracle {
81 payer,
82 authority: authority.copied().unwrap_or(payer),
83 store: *store,
84 oracle: oracle_address,
85 system_program: system_program::ID,
86 })
87 .anchor_args(instruction::InitializeOracle {})
88 .signer(oracle);
89 Ok((builder, oracle_address))
90 }
91
92 fn initialize_price_feed(
93 &self,
94 store: &Pubkey,
95 index: u16,
96 provider: PriceProviderKind,
97 token: &Pubkey,
98 feed_id: &Pubkey,
99 ) -> (TransactionBuilder<C>, Pubkey) {
100 let authority = self.payer();
101 let price_feed = self.find_price_feed_address(store, &authority, index, provider, token);
102 let rpc = self
103 .store_transaction()
104 .anchor_accounts(accounts::InitializePriceFeed {
105 authority,
106 store: *store,
107 price_feed,
108 system_program: system_program::ID,
109 })
110 .anchor_args(instruction::InitializePriceFeed {
111 index,
112 provider: provider.into(),
113 token: *token,
114 feed_id: *feed_id,
115 });
116 (rpc, price_feed)
117 }
118
119 #[cfg(feature = "gmsol-chainlink-datastreams")]
120 fn update_price_feed_with_chainlink(
121 &self,
122 store: &Pubkey,
123 price_feed: &Pubkey,
124 chainlink: &Pubkey,
125 access_controller: &Pubkey,
126 signed_report: &[u8],
127 ) -> crate::Result<TransactionBuilder<C>> {
128 use gmsol_chainlink_datastreams::utils::{
129 find_config_account_pda, find_verifier_account_pda, Compressor,
130 };
131
132 let authority = self.payer();
133 let verifier_account = find_verifier_account_pda(chainlink);
134 let config_account = find_config_account_pda(signed_report, chainlink);
135 Ok(self
136 .store_transaction()
137 .anchor_accounts(accounts::UpdatePriceFeedWithChainlink {
138 authority,
139 store: *store,
140 verifier_account,
141 access_controller: *access_controller,
142 config_account,
143 price_feed: *price_feed,
144 chainlink: *chainlink,
145 })
146 .anchor_args(instruction::UpdatePriceFeedWithChainlink {
147 compressed_report: Compressor::compress(signed_report)
148 .map_err(crate::Error::invalid_argument)?,
149 }))
150 }
151}