Skip to main content

gmsol_sdk/client/ops/
oracle.rs

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/// Arguments for updating a Chainlink price feed.
14#[cfg(feature = "gmsol-chainlink-datastreams")]
15pub struct ChainlinkPriceFeedUpdateArgs<'a> {
16    /// Chainlink DataStreams program address.
17    pub chainlink: &'a Pubkey,
18    /// The address controller address.
19    pub access_controller: &'a Pubkey,
20    /// Signed report.
21    pub signed_report: &'a [u8],
22    /// Whether to update idempotently.
23    pub idempotent: bool,
24}
25
26/// Operations for oracle management.
27pub trait OracleOps<C> {
28    /// Initialize [`Oracle`] account.
29    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    /// Initialize Price Feed.
37    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    /// Update price feed with chainlink.
47    #[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    /// Update price feed with chainlink.
57    #[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}