drift_rs/math/
account_list_builder.rs1use ahash::{HashMap, HashMapExt};
2use arrayvec::ArrayVec;
3use solana_sdk::{account::Account, pubkey::Pubkey};
4
5use crate::{
6 accounts::State,
7 constants::{self, oracle_source_to_owner, state_account},
8 ffi::{AccountWithKey, AccountsList},
9 types::accounts::User,
10 utils::zero_account_to_bytes,
11 DriftClient, MarketId, SdkError, SdkResult,
12};
13
14#[derive(Default)]
21pub struct AccountsListBuilder {
22 perp_accounts: ArrayVec<AccountWithKey, 16>,
24 spot_accounts: ArrayVec<AccountWithKey, 16>,
25 oracle_accounts: ArrayVec<AccountWithKey, 16>,
26}
27
28impl AccountsListBuilder {
29 pub fn try_build(
37 &mut self,
38 client: &DriftClient,
39 user: &User,
40 force_markets: &[MarketId],
41 ) -> SdkResult<AccountsList> {
42 let mut oracle_markets = HashMap::<Pubkey, MarketId>::with_capacity(16);
43 let drift_state_account = client.try_get_account::<State>(state_account())?;
44
45 let force_spot_iter = force_markets
46 .iter()
47 .filter(|m| m.is_spot())
48 .map(|m| m.index());
49
50 let spot_market_idxs = ahash::HashSet::from_iter(
51 user.spot_positions
52 .iter()
53 .filter(|p| !p.is_available())
54 .map(|p| p.market_index)
55 .chain(force_spot_iter)
56 .chain(std::iter::once(MarketId::QUOTE_SPOT.index())),
57 );
58
59 for idx in spot_market_idxs {
60 let market = client.try_get_spot_market_account(idx)?;
61 oracle_markets.insert(market.oracle, MarketId::spot(market.market_index));
62 self.spot_accounts.push(
63 (
64 market.pubkey,
65 Account {
66 data: zero_account_to_bytes(market),
67 owner: constants::PROGRAM_ID,
68 ..Default::default()
69 },
70 )
71 .into(),
72 );
73 }
74
75 let force_perp_iter = force_markets
76 .iter()
77 .filter(|m| m.is_perp())
78 .map(|m| m.index());
79 let perp_market_idxs = ahash::HashSet::from_iter(
80 user.perp_positions
81 .iter()
82 .filter(|p| !p.is_available())
83 .map(|p| p.market_index)
84 .chain(force_perp_iter),
85 );
86
87 for idx in perp_market_idxs {
88 let market = client.try_get_perp_market_account(idx)?;
89 oracle_markets.insert(market.amm.oracle, MarketId::perp(market.market_index));
90 self.perp_accounts.push(
91 (
92 market.pubkey,
93 Account {
94 data: zero_account_to_bytes(market),
95 owner: constants::PROGRAM_ID,
96 ..Default::default()
97 },
98 )
99 .into(),
100 );
101 }
102
103 let mut latest_oracle_slot = 0;
104 for (oracle_key, market) in oracle_markets.iter() {
105 let oracle = client
106 .try_get_oracle_price_data_and_slot(*market)
107 .ok_or(SdkError::NoMarketData(*market))?;
108
109 latest_oracle_slot = oracle.slot.max(latest_oracle_slot);
110 let oracle_owner = oracle_source_to_owner(client.context, oracle.source);
111 self.oracle_accounts.push(
112 (
113 *oracle_key,
114 Account {
115 data: oracle.raw,
116 owner: oracle_owner,
117 ..Default::default()
118 },
119 )
120 .into(),
121 );
122 }
123
124 Ok(AccountsList {
125 perp_markets: self.perp_accounts.as_mut_slice(),
126 spot_markets: self.spot_accounts.as_mut_slice(),
127 oracles: self.oracle_accounts.as_mut_slice(),
128 oracle_guard_rails: Some(drift_state_account.oracle_guard_rails),
129 latest_slot: latest_oracle_slot,
130 })
131 }
132
133 pub async fn build(
143 &mut self,
144 client: &DriftClient,
145 user: &User,
146 force_markets: &[MarketId],
147 ) -> SdkResult<AccountsList> {
148 let mut oracle_markets = HashMap::<Pubkey, MarketId>::with_capacity(16);
149 let drift_state_account = client.try_get_account::<State>(state_account())?;
150
151 let force_spot_iter = force_markets
153 .iter()
154 .filter(|m| m.is_spot())
155 .map(|m| m.index());
156 let spot_market_idxs = ahash::HashSet::from_iter(
157 user.spot_positions
158 .iter()
159 .filter(|p| !p.is_available())
160 .map(|p| p.market_index)
161 .chain(force_spot_iter)
162 .chain(std::iter::once(MarketId::QUOTE_SPOT.index())),
163 );
164
165 for market_idx in spot_market_idxs.iter() {
166 let market = client.get_spot_market_account(*market_idx).await?;
167 oracle_markets.insert(market.oracle, MarketId::spot(market.market_index));
168
169 self.spot_accounts.push(
170 (
171 market.pubkey,
172 Account {
173 data: zero_account_to_bytes(market),
174 owner: constants::PROGRAM_ID,
175 ..Default::default()
176 },
177 )
178 .into(),
179 );
180 }
181
182 let force_perp_iter = force_markets
183 .iter()
184 .filter(|m| m.is_perp())
185 .map(|m| m.index());
186 let perp_market_idxs = ahash::HashSet::from_iter(
187 user.perp_positions
188 .iter()
189 .filter(|p| !p.is_available())
190 .map(|p| p.market_index)
191 .chain(force_perp_iter),
192 );
193
194 for market_idx in perp_market_idxs.iter() {
195 let market = client.get_perp_market_account(*market_idx).await?;
196 oracle_markets.insert(market.amm.oracle, MarketId::perp(market.market_index));
197
198 self.perp_accounts.push(
199 (
200 market.pubkey,
201 Account {
202 data: zero_account_to_bytes(market),
203 owner: constants::PROGRAM_ID,
204 ..Default::default()
205 },
206 )
207 .into(),
208 );
209 }
210
211 let mut latest_oracle_slot = 0;
212 for (oracle_key, market) in oracle_markets.iter() {
213 let oracle = client.get_oracle_price_data_and_slot(*market).await?;
214
215 latest_oracle_slot = oracle.slot.max(latest_oracle_slot);
216 let oracle_owner = oracle_source_to_owner(client.context, oracle.source);
217 self.oracle_accounts.push(
218 (
219 *oracle_key,
220 Account {
221 data: oracle.raw,
222 owner: oracle_owner,
223 ..Default::default()
224 },
225 )
226 .into(),
227 );
228 }
229
230 Ok(AccountsList {
231 perp_markets: self.perp_accounts.as_mut_slice(),
232 spot_markets: self.spot_accounts.as_mut_slice(),
233 oracles: self.oracle_accounts.as_mut_slice(),
234 oracle_guard_rails: Some(drift_state_account.oracle_guard_rails),
235 latest_slot: latest_oracle_slot,
236 })
237 }
238}