1use std::{collections::HashMap, ops::Deref};
2
3use anchor_client::solana_sdk::{pubkey::Pubkey, signer::Signer};
4use gmsol_solana_utils::{
5 bundle_builder::{BundleBuilder, BundleOptions},
6 transaction_builder::TransactionBuilder,
7};
8use gmsol_store::states::{common::TokensWithFeed, Market, PriceProviderKind};
9use solana_sdk::address_lookup_table::AddressLookupTableAccount;
10
11use crate::{
12 store::utils::FeedsParser,
13 utils::{
14 builder::{
15 FeedAddressMap, FeedIds, MakeBundleBuilder, PullOraclePriceConsumer, SetExecutionFee,
16 },
17 fix_optional_account_metas,
18 },
19};
20
21pub const ADL_COMPUTE_BUDGET: u32 = 800_000;
23
24pub struct UpdateAdlBuilder<'a, C> {
26 client: &'a crate::Client<C>,
27 store: Pubkey,
28 market_token: Pubkey,
29 oracle: Pubkey,
30 for_long: bool,
31 for_short: bool,
32 hint: Option<UpdateAdlHint>,
33 feeds_parser: FeedsParser,
34 alts: HashMap<Pubkey, Vec<Pubkey>>,
35}
36
37impl<'a, C: Deref<Target = impl Signer> + Clone> UpdateAdlBuilder<'a, C> {
38 pub(super) fn try_new(
39 client: &'a crate::Client<C>,
40 store: &Pubkey,
41 oracle: &Pubkey,
42 market_token: &Pubkey,
43 for_long: bool,
44 for_short: bool,
45 ) -> crate::Result<Self> {
46 Ok(Self {
47 client,
48 store: *store,
49 market_token: *market_token,
50 oracle: *oracle,
51 for_long,
52 for_short,
53 hint: None,
54 feeds_parser: FeedsParser::default(),
55 alts: Default::default(),
56 })
57 }
58
59 pub fn add_alt(&mut self, account: AddressLookupTableAccount) -> &mut Self {
61 self.alts.insert(account.key, account.addresses);
62 self
63 }
64
65 pub async fn prepare_hint(&mut self) -> crate::Result<UpdateAdlHint> {
67 match &self.hint {
68 Some(hint) => Ok(hint.clone()),
69 None => {
70 let market_address = self
71 .client
72 .find_market_address(&self.store, &self.market_token);
73 let market = self.client.market(&market_address).await?;
74 let hint = UpdateAdlHint::from_market(self.client, &market).await?;
75 self.hint = Some(hint.clone());
76 Ok(hint)
77 }
78 }
79 }
80
81 pub async fn build_txns(&mut self) -> crate::Result<Vec<TransactionBuilder<'a, C>>> {
83 let hint = self.prepare_hint().await?;
84 let feeds = self
85 .feeds_parser
86 .parse(hint.feeds())
87 .collect::<Result<Vec<_>, _>>()?;
88
89 let mut txns = vec![];
90
91 let sides = self
92 .for_long
93 .then_some(true)
94 .into_iter()
95 .chain(self.for_short.then_some(false));
96
97 for is_long in sides {
98 let rpc = self
99 .client
100 .store_transaction()
101 .accounts(fix_optional_account_metas(
102 gmsol_store::accounts::UpdateAdlState {
103 authority: self.client.payer(),
104 store: self.store,
105 token_map: hint.token_map,
106 oracle: self.oracle,
107 market: self
108 .client
109 .find_market_address(&self.store, &self.market_token),
110 chainlink_program: None,
111 },
112 &crate::program_ids::DEFAULT_GMSOL_STORE_ID,
113 self.client.store_program_id(),
114 ))
115 .anchor_args(gmsol_store::instruction::UpdateAdlState { is_long })
116 .accounts(feeds.clone())
117 .lookup_tables(self.alts.clone());
118
119 txns.push(rpc);
120 }
121
122 Ok(txns)
123 }
124}
125
126#[derive(Clone)]
128pub struct UpdateAdlHint {
129 token_map: Pubkey,
130 tokens_with_feed: TokensWithFeed,
131}
132
133impl UpdateAdlHint {
134 async fn from_market<C: Deref<Target = impl Signer> + Clone>(
135 client: &crate::Client<C>,
136 market: &Market,
137 ) -> crate::Result<Self> {
138 use gmsol_store::states::common::token_with_feeds::token_records;
139
140 let store_address = market.store;
141 let token_map_address = client
142 .authorized_token_map_address(&store_address)
143 .await?
144 .ok_or(crate::Error::invalid_argument(
145 "token map is not configurated for the store",
146 ))?;
147 let token_map = client.token_map(&token_map_address).await?;
148 let meta = market.meta();
149
150 let records = token_records(
151 &token_map,
152 &[
153 meta.index_token_mint,
154 meta.long_token_mint,
155 meta.short_token_mint,
156 ]
157 .into(),
158 )?;
159 let tokens_with_feed = TokensWithFeed::try_from_records(records)?;
160
161 Ok(Self {
162 token_map: token_map_address,
163 tokens_with_feed,
164 })
165 }
166
167 pub fn feeds(&self) -> &TokensWithFeed {
169 &self.tokens_with_feed
170 }
171}
172
173impl<'a, C: Deref<Target = impl Signer> + Clone> MakeBundleBuilder<'a, C>
174 for UpdateAdlBuilder<'a, C>
175{
176 async fn build_with_options(
177 &mut self,
178 options: BundleOptions,
179 ) -> gmsol_solana_utils::Result<BundleBuilder<'a, C>> {
180 let mut bundle = self.client.bundle_with_options(options);
181
182 bundle.push_many(
183 self.build_txns()
184 .await
185 .map_err(gmsol_solana_utils::Error::custom)?,
186 false,
187 )?;
188
189 Ok(bundle)
190 }
191}
192
193impl<C: Deref<Target = impl Signer> + Clone> PullOraclePriceConsumer for UpdateAdlBuilder<'_, C> {
194 async fn feed_ids(&mut self) -> crate::Result<FeedIds> {
195 let hint = self.prepare_hint().await?;
196 Ok(FeedIds::new(self.store, hint.tokens_with_feed))
197 }
198
199 fn process_feeds(
200 &mut self,
201 provider: PriceProviderKind,
202 map: FeedAddressMap,
203 ) -> crate::Result<()> {
204 self.feeds_parser
205 .insert_pull_oracle_feed_parser(provider, map);
206 Ok(())
207 }
208}
209
210impl<C> SetExecutionFee for UpdateAdlBuilder<'_, C> {
211 fn is_execution_fee_estimation_required(&self) -> bool {
212 false
213 }
214
215 fn set_execution_fee(&mut self, _lamports: u64) -> &mut Self {
216 self
217 }
218}