gmsol_sdk/client/ops/
market.rs

1use std::{future::Future, ops::Deref};
2
3use anchor_spl::associated_token::get_associated_token_address_with_program_id;
4use gmsol_model::{price::Prices, PnlFactorKind};
5use gmsol_programs::gmsol_store::{
6    client::{accounts, args},
7    types::EntryArgs,
8};
9use gmsol_solana_utils::{
10    make_bundle_builder::MakeBundleBuilder, transaction_builder::TransactionBuilder,
11    IntoAtomicGroup,
12};
13use gmsol_utils::{
14    market::{MarketConfigFactor, MarketConfigFlag, MarketConfigKey, MarketMeta},
15    oracle::PriceProviderKind,
16    token_config::{token_records, TokensWithFeed},
17};
18use indexmap::IndexMap;
19use solana_sdk::{pubkey::Pubkey, signer::Signer, system_program};
20
21use crate::{
22    builders::market::SetMarketConfigUpdatable,
23    client::{
24        feeds_parser::{FeedAddressMap, FeedsParser},
25        pull_oracle::{FeedIds, PullOraclePriceConsumer},
26    },
27    utils::market::ordered_tokens,
28    Client,
29};
30
31use super::token_account::TokenAccountOps;
32
33type Factor = u128;
34
35const DEFAULT_MAX_AGE: u32 = 120;
36
37/// Market operations.
38pub trait MarketOps<C> {
39    /// Initialize a market vault for the given token.
40    fn initialize_market_vault(
41        &self,
42        store: &Pubkey,
43        token: &Pubkey,
44    ) -> (TransactionBuilder<C>, Pubkey);
45
46    /// Create a new market and return its token mint address.
47    #[allow(clippy::too_many_arguments)]
48    fn create_market(
49        &self,
50        store: &Pubkey,
51        name: &str,
52        index_token: &Pubkey,
53        long_token: &Pubkey,
54        short_token: &Pubkey,
55        enable: bool,
56        token_map: Option<&Pubkey>,
57    ) -> impl Future<Output = crate::Result<(TransactionBuilder<C>, Pubkey)>>;
58
59    /// Fund the given market.
60    fn fund_market(
61        &self,
62        store: &Pubkey,
63        market_token: &Pubkey,
64        source_account: &Pubkey,
65        amount: u64,
66        token: Option<&Pubkey>,
67    ) -> impl Future<Output = crate::Result<TransactionBuilder<C>>>;
68
69    /// Claim fees.
70    fn claim_fees(
71        &self,
72        store: &Pubkey,
73        market_token: &Pubkey,
74        is_long_token: bool,
75    ) -> ClaimFeesBuilder<C>;
76
77    /// Get market status.
78    fn get_market_status(
79        &self,
80        store: &Pubkey,
81        market_token: &Pubkey,
82        prices: Prices<u128>,
83        maximize_pnl: bool,
84        maximize_pool_value: bool,
85    ) -> TransactionBuilder<C>;
86
87    /// Get market token price.
88    fn get_market_token_price(
89        &self,
90        store: &Pubkey,
91        market_token: &Pubkey,
92        prices: Prices<u128>,
93        pnl_factor: PnlFactorKind,
94        maximize: bool,
95    ) -> TransactionBuilder<C>;
96
97    /// Get market token value.
98    fn get_market_token_value(
99        &self,
100        store: &Pubkey,
101        oracle: &Pubkey,
102        market_token: &Pubkey,
103        amount: u64,
104    ) -> GetMarketTokenValueBuilder<'_, C>;
105
106    /// Update market config.
107    fn update_market_config(
108        &self,
109        store: &Pubkey,
110        market_token: &Pubkey,
111        key: &str,
112        value: &Factor,
113    ) -> crate::Result<TransactionBuilder<C>>;
114
115    /// Update market config flag
116    fn update_market_config_flag(
117        &self,
118        store: &Pubkey,
119        market_token: &Pubkey,
120        key: &str,
121        value: bool,
122    ) -> crate::Result<TransactionBuilder<C>>;
123
124    /// Update market config by key.
125    fn update_market_config_by_key(
126        &self,
127        store: &Pubkey,
128        market_token: &Pubkey,
129        key: MarketConfigKey,
130        value: &Factor,
131    ) -> crate::Result<TransactionBuilder<C>> {
132        let key = key.to_string();
133        self.update_market_config(store, market_token, &key, value)
134    }
135
136    /// Update market config flag by key.
137    fn update_market_config_flag_by_key(
138        &self,
139        store: &Pubkey,
140        market_token: &Pubkey,
141        key: MarketConfigFlag,
142        value: bool,
143    ) -> crate::Result<TransactionBuilder<C>> {
144        let key = key.to_string();
145        self.update_market_config_flag(store, market_token, &key, value)
146    }
147
148    /// Toggle market.
149    fn toggle_market(
150        &self,
151        store: &Pubkey,
152        market_token: &Pubkey,
153        enable: bool,
154    ) -> TransactionBuilder<C>;
155
156    /// Toggle GT minting.
157    fn toggle_gt_minting(
158        &self,
159        store: &Pubkey,
160        market_token: &Pubkey,
161        enable: bool,
162    ) -> TransactionBuilder<C>;
163
164    /// Initialize Market Config Buffer.
165    fn initialize_market_config_buffer<'a>(
166        &'a self,
167        store: &Pubkey,
168        buffer: &'a dyn Signer,
169        expire_after_secs: u32,
170    ) -> TransactionBuilder<'a, C>;
171
172    /// Close Market Config Buffer.
173    fn close_marekt_config_buffer(
174        &self,
175        buffer: &Pubkey,
176        receiver: Option<&Pubkey>,
177    ) -> TransactionBuilder<C>;
178
179    /// Push to Market Config Buffer.
180    fn push_to_market_config_buffer<S: ToString>(
181        &self,
182        buffer: &Pubkey,
183        new_configs: impl IntoIterator<Item = (S, Factor)>,
184    ) -> TransactionBuilder<C>;
185
186    /// Set the authority of the Market Config Buffer.
187    fn set_market_config_buffer_authority(
188        &self,
189        buffer: &Pubkey,
190        new_authority: &Pubkey,
191    ) -> TransactionBuilder<C>;
192
193    /// Update Market Config with the buffer.
194    fn update_market_config_with_buffer(
195        &self,
196        store: &Pubkey,
197        market_token: &Pubkey,
198        buffer: &Pubkey,
199    ) -> TransactionBuilder<C>;
200
201    /// Set market config updatable.
202    fn set_market_config_updatable(
203        &self,
204        store: &Pubkey,
205        flags: impl Into<IndexMap<MarketConfigFlag, bool>>,
206        factors: impl Into<IndexMap<MarketConfigFactor, bool>>,
207    ) -> crate::Result<TransactionBuilder<C>>;
208}
209
210impl<C: Deref<Target = impl Signer> + Clone> MarketOps<C> for crate::Client<C> {
211    fn initialize_market_vault(
212        &self,
213        store: &Pubkey,
214        token: &Pubkey,
215    ) -> (TransactionBuilder<C>, Pubkey) {
216        let authority = self.payer();
217        let vault = self.find_market_vault_address(store, token);
218        let builder = self
219            .store_transaction()
220            .anchor_accounts(accounts::InitializeMarketVault {
221                authority,
222                store: *store,
223                mint: *token,
224                vault,
225                system_program: system_program::ID,
226                token_program: anchor_spl::token::ID,
227            })
228            .anchor_args(args::InitializeMarketVault {});
229        (builder, vault)
230    }
231
232    async fn create_market(
233        &self,
234        store: &Pubkey,
235        name: &str,
236        index_token: &Pubkey,
237        long_token: &Pubkey,
238        short_token: &Pubkey,
239        enable: bool,
240        token_map: Option<&Pubkey>,
241    ) -> crate::Result<(TransactionBuilder<C>, Pubkey)> {
242        let token_map = match token_map {
243            Some(token_map) => *token_map,
244            None => self
245                .authorized_token_map_address(store)
246                .await?
247                .ok_or(crate::Error::NotFound)?,
248        };
249        let authority = self.payer();
250        let market_token =
251            self.find_market_token_address(store, index_token, long_token, short_token);
252        let prepare_long_token_vault = self.initialize_market_vault(store, long_token).0;
253        let prepare_short_token_vault = self.initialize_market_vault(store, short_token).0;
254        let prepare_market_token_vault = self.initialize_market_vault(store, &market_token).0;
255        let builder = self
256            .store_transaction()
257            .anchor_accounts(accounts::InitializeMarket {
258                authority,
259                store: *store,
260                token_map,
261                market: self.find_market_address(store, &market_token),
262                market_token_mint: market_token,
263                long_token_mint: *long_token,
264                short_token_mint: *short_token,
265                long_token_vault: self.find_market_vault_address(store, long_token),
266                short_token_vault: self.find_market_vault_address(store, short_token),
267                system_program: system_program::ID,
268                token_program: anchor_spl::token::ID,
269            })
270            .anchor_args(args::InitializeMarket {
271                name: name.to_string(),
272                index_token_mint: *index_token,
273                enable,
274            });
275        Ok((
276            prepare_long_token_vault
277                .merge(prepare_short_token_vault)
278                .merge(builder)
279                .merge(prepare_market_token_vault),
280            market_token,
281        ))
282    }
283
284    async fn fund_market(
285        &self,
286        store: &Pubkey,
287        market_token: &Pubkey,
288        source_account: &Pubkey,
289        amount: u64,
290        token: Option<&Pubkey>,
291    ) -> crate::Result<TransactionBuilder<C>> {
292        use anchor_spl::token::TokenAccount;
293
294        let token = match token {
295            Some(token) => *token,
296            None => {
297                let account = self
298                    .account::<TokenAccount>(source_account)
299                    .await?
300                    .ok_or(crate::Error::NotFound)?;
301                account.mint
302            }
303        };
304        let vault = self.find_market_vault_address(store, &token);
305        let market = self.find_market_address(store, market_token);
306        Ok(self
307            .store_transaction()
308            .anchor_args(args::MarketTransferIn { amount })
309            .anchor_accounts(accounts::MarketTransferIn {
310                authority: self.payer(),
311                from_authority: self.payer(),
312                store: *store,
313                market,
314                vault,
315                from: *source_account,
316                token_program: anchor_spl::token::ID,
317                event_authority: self.store_event_authority(),
318                program: *self.store_program_id(),
319            }))
320    }
321
322    fn claim_fees(
323        &self,
324        store: &Pubkey,
325        market_token: &Pubkey,
326        is_long_token: bool,
327    ) -> ClaimFeesBuilder<C> {
328        ClaimFeesBuilder::new(self, store, market_token, is_long_token)
329    }
330
331    fn get_market_status(
332        &self,
333        store: &Pubkey,
334        market_token: &Pubkey,
335        prices: Prices<u128>,
336        maximize_pnl: bool,
337        maximize_pool_value: bool,
338    ) -> TransactionBuilder<C> {
339        self.store_transaction()
340            .anchor_args(args::GetMarketStatus {
341                prices: prices.into(),
342                maximize_pnl,
343                maximize_pool_value,
344            })
345            .anchor_accounts(accounts::GetMarketStatus {
346                market: self.find_market_address(store, market_token),
347            })
348    }
349
350    fn get_market_token_price(
351        &self,
352        store: &Pubkey,
353        market_token: &Pubkey,
354        prices: Prices<u128>,
355        pnl_factor: PnlFactorKind,
356        maximize: bool,
357    ) -> TransactionBuilder<C> {
358        self.store_transaction()
359            .anchor_args(args::GetMarketTokenPrice {
360                prices: prices.into(),
361                pnl_factor: pnl_factor.to_string(),
362                maximize,
363            })
364            .anchor_accounts(accounts::GetMarketTokenPrice {
365                market: self.find_market_address(store, market_token),
366                market_token: *market_token,
367            })
368    }
369
370    fn get_market_token_value(
371        &self,
372        store: &Pubkey,
373        oracle: &Pubkey,
374        market_token: &Pubkey,
375        amount: u64,
376    ) -> GetMarketTokenValueBuilder<'_, C> {
377        GetMarketTokenValueBuilder::new(self, *store, *oracle, *market_token, amount)
378    }
379
380    fn update_market_config(
381        &self,
382        store: &Pubkey,
383        market_token: &Pubkey,
384        key: &str,
385        value: &Factor,
386    ) -> crate::Result<TransactionBuilder<C>> {
387        let req = self
388            .store_transaction()
389            .anchor_args(args::UpdateMarketConfig {
390                key: key.to_string(),
391                value: *value,
392            })
393            .anchor_accounts(accounts::UpdateMarketConfig {
394                authority: self.payer(),
395                store: *store,
396                market: self.find_market_address(store, market_token),
397            });
398        Ok(req)
399    }
400
401    fn update_market_config_flag(
402        &self,
403        store: &Pubkey,
404        market_token: &Pubkey,
405        key: &str,
406        value: bool,
407    ) -> crate::Result<TransactionBuilder<C>> {
408        let req = self
409            .store_transaction()
410            .anchor_args(args::UpdateMarketConfigFlag {
411                key: key.to_string(),
412                value,
413            })
414            .anchor_accounts(accounts::UpdateMarketConfig {
415                authority: self.payer(),
416                store: *store,
417                market: self.find_market_address(store, market_token),
418            });
419        Ok(req)
420    }
421
422    fn toggle_market(
423        &self,
424        store: &Pubkey,
425        market_token: &Pubkey,
426        enable: bool,
427    ) -> TransactionBuilder<C> {
428        self.store_transaction()
429            .anchor_args(args::ToggleMarket { enable })
430            .anchor_accounts(accounts::ToggleMarket {
431                authority: self.payer(),
432                store: *store,
433                market: self.find_market_address(store, market_token),
434            })
435    }
436
437    fn toggle_gt_minting(
438        &self,
439        store: &Pubkey,
440        market_token: &Pubkey,
441        enable: bool,
442    ) -> TransactionBuilder<C> {
443        self.store_transaction()
444            .anchor_args(args::ToggleGtMinting { enable })
445            .anchor_accounts(accounts::ToggleGtMinting {
446                authority: self.payer(),
447                store: *store,
448                market: self.find_market_address(store, market_token),
449            })
450    }
451
452    fn initialize_market_config_buffer<'a>(
453        &'a self,
454        store: &Pubkey,
455        buffer: &'a dyn Signer,
456        expire_after_secs: u32,
457    ) -> TransactionBuilder<'a, C> {
458        self.store_transaction()
459            .anchor_args(args::InitializeMarketConfigBuffer { expire_after_secs })
460            .anchor_accounts(accounts::InitializeMarketConfigBuffer {
461                authority: self.payer(),
462                store: *store,
463                buffer: buffer.pubkey(),
464                system_program: system_program::ID,
465            })
466            .signer(buffer)
467    }
468
469    fn close_marekt_config_buffer(
470        &self,
471        buffer: &Pubkey,
472        receiver: Option<&Pubkey>,
473    ) -> TransactionBuilder<C> {
474        self.store_transaction()
475            .anchor_args(args::CloseMarketConfigBuffer {})
476            .anchor_accounts(accounts::CloseMarketConfigBuffer {
477                authority: self.payer(),
478                buffer: *buffer,
479                receiver: receiver.copied().unwrap_or(self.payer()),
480            })
481    }
482
483    fn push_to_market_config_buffer<K: ToString>(
484        &self,
485        buffer: &Pubkey,
486        new_configs: impl IntoIterator<Item = (K, Factor)>,
487    ) -> TransactionBuilder<C> {
488        self.store_transaction()
489            .anchor_args(args::PushToMarketConfigBuffer {
490                new_configs: new_configs
491                    .into_iter()
492                    .map(|(key, value)| EntryArgs {
493                        key: key.to_string(),
494                        value,
495                    })
496                    .collect(),
497            })
498            .anchor_accounts(accounts::PushToMarketConfigBuffer {
499                authority: self.payer(),
500                buffer: *buffer,
501                system_program: system_program::ID,
502            })
503    }
504
505    fn set_market_config_buffer_authority(
506        &self,
507        buffer: &Pubkey,
508        new_authority: &Pubkey,
509    ) -> TransactionBuilder<C> {
510        self.store_transaction()
511            .anchor_args(args::SetMarketConfigBufferAuthority {
512                new_authority: *new_authority,
513            })
514            .anchor_accounts(accounts::SetMarketConfigBufferAuthority {
515                authority: self.payer(),
516                buffer: *buffer,
517            })
518    }
519
520    fn update_market_config_with_buffer(
521        &self,
522        store: &Pubkey,
523        market_token: &Pubkey,
524        buffer: &Pubkey,
525    ) -> TransactionBuilder<C> {
526        self.store_transaction()
527            .anchor_args(args::UpdateMarketConfigWithBuffer {})
528            .anchor_accounts(accounts::UpdateMarketConfigWithBuffer {
529                authority: self.payer(),
530                store: *store,
531                market: self.find_market_address(store, market_token),
532                buffer: *buffer,
533            })
534    }
535
536    fn set_market_config_updatable(
537        &self,
538        store: &Pubkey,
539        flags: impl Into<IndexMap<MarketConfigFlag, bool>>,
540        factors: impl Into<IndexMap<MarketConfigFactor, bool>>,
541    ) -> crate::Result<TransactionBuilder<C>> {
542        let ag = SetMarketConfigUpdatable::builder()
543            .payer(self.payer())
544            .store_program(self.store_program_for_builders(store))
545            .flags(flags)
546            .factors(factors)
547            .build()
548            .into_atomic_group(&())?;
549        Ok(self.store_transaction().pre_atomic_group(ag, true))
550    }
551}
552
553/// Claim fees builder.
554pub struct ClaimFeesBuilder<'a, C> {
555    client: &'a crate::Client<C>,
556    store: Pubkey,
557    market_token: Pubkey,
558    is_long_token: bool,
559    hint_token: Option<Pubkey>,
560}
561
562impl<'a, C: Deref<Target = impl Signer> + Clone> ClaimFeesBuilder<'a, C> {
563    /// Create a new builder.
564    pub fn new(
565        client: &'a crate::Client<C>,
566        store: &Pubkey,
567        market_token: &Pubkey,
568        is_long_token: bool,
569    ) -> Self {
570        Self {
571            client,
572            store: *store,
573            market_token: *market_token,
574            is_long_token,
575            hint_token: None,
576        }
577    }
578
579    /// Set hint.
580    pub fn set_hint(&mut self, token: Pubkey) -> &mut Self {
581        self.hint_token = Some(token);
582        self
583    }
584
585    /// Build.
586    pub async fn build(&self) -> crate::Result<TransactionBuilder<'a, C>> {
587        let market = self
588            .client
589            .find_market_address(&self.store, &self.market_token);
590        let token = match self.hint_token {
591            Some(token) => token,
592            None => {
593                let market = self.client.market(&market).await?;
594                MarketMeta::from(market.meta).pnl_token(self.is_long_token)
595            }
596        };
597
598        let authority = self.client.payer();
599        let vault = self.client.find_market_vault_address(&self.store, &token);
600        // Note: If possible, the program ID should be read from the market.
601        let token_program = anchor_spl::token::ID;
602        let target =
603            get_associated_token_address_with_program_id(&authority, &token, &token_program);
604
605        let prepare = self
606            .client
607            .prepare_associated_token_account(&token, &token_program, None);
608
609        let rpc = self
610            .client
611            .store_transaction()
612            .anchor_accounts(accounts::ClaimFeesFromMarket {
613                authority,
614                store: self.store,
615                market,
616                token_mint: token,
617                vault,
618                target,
619                token_program,
620                event_authority: self.client.store_event_authority(),
621                program: *self.client.store_program_id(),
622            })
623            .anchor_args(args::ClaimFeesFromMarket {});
624
625        Ok(prepare.merge(rpc))
626    }
627}
628
629/// Builder for `get_market_token_value` instruction.
630pub struct GetMarketTokenValueBuilder<'a, C> {
631    client: &'a Client<C>,
632    store: Pubkey,
633    oracle: Pubkey,
634    market_token: Pubkey,
635    amount: u64,
636    pnl_factor: PnlFactorKind,
637    maximize: bool,
638    max_age: u32,
639    emit_event: bool,
640    feeds_parser: FeedsParser,
641    hint: Option<GetMarketTokenValueHint>,
642}
643
644/// Hint for [`GetMarketTokenValueBuilder`].
645#[derive(Debug, Clone)]
646pub struct GetMarketTokenValueHint {
647    /// Token map address.
648    pub token_map: Pubkey,
649    /// Feeds.
650    pub feeds: TokensWithFeed,
651}
652
653impl<C> GetMarketTokenValueBuilder<'_, C> {
654    /// Set PnL factor kind. Defaults to [`MaxAfterDeposit`](PnlFactorKind::MaxAfterDeposit).
655    pub fn pnl_factor(&mut self, kind: PnlFactorKind) -> &mut Self {
656        self.pnl_factor = kind;
657        self
658    }
659
660    /// Set whether to maximize the computed value. Defaults to `false`.
661    pub fn maximize(&mut self, maximize: bool) -> &mut Self {
662        self.maximize = maximize;
663        self
664    }
665
666    /// Set max age (seconds). Defaults to `120`.
667    pub fn max_age(&mut self, max_age: u32) -> &mut Self {
668        self.max_age = max_age;
669        self
670    }
671
672    /// Set whether to emit event. Defaults to `true`
673    pub fn emit_event(&mut self, emit: bool) -> &mut Self {
674        self.emit_event = emit;
675        self
676    }
677
678    /// Set hint.
679    pub fn hint(&mut self, hint: Option<GetMarketTokenValueHint>) -> &mut Self {
680        self.hint = hint;
681        self
682    }
683}
684
685impl<'a, C: Deref<Target = impl Signer> + Clone> GetMarketTokenValueBuilder<'a, C> {
686    fn new(
687        client: &'a Client<C>,
688        store: Pubkey,
689        oracle: Pubkey,
690        market_token: Pubkey,
691        amount: u64,
692    ) -> Self {
693        Self {
694            client,
695            store,
696            oracle,
697            market_token,
698            amount,
699            pnl_factor: PnlFactorKind::MaxAfterDeposit,
700            maximize: false,
701            max_age: DEFAULT_MAX_AGE,
702            emit_event: true,
703            feeds_parser: Default::default(),
704            hint: None,
705        }
706    }
707
708    /// Prepare hint.
709    pub async fn prepare_hint(&mut self) -> crate::Result<GetMarketTokenValueHint> {
710        if let Some(hint) = self.hint.as_ref() {
711            return Ok(hint.clone());
712        }
713
714        let store = self.client.store(&self.store).await?;
715        let token_map_address = store.token_map;
716        let market = self
717            .client
718            .market_by_token(&self.store, &self.market_token)
719            .await?;
720        let token_map = self.client.token_map(&token_map_address).await?;
721        let tokens = ordered_tokens(&market.meta.into());
722        let records = token_records(&token_map, &tokens).map_err(crate::Error::custom)?;
723        let feeds = TokensWithFeed::try_from_records(records).map_err(crate::Error::custom)?;
724        let hint = GetMarketTokenValueHint {
725            token_map: token_map_address,
726            feeds,
727        };
728        self.hint = Some(hint.clone());
729        Ok(hint)
730    }
731
732    async fn build_txn(&mut self) -> crate::Result<TransactionBuilder<'a, C>> {
733        let hint = self.prepare_hint().await?;
734        let Self {
735            client,
736            store,
737            oracle,
738            market_token,
739            amount,
740            pnl_factor,
741            maximize,
742            max_age,
743            feeds_parser,
744            emit_event,
745            ..
746        } = self;
747        let authority = client.payer();
748        let feeds = feeds_parser
749            .parse(&hint.feeds)
750            .collect::<Result<Vec<_>, _>>()?;
751        let market = client.find_market_address(store, market_token);
752        let txn = client
753            .store_transaction()
754            .anchor_args(args::GetMarketTokenValue {
755                amount: *amount,
756                pnl_factor: pnl_factor.to_string(),
757                maximize: *maximize,
758                max_age: *max_age,
759                emit_event: *emit_event,
760            })
761            .anchor_accounts(accounts::GetMarketTokenValue {
762                authority,
763                store: *store,
764                token_map: hint.token_map,
765                oracle: *oracle,
766                market,
767                market_token: *market_token,
768                event_authority: client.store_event_authority(),
769                program: *client.store_program_id(),
770            })
771            .accounts(feeds);
772        Ok(txn)
773    }
774}
775
776impl<'a, C: Deref<Target = impl Signer> + Clone> MakeBundleBuilder<'a, C>
777    for GetMarketTokenValueBuilder<'a, C>
778{
779    async fn build_with_options(
780        &mut self,
781        options: gmsol_solana_utils::bundle_builder::BundleOptions,
782    ) -> gmsol_solana_utils::Result<gmsol_solana_utils::bundle_builder::BundleBuilder<'a, C>> {
783        let mut tx = self.client.bundle_with_options(options);
784
785        tx.try_push(
786            self.build_txn()
787                .await
788                .map_err(gmsol_solana_utils::Error::custom)?,
789        )?;
790
791        Ok(tx)
792    }
793}
794
795impl<C: Deref<Target = impl Signer> + Clone> PullOraclePriceConsumer
796    for GetMarketTokenValueBuilder<'_, C>
797{
798    async fn feed_ids(&mut self) -> crate::Result<FeedIds> {
799        let hint = self.prepare_hint().await?;
800        Ok(FeedIds::new(self.store, hint.feeds))
801    }
802
803    fn process_feeds(
804        &mut self,
805        provider: PriceProviderKind,
806        map: FeedAddressMap,
807    ) -> crate::Result<()> {
808        self.feeds_parser
809            .insert_pull_oracle_feed_parser(provider, map);
810        Ok(())
811    }
812}