gmsol/exchange/
withdrawal.rs

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