gmsol/exchange/
deposit.rs

1use std::ops::Deref;
2
3use anchor_client::{
4    anchor_lang::system_program,
5    solana_sdk::{instruction::AccountMeta, pubkey::Pubkey, signer::Signer},
6};
7use anchor_spl::associated_token::get_associated_token_address;
8use gmsol_solana_utils::{
9    bundle_builder::{BundleBuilder, BundleOptions},
10    compute_budget::ComputeBudget,
11    transaction_builder::TransactionBuilder,
12};
13use gmsol_store::{
14    accounts, instruction,
15    ops::deposit::CreateDepositParams,
16    states::{
17        common::{action::Action, swap::SwapActionParams, TokensWithFeed},
18        Deposit, NonceBytes, PriceProviderKind, TokenMapAccess,
19    },
20};
21
22use crate::{
23    exchange::ExchangeOps,
24    store::{token::TokenAccountOps, utils::FeedsParser},
25    utils::builder::{
26        FeedAddressMap, FeedIds, MakeBundleBuilder, PullOraclePriceConsumer, SetExecutionFee,
27    },
28};
29
30use super::{generate_nonce, get_ata_or_owner};
31
32#[cfg(feature = "pyth-pull-oracle")]
33use crate::pyth::pull_oracle::Prices;
34
35/// `execute_deposit` compute budget.
36pub const EXECUTE_DEPOSIT_COMPUTE_BUDGET: u32 = 400_000;
37
38/// Create Deposit Builder.
39pub struct CreateDepositBuilder<'a, C> {
40    client: &'a crate::Client<C>,
41    store: Pubkey,
42    market_token: Pubkey,
43    execution_fee: u64,
44    long_token_swap_path: Vec<Pubkey>,
45    short_token_swap_path: Vec<Pubkey>,
46    initial_long_token: Option<Pubkey>,
47    initial_short_token: Option<Pubkey>,
48    initial_long_token_account: Option<Pubkey>,
49    initial_short_token_account: Option<Pubkey>,
50    initial_long_token_amount: u64,
51    initial_short_token_amount: u64,
52    min_market_token: u64,
53    receiver: Option<Pubkey>,
54    nonce: Option<NonceBytes>,
55    should_unwrap_native_token: bool,
56}
57
58impl<C> CreateDepositBuilder<'_, C> {
59    /// Set the nonce.
60    pub fn nonce(&mut self, nonce: NonceBytes) -> &mut Self {
61        self.nonce = Some(nonce);
62        self
63    }
64
65    /// Set execution fee. Defaults to min execution fee.
66    pub fn execution_fee(&mut self, fee: u64) -> &mut Self {
67        self.execution_fee = fee;
68        self
69    }
70
71    /// Set min market token to mint.
72    pub fn min_market_token(&mut self, amount: u64) -> &mut Self {
73        self.min_market_token = amount;
74        self
75    }
76
77    /// Set long swap path.
78    pub fn long_token_swap_path(&mut self, market_tokens: Vec<Pubkey>) -> &mut Self {
79        self.long_token_swap_path = market_tokens;
80        self
81    }
82
83    /// Set short swap path.
84    pub fn short_token_swap_path(&mut self, market_tokens: Vec<Pubkey>) -> &mut Self {
85        self.short_token_swap_path = market_tokens;
86        self
87    }
88
89    /// Set recevier.
90    /// Defaults to the payer.
91    pub fn receiver(&mut self, receiver: Option<Pubkey>) -> &mut Self {
92        self.receiver = receiver;
93        self
94    }
95
96    /// Set whether to unwrap native token.
97    /// Defaults to should unwrap.
98    pub fn should_unwrap_native_token(&mut self, should_unwrap: bool) -> &mut Self {
99        self.should_unwrap_native_token = should_unwrap;
100        self
101    }
102}
103
104impl<'a, C, S> CreateDepositBuilder<'a, C>
105where
106    C: Deref<Target = S> + Clone,
107    S: Signer,
108{
109    pub(super) fn new(client: &'a crate::Client<C>, store: Pubkey, market_token: Pubkey) -> Self {
110        Self {
111            client,
112            store,
113            market_token,
114            execution_fee: Deposit::MIN_EXECUTION_LAMPORTS,
115            long_token_swap_path: vec![],
116            short_token_swap_path: vec![],
117            initial_long_token: None,
118            initial_short_token: None,
119            initial_long_token_account: None,
120            initial_short_token_account: None,
121            initial_long_token_amount: 0,
122            initial_short_token_amount: 0,
123            min_market_token: 0,
124            receiver: None,
125            nonce: None,
126            should_unwrap_native_token: true,
127        }
128    }
129
130    fn get_receiver(&self) -> Pubkey {
131        self.receiver.unwrap_or(self.client.payer())
132    }
133
134    fn get_or_find_associated_initial_long_token_account(
135        &self,
136        token: Option<&Pubkey>,
137    ) -> Option<Pubkey> {
138        let token = token?;
139        match self.initial_long_token_account {
140            Some(account) => Some(account),
141            None => Some(get_associated_token_address(&self.client.payer(), token)),
142        }
143    }
144
145    fn get_or_find_associated_initial_short_token_account(
146        &self,
147        token: Option<&Pubkey>,
148    ) -> Option<Pubkey> {
149        let token = token?;
150        match self.initial_short_token_account {
151            Some(account) => Some(account),
152            None => Some(get_associated_token_address(&self.client.payer(), token)),
153        }
154    }
155
156    async fn get_or_fetch_initial_tokens(
157        &self,
158        market: &Pubkey,
159    ) -> crate::Result<(Option<Pubkey>, Option<Pubkey>)> {
160        let res = match (
161            self.initial_long_token,
162            self.initial_long_token_amount,
163            self.initial_short_token,
164            self.initial_short_token_amount,
165        ) {
166            (Some(long_token), _, Some(short_token), _) => (Some(long_token), Some(short_token)),
167            (_, 0, _, 0) => {
168                return Err(crate::Error::EmptyDeposit);
169            }
170            (None, 0, Some(short_token), _) => (None, Some(short_token)),
171            (Some(long_token), _, None, 0) => (Some(long_token), None),
172            (mut long_token, long_amount, mut short_token, short_amount) => {
173                debug_assert!(
174                    (long_token.is_none() && long_amount != 0)
175                        || (short_token.is_none() && short_amount != 0)
176                );
177                let market = self.client.market(market).await?;
178                if long_amount != 0 && long_token.is_none() {
179                    long_token = Some(market.meta().long_token_mint);
180                }
181                if short_amount != 0 && short_token.is_none() {
182                    short_token = Some(market.meta().short_token_mint);
183                }
184                (long_token, short_token)
185            }
186        };
187        Ok(res)
188    }
189
190    /// Set the initial long token params for deposit.
191    pub fn long_token(
192        &mut self,
193        amount: u64,
194        token: Option<&Pubkey>,
195        token_account: Option<&Pubkey>,
196    ) -> &mut Self {
197        self.initial_long_token = token.copied();
198        self.initial_long_token_amount = amount;
199        self.initial_long_token_account = token_account.copied();
200        self
201    }
202
203    /// Set the initial short token params for deposit.
204    pub fn short_token(
205        &mut self,
206        amount: u64,
207        token: Option<&Pubkey>,
208        token_account: Option<&Pubkey>,
209    ) -> &mut Self {
210        self.initial_short_token = token.cloned();
211        self.initial_short_token_amount = amount;
212        self.initial_short_token_account = token_account.copied();
213        self
214    }
215
216    /// Build a [`TransactionBuilder`] and return deposit address.
217    pub async fn build_with_address(&self) -> crate::Result<(TransactionBuilder<'a, C>, Pubkey)> {
218        let token_program_id = anchor_spl::token::ID;
219        let Self {
220            client,
221            store,
222            nonce,
223            market_token,
224            execution_fee,
225            long_token_swap_path,
226            short_token_swap_path,
227            initial_long_token_amount,
228            initial_short_token_amount,
229            min_market_token,
230            should_unwrap_native_token,
231            ..
232        } = self;
233        let nonce = nonce.unwrap_or_else(generate_nonce);
234        let owner = client.payer();
235        let receiver = self.get_receiver();
236        let deposit = client.find_deposit_address(store, &owner, &nonce);
237        let market = client.find_market_address(store, market_token);
238
239        let (long_token, short_token) = self.get_or_fetch_initial_tokens(&market).await?;
240
241        let initial_long_token_account =
242            self.get_or_find_associated_initial_long_token_account(long_token.as_ref());
243        let initial_short_token_account =
244            self.get_or_find_associated_initial_short_token_account(short_token.as_ref());
245        let market_token_ata = get_associated_token_address(&receiver, market_token);
246
247        let market_token_escrow = get_associated_token_address(&deposit, market_token);
248        let initial_long_token_escrow = long_token
249            .as_ref()
250            .map(|mint| get_associated_token_address(&deposit, mint));
251        let initial_short_token_escrow = short_token
252            .as_ref()
253            .map(|mint| get_associated_token_address(&deposit, mint));
254
255        let mut prepare = client.prepare_associated_token_account(
256            market_token,
257            &token_program_id,
258            Some(&deposit),
259        );
260
261        for token in long_token.iter().chain(short_token.iter()) {
262            prepare = prepare.merge(client.prepare_associated_token_account(
263                token,
264                &token_program_id,
265                Some(&deposit),
266            ));
267        }
268
269        let create = client
270            .store_transaction()
271            .accounts(crate::utils::fix_optional_account_metas(
272                accounts::CreateDeposit {
273                    owner,
274                    receiver,
275                    store: *store,
276                    market,
277                    deposit,
278                    market_token: *market_token,
279                    initial_long_token: long_token,
280                    initial_short_token: short_token,
281                    market_token_ata,
282                    market_token_escrow,
283                    initial_long_token_escrow,
284                    initial_short_token_escrow,
285                    initial_long_token_source: initial_long_token_account,
286                    initial_short_token_source: initial_short_token_account,
287                    system_program: system_program::ID,
288                    token_program: token_program_id,
289                    associated_token_program: anchor_spl::associated_token::ID,
290                },
291                &gmsol_store::id(),
292                client.store_program_id(),
293            ))
294            .anchor_args(instruction::CreateDeposit {
295                nonce,
296                params: CreateDepositParams {
297                    execution_lamports: *execution_fee,
298                    long_token_swap_length: long_token_swap_path
299                        .len()
300                        .try_into()
301                        .map_err(|_| crate::Error::NumberOutOfRange)?,
302                    short_token_swap_length: short_token_swap_path
303                        .len()
304                        .try_into()
305                        .map_err(|_| crate::Error::NumberOutOfRange)?,
306                    initial_long_token_amount: *initial_long_token_amount,
307                    initial_short_token_amount: *initial_short_token_amount,
308                    min_market_token_amount: *min_market_token,
309                    should_unwrap_native_token: *should_unwrap_native_token,
310                },
311            })
312            .accounts(
313                long_token_swap_path
314                    .iter()
315                    .enumerate()
316                    .map(|(idx, mint)| AccountMeta {
317                        pubkey: client.find_market_address(store, mint),
318                        is_signer: false,
319                        is_writable: idx == 0,
320                    })
321                    .chain(short_token_swap_path.iter().enumerate().map(|(idx, mint)| {
322                        AccountMeta {
323                            pubkey: client.find_market_address(store, mint),
324                            is_signer: false,
325                            is_writable: idx == 0,
326                        }
327                    }))
328                    .collect::<Vec<_>>(),
329            );
330        let builder = prepare.merge(create);
331        Ok((builder, deposit))
332    }
333}
334
335/// Close Deposit Builder.
336pub struct CloseDepositBuilder<'a, C> {
337    client: &'a crate::Client<C>,
338    store: Pubkey,
339    deposit: Pubkey,
340    reason: String,
341    hint: Option<CloseDepositHint>,
342}
343
344/// Close Deposit Hint.
345#[derive(Debug, Clone, Copy)]
346pub struct CloseDepositHint {
347    owner: Pubkey,
348    receiver: Pubkey,
349    market_token: Pubkey,
350    market_token_account: Pubkey,
351    initial_long_token: Option<Pubkey>,
352    initial_short_token: Option<Pubkey>,
353    initial_long_token_account: Option<Pubkey>,
354    initial_short_token_account: Option<Pubkey>,
355    should_unwrap_native_token: bool,
356}
357
358impl CloseDepositHint {
359    /// Create from deposit.
360    pub fn new(deposit: &Deposit) -> Self {
361        Self {
362            owner: *deposit.header().owner(),
363            receiver: deposit.header().receiver(),
364            market_token: deposit.tokens().market_token(),
365            market_token_account: deposit.tokens().market_token_account(),
366            initial_long_token: deposit.tokens().initial_long_token.token(),
367            initial_short_token: deposit.tokens().initial_short_token.token(),
368            initial_long_token_account: deposit.tokens().initial_long_token.account(),
369            initial_short_token_account: deposit.tokens().initial_short_token.account(),
370            should_unwrap_native_token: deposit.header().should_unwrap_native_token(),
371        }
372    }
373}
374
375impl<'a, S, C> CloseDepositBuilder<'a, C>
376where
377    C: Deref<Target = S> + Clone,
378    S: Signer,
379{
380    pub(super) fn new(client: &'a crate::Client<C>, store: &Pubkey, deposit: &Pubkey) -> Self {
381        Self {
382            client,
383            store: *store,
384            deposit: *deposit,
385            reason: "cancelled".to_string(),
386            hint: None,
387        }
388    }
389
390    /// Set hint with the given deposit.
391    pub fn hint_with_deposit(&mut self, deposit: &Deposit) -> &mut Self {
392        self.hint(CloseDepositHint::new(deposit))
393    }
394
395    /// Set hint.
396    pub fn hint(&mut self, hint: CloseDepositHint) -> &mut Self {
397        self.hint = Some(hint);
398        self
399    }
400
401    /// Set the close reason.
402    pub fn reason(&mut self, reason: impl ToString) -> &mut Self {
403        self.reason = reason.to_string();
404        self
405    }
406
407    async fn get_or_fetch_deposit_info(&self) -> crate::Result<CloseDepositHint> {
408        match &self.hint {
409            Some(hint) => Ok(*hint),
410            None => {
411                let deposit = self.client.deposit(&self.deposit).await?;
412                Ok(CloseDepositHint::new(&deposit))
413            }
414        }
415    }
416
417    /// Build a [`TransactionBuilder`] for `cancel_deposit` instruction.
418    pub async fn build(&self) -> crate::Result<TransactionBuilder<'a, C>> {
419        let executor = self.client.payer();
420        let hint = self.get_or_fetch_deposit_info().await?;
421        let Self {
422            client,
423            store,
424            deposit,
425            ..
426        } = self;
427        let owner = hint.owner;
428        let receiver = hint.receiver;
429        let market_token_ata = get_associated_token_address(&receiver, &hint.market_token);
430        let should_unwrap_native_token = hint.should_unwrap_native_token;
431        let initial_long_token_ata = hint
432            .initial_long_token
433            .as_ref()
434            .map(|mint| get_ata_or_owner(&owner, mint, should_unwrap_native_token));
435        let initial_short_token_ata = hint
436            .initial_short_token
437            .as_ref()
438            .map(|mint| get_ata_or_owner(&owner, mint, should_unwrap_native_token));
439        Ok(client
440            .store_transaction()
441            .accounts(crate::utils::fix_optional_account_metas(
442                accounts::CloseDeposit {
443                    executor,
444                    store: *store,
445                    store_wallet: client.find_store_wallet_address(store),
446                    owner,
447                    receiver,
448                    market_token: hint.market_token,
449                    initial_long_token: hint.initial_long_token,
450                    initial_short_token: hint.initial_short_token,
451                    deposit: *deposit,
452                    market_token_escrow: hint.market_token_account,
453                    initial_long_token_escrow: hint.initial_long_token_account,
454                    initial_short_token_escrow: hint.initial_short_token_account,
455                    market_token_ata,
456                    initial_long_token_ata,
457                    initial_short_token_ata,
458                    associated_token_program: anchor_spl::associated_token::ID,
459                    token_program: anchor_spl::token::ID,
460                    system_program: system_program::ID,
461                    event_authority: client.store_event_authority(),
462                    program: *client.store_program_id(),
463                },
464                &gmsol_store::id(),
465                client.store_program_id(),
466            ))
467            .anchor_args(instruction::CloseDeposit {
468                reason: self.reason.clone(),
469            }))
470    }
471}
472
473/// Execute Deposit Builder.
474pub struct ExecuteDepositBuilder<'a, C> {
475    client: &'a crate::Client<C>,
476    store: Pubkey,
477    oracle: Pubkey,
478    deposit: Pubkey,
479    execution_fee: u64,
480    feeds_parser: FeedsParser,
481    hint: Option<ExecuteDepositHint>,
482    token_map: Option<Pubkey>,
483    cancel_on_execution_error: bool,
484    close: bool,
485}
486
487/// Hint for executing deposit.
488#[derive(Clone, Debug)]
489pub struct ExecuteDepositHint {
490    owner: Pubkey,
491    receiver: Pubkey,
492    market_token_escrow: Pubkey,
493    market_token_mint: Pubkey,
494    /// Feeds.
495    pub feeds: TokensWithFeed,
496    swap: SwapActionParams,
497    initial_long_token_escrow: Option<Pubkey>,
498    initial_short_token_escrow: Option<Pubkey>,
499    initial_long_token: Option<Pubkey>,
500    initial_short_token: Option<Pubkey>,
501    should_unwrap_native_token: bool,
502}
503
504impl ExecuteDepositHint {
505    /// Create a new hint for the deposit.
506    pub fn new(deposit: &Deposit, map: &impl TokenMapAccess) -> crate::Result<Self> {
507        Ok(Self {
508            owner: *deposit.header().owner(),
509            receiver: deposit.header().receiver(),
510            market_token_escrow: deposit.tokens().market_token_account(),
511            market_token_mint: deposit.tokens().market_token(),
512            feeds: deposit.swap().to_feeds(map)?,
513            swap: *deposit.swap(),
514            initial_long_token: deposit.tokens().initial_long_token.token(),
515            initial_short_token: deposit.tokens().initial_short_token.token(),
516            initial_long_token_escrow: deposit.tokens().initial_long_token.account(),
517            initial_short_token_escrow: deposit.tokens().initial_short_token.account(),
518            should_unwrap_native_token: deposit.header().should_unwrap_native_token(),
519        })
520    }
521}
522
523impl<'a, S, C> ExecuteDepositBuilder<'a, C>
524where
525    C: Deref<Target = S> + Clone,
526    S: Signer,
527{
528    pub(super) fn new(
529        client: &'a crate::Client<C>,
530        store: &Pubkey,
531        oracle: &Pubkey,
532        deposit: &Pubkey,
533        cancel_on_execution_error: bool,
534    ) -> Self {
535        Self {
536            client,
537            store: *store,
538            oracle: *oracle,
539            deposit: *deposit,
540            execution_fee: 0,
541            hint: None,
542            feeds_parser: Default::default(),
543            token_map: None,
544            cancel_on_execution_error,
545            close: true,
546        }
547    }
548
549    /// Set whether to close the deposit after execution.
550    pub fn close(&mut self, close: bool) -> &mut Self {
551        self.close = close;
552        self
553    }
554
555    /// Set hint with the given deposit.
556    pub fn hint(
557        &mut self,
558        deposit: &Deposit,
559        map: &impl TokenMapAccess,
560    ) -> crate::Result<&mut Self> {
561        self.hint = Some(ExecuteDepositHint::new(deposit, map)?);
562        Ok(self)
563    }
564
565    /// Parse feeds with the given price udpates map.
566    #[cfg(feature = "pyth-pull-oracle")]
567    pub fn parse_with_pyth_price_updates(&mut self, price_updates: Prices) -> &mut Self {
568        self.feeds_parser.with_pyth_price_updates(price_updates);
569        self
570    }
571
572    /// Prepare [`ExecuteDepositHint`].
573    pub async fn prepare_hint(&mut self) -> crate::Result<ExecuteDepositHint> {
574        match &self.hint {
575            Some(hint) => Ok(hint.clone()),
576            None => {
577                let map = self.client.authorized_token_map(&self.store).await?;
578                let deposit = self.client.deposit(&self.deposit).await?;
579                let hint = ExecuteDepositHint::new(&deposit, &map)?;
580                self.hint = Some(hint.clone());
581                Ok(hint)
582            }
583        }
584    }
585
586    async fn get_token_map(&self) -> crate::Result<Pubkey> {
587        if let Some(address) = self.token_map {
588            Ok(address)
589        } else {
590            Ok(self
591                .client
592                .authorized_token_map_address(&self.store)
593                .await?
594                .ok_or(crate::Error::NotFound)?)
595        }
596    }
597
598    /// Set token map.
599    pub fn token_map(&mut self, address: Pubkey) -> &mut Self {
600        self.token_map = Some(address);
601        self
602    }
603}
604
605impl<'a, C: Deref<Target = impl Signer> + Clone> MakeBundleBuilder<'a, C>
606    for ExecuteDepositBuilder<'a, C>
607{
608    async fn build_with_options(
609        &mut self,
610        options: BundleOptions,
611    ) -> gmsol_solana_utils::Result<BundleBuilder<'a, C>> {
612        let token_map = self
613            .get_token_map()
614            .await
615            .map_err(gmsol_solana_utils::Error::custom)?;
616        let hint = self
617            .prepare_hint()
618            .await
619            .map_err(gmsol_solana_utils::Error::custom)?;
620        let Self {
621            client,
622            store,
623            oracle,
624            deposit,
625            execution_fee,
626            cancel_on_execution_error,
627            ..
628        } = &self;
629        let authority = client.payer();
630        let feeds = self
631            .feeds_parser
632            .parse(&hint.feeds)
633            .collect::<Result<Vec<_>, _>>()
634            .map_err(gmsol_solana_utils::Error::custom)?;
635        let markets = hint
636            .swap
637            .unique_market_tokens_excluding_current(&hint.market_token_mint)
638            .map(|mint| AccountMeta {
639                pubkey: client.find_market_address(store, mint),
640                is_signer: false,
641                is_writable: true,
642            });
643
644        // Execution.
645        let execute = client
646            .store_transaction()
647            .accounts(crate::utils::fix_optional_account_metas(
648                accounts::ExecuteDeposit {
649                    authority,
650                    store: *store,
651                    oracle: *oracle,
652                    token_map,
653                    deposit: *deposit,
654                    market: client.find_market_address(store, &hint.market_token_mint),
655                    market_token: hint.market_token_mint,
656                    initial_long_token_vault: hint
657                        .initial_long_token
658                        .as_ref()
659                        .map(|token| client.find_market_vault_address(store, token)),
660                    initial_short_token_vault: hint
661                        .initial_short_token
662                        .as_ref()
663                        .map(|token| client.find_market_vault_address(store, token)),
664                    token_program: anchor_spl::token::ID,
665                    system_program: system_program::ID,
666                    initial_long_token: hint.initial_long_token,
667                    initial_short_token: hint.initial_short_token,
668                    market_token_escrow: hint.market_token_escrow,
669                    initial_long_token_escrow: hint.initial_long_token_escrow,
670                    initial_short_token_escrow: hint.initial_short_token_escrow,
671                    chainlink_program: None,
672                    event_authority: client.store_event_authority(),
673                    program: *client.store_program_id(),
674                },
675                &gmsol_store::ID,
676                self.client.store_program_id(),
677            ))
678            .anchor_args(instruction::ExecuteDeposit {
679                execution_fee: *execution_fee,
680                throw_on_execution_error: !*cancel_on_execution_error,
681            })
682            .accounts(feeds.into_iter().chain(markets).collect::<Vec<_>>())
683            .compute_budget(ComputeBudget::default().with_limit(EXECUTE_DEPOSIT_COMPUTE_BUDGET));
684
685        let rpc = if self.close {
686            let close = self
687                .client
688                .close_deposit(store, deposit)
689                .hint(CloseDepositHint {
690                    owner: hint.owner,
691                    receiver: hint.receiver,
692                    market_token: hint.market_token_mint,
693                    market_token_account: hint.market_token_escrow,
694                    initial_long_token: hint.initial_long_token,
695                    initial_short_token: hint.initial_short_token,
696                    initial_long_token_account: hint.initial_long_token_escrow,
697                    initial_short_token_account: hint.initial_short_token_escrow,
698                    should_unwrap_native_token: hint.should_unwrap_native_token,
699                })
700                .reason("executed")
701                .build()
702                .await
703                .map_err(gmsol_solana_utils::Error::custom)?;
704            execute.merge(close)
705        } else {
706            execute
707        };
708
709        let mut tx = self.client.bundle_with_options(options);
710
711        tx.try_push(rpc)?;
712
713        Ok(tx)
714    }
715}
716
717impl<C: Deref<Target = impl Signer> + Clone> PullOraclePriceConsumer
718    for ExecuteDepositBuilder<'_, C>
719{
720    async fn feed_ids(&mut self) -> crate::Result<FeedIds> {
721        let hint = self.prepare_hint().await?;
722        Ok(FeedIds::new(self.store, hint.feeds))
723    }
724
725    fn process_feeds(
726        &mut self,
727        provider: PriceProviderKind,
728        map: FeedAddressMap,
729    ) -> crate::Result<()> {
730        self.feeds_parser
731            .insert_pull_oracle_feed_parser(provider, map);
732        Ok(())
733    }
734}
735
736impl<C> SetExecutionFee for ExecuteDepositBuilder<'_, C> {
737    fn set_execution_fee(&mut self, lamports: u64) -> &mut Self {
738        self.execution_fee = lamports;
739        self
740    }
741}