1use std::{
4 borrow::Cow,
5 collections::BTreeSet,
6 sync::{Arc, RwLock},
7 time::Duration,
8};
9
10use anchor_lang::{AccountDeserialize, Discriminator, InstructionData};
11pub use drift_pubsub_client::PubsubClient;
12use futures_util::TryFutureExt;
13use log::debug;
14pub use solana_rpc_client::nonblocking::rpc_client::RpcClient;
15use solana_rpc_client_api::response::Response;
16use solana_sdk::{
17 account::Account,
18 clock::Slot,
19 commitment_config::CommitmentLevel,
20 compute_budget::ComputeBudgetInstruction,
21 hash::Hash,
22 instruction::{AccountMeta, Instruction},
23 message::{v0, Message, VersionedMessage},
24 signature::Signature,
25};
26pub use solana_sdk::{address_lookup_table::AddressLookupTableAccount, pubkey::Pubkey};
27
28use crate::{
29 account_map::AccountMap,
30 blockhash_subscriber::BlockhashSubscriber,
31 constants::{
32 derive_perp_market_account, derive_spot_market_account, state_account, MarketExt,
33 ProgramData, DEFAULT_PUBKEY, SYSVAR_INSTRUCTIONS_PUBKEY,
34 },
35 drift_idl::traits::ToAccountMetas,
36 grpc::grpc_subscriber::{AccountFilter, DriftGrpcClient, GeyserSubscribeOpts},
37 marketmap::MarketMap,
38 oraclemap::{Oracle, OracleMap},
39 swift_order_subscriber::{SignedOrderInfo, SwiftOrderStream},
40 types::{
41 accounts::{PerpMarket, SpotMarket, State, User, UserStats},
42 DataAndSlot, MarketType, *,
43 },
44 utils::{get_http_url, get_ws_url},
45};
46pub use crate::{grpc::GrpcSubscribeOpts, types::Context, wallet::Wallet};
47
48pub mod async_utils;
50pub mod ffi;
51pub mod math;
52pub mod memcmp;
53pub mod utils;
54pub mod wallet;
55
56pub mod constants;
58pub mod drift_idl;
59pub mod types;
60
61pub mod grpc;
63pub mod polled_account_subscriber;
64pub mod websocket_account_subscriber;
65pub mod websocket_program_account_subscriber;
66
67pub mod auction_subscriber;
69pub mod blockhash_subscriber;
70pub mod event_subscriber;
71pub mod priority_fee_subscriber;
72pub mod swift_order_subscriber;
73
74pub mod jit_client;
75
76pub mod account_map;
77pub mod marketmap;
78pub mod oraclemap;
79pub mod slot_subscriber;
80pub mod usermap;
81
82#[cfg(feature = "dlob")]
83pub mod dlob;
84
85#[derive(Clone)]
113#[must_use]
114pub struct DriftClient {
115 pub context: Context,
116 backend: &'static DriftClientBackend,
117 pub wallet: Wallet,
118}
119
120impl DriftClient {
121 pub async fn new(context: Context, rpc_client: RpcClient, wallet: Wallet) -> SdkResult<Self> {
127 let _ = get_http_url(&rpc_client.url())?;
129 Ok(Self {
130 backend: Box::leak(Box::new(
131 DriftClientBackend::new(context, Arc::new(rpc_client)).await?,
132 )),
133 context,
134 wallet,
135 })
136 }
137
138 pub async fn subscribe_blockhashes(&self) -> SdkResult<()> {
142 self.backend.subscribe_blockhashes().await
143 }
144
145 pub async fn subscribe_markets(&self, markets: &[MarketId]) -> SdkResult<()> {
151 self.backend.subscribe_markets(markets).await
152 }
153
154 pub async fn subscribe_all_markets(&self) -> SdkResult<()> {
158 let markets = self.get_all_market_ids();
159 self.backend.subscribe_markets(&markets).await
160 }
161
162 pub async fn subscribe_all_spot_markets(&self) -> SdkResult<()> {
166 let markets = self.get_all_spot_market_ids();
167 self.backend.subscribe_markets(&markets).await
168 }
169
170 pub async fn subscribe_all_perp_markets(&self) -> SdkResult<()> {
174 let markets = self.get_all_perp_market_ids();
175 self.backend.subscribe_markets(&markets).await
176 }
177
178 pub async fn subscribe_oracles(&self, markets: &[MarketId]) -> SdkResult<()> {
184 self.backend.subscribe_oracles(markets).await
185 }
186
187 pub async fn subscribe_all_oracles(&self) -> SdkResult<()> {
191 let markets = self.get_all_market_ids();
192 self.backend.subscribe_oracles(&markets).await
193 }
194
195 pub async fn subscribe_all_spot_oracles(&self) -> SdkResult<()> {
199 let markets = self.get_all_spot_market_ids();
200 self.backend.subscribe_oracles(&markets).await
201 }
202
203 pub async fn subscribe_all_perp_oracles(&self) -> SdkResult<()> {
207 let markets = self.get_all_perp_market_ids();
208 self.backend.subscribe_oracles(&markets).await
209 }
210
211 pub async fn subscribe_swift_orders(
217 &self,
218 markets: &[MarketId],
219 ) -> SdkResult<SwiftOrderStream> {
220 swift_order_subscriber::subscribe_swift_orders(self, markets).await
221 }
222
223 pub fn get_all_spot_market_ids(&self) -> Vec<MarketId> {
227 self.program_data()
228 .spot_market_configs()
229 .iter()
230 .filter_map(|m| match m.status {
231 MarketStatus::Settlement | MarketStatus::Delisted => {
232 log::debug!("ignoring settled/delisted spot market: {}", m.market_index);
233 None
234 }
235 _ => Some(MarketId::spot(m.market_index)),
236 })
237 .collect()
238 }
239
240 pub fn get_all_perp_market_ids(&self) -> Vec<MarketId> {
244 self.program_data()
245 .perp_market_configs()
246 .iter()
247 .filter_map(|m| match m.status {
248 MarketStatus::Settlement | MarketStatus::Delisted => {
249 log::debug!("ignoring settled/delisted perp market: {}", m.market_index);
250 None
251 }
252 _ => Some(MarketId::perp(m.market_index)),
253 })
254 .collect()
255 }
256
257 pub fn get_all_market_ids(&self) -> Vec<MarketId> {
261 let spot_markets = self.get_all_spot_market_ids();
262 let perp_markets = self.get_all_perp_market_ids();
263 spot_markets.into_iter().chain(perp_markets).collect()
264 }
265
266 pub async fn unsubscribe(&self) -> SdkResult<()> {
271 self.backend.unsubscribe().await
272 }
273
274 #[deprecated]
276 pub fn inner(&self) -> &RpcClient {
277 &self.backend.rpc_client
278 }
279
280 pub fn rpc(&self) -> Arc<RpcClient> {
282 self.backend.client()
283 }
284
285 pub fn ws(&self) -> Arc<PubsubClient> {
287 self.backend.ws()
288 }
289
290 pub fn program_data(&self) -> &ProgramData {
294 &self.backend.program_data
295 }
296
297 pub async fn get_order_by_id(
304 &self,
305 account: &Pubkey,
306 order_id: u32,
307 ) -> SdkResult<Option<Order>> {
308 let user = self.backend.get_user_account(account).await?;
309
310 Ok(user.orders.iter().find(|o| o.order_id == order_id).copied())
311 }
312
313 pub async fn get_order_by_user_id(
320 &self,
321 account: &Pubkey,
322 user_order_id: u8,
323 ) -> SdkResult<Option<Order>> {
324 let user = self.backend.get_user_account(account).await?;
325
326 Ok(user
327 .orders
328 .iter()
329 .find(|o| o.user_order_id == user_order_id)
330 .copied())
331 }
332
333 pub async fn all_orders(&self, account: &Pubkey) -> SdkResult<Vec<Order>> {
339 let user = self.backend.get_user_account(account).await?;
340
341 Ok(user
342 .orders
343 .iter()
344 .filter(|o| o.status == OrderStatus::Open)
345 .copied()
346 .collect())
347 }
348
349 pub async fn unsettled_positions(&self, account: &Pubkey) -> SdkResult<Vec<PerpPosition>> {
355 let user = self.backend.get_user_account(account).await?;
356
357 Ok(user
358 .perp_positions
359 .iter()
360 .filter(|p| p.base_asset_amount == 0 && p.quote_asset_amount != 0)
361 .copied()
362 .collect())
363 }
364
365 pub async fn all_positions(
369 &self,
370 account: &Pubkey,
371 ) -> SdkResult<(Vec<SpotPosition>, Vec<PerpPosition>)> {
372 let user = self.backend.get_user_account(account).await?;
373
374 Ok((
375 user.spot_positions
376 .iter()
377 .filter(|s| !s.is_available())
378 .copied()
379 .collect(),
380 user.perp_positions
381 .iter()
382 .filter(|p| p.is_open_position())
383 .copied()
384 .collect(),
385 ))
386 }
387
388 pub async fn perp_position(
394 &self,
395 account: &Pubkey,
396 market_index: u16,
397 ) -> SdkResult<Option<PerpPosition>> {
398 let user = self.backend.get_user_account(account).await?;
399
400 Ok(user
401 .perp_positions
402 .iter()
403 .find(|p| p.market_index == market_index && !p.is_available())
404 .copied())
405 }
406
407 pub async fn spot_position(
413 &self,
414 account: &Pubkey,
415 market_index: u16,
416 ) -> SdkResult<Option<SpotPosition>> {
417 let user = self.backend.get_user_account(account).await?;
418
419 Ok(user
420 .spot_positions
421 .iter()
422 .find(|p| p.market_index == market_index && !p.is_available())
423 .copied())
424 }
425
426 pub fn wallet(&self) -> &Wallet {
428 &self.wallet
429 }
430
431 pub async fn get_user_account(&self, account: &Pubkey) -> SdkResult<User> {
438 self.backend.get_user_account(account).await
439 }
440
441 pub async fn get_user_stats(&self, authority: &Pubkey) -> SdkResult<UserStats> {
445 let user_stats_pubkey = Wallet::derive_stats_account(authority);
446 self.backend.get_account(&user_stats_pubkey).await
447 }
448
449 pub async fn get_latest_blockhash(&self) -> SdkResult<Hash> {
452 self.backend.get_latest_blockhash().await
453 }
454
455 pub async fn get_account_value<T: AccountDeserialize>(&self, account: &Pubkey) -> SdkResult<T> {
462 self.backend.get_account(account).await
463 }
464
465 pub fn try_get_account<T: AccountDeserialize>(&self, account: &Pubkey) -> SdkResult<T> {
470 self.backend.try_get_account(account)
471 }
472
473 pub fn state_account(&self) -> SdkResult<State> {
476 self.backend.try_get_account(state_account())
477 }
478
479 pub async fn sign_and_send(&self, tx: VersionedMessage) -> SdkResult<Signature> {
483 let recent_block_hash = self.backend.get_latest_blockhash().await?;
484 self.backend
485 .sign_and_send(self.wallet(), tx, recent_block_hash)
486 .await
487 .map_err(|err| err.to_out_of_sol_error().unwrap_or(err))
488 }
489
490 pub async fn sign_and_send_with_config(
497 &self,
498 tx: VersionedMessage,
499 recent_block_hash: Option<Hash>,
500 config: RpcSendTransactionConfig,
501 ) -> SdkResult<Signature> {
502 let recent_block_hash = match recent_block_hash {
503 Some(h) => h,
504 None => self.backend.get_latest_blockhash().await?,
505 };
506 self.backend
507 .sign_and_send_with_config(self.wallet(), tx, recent_block_hash, config)
508 .await
509 .map_err(|err| err.to_out_of_sol_error().unwrap_or(err))
510 }
511
512 pub async fn get_spot_market_account(&self, market_index: u16) -> SdkResult<SpotMarket> {
518 match self
519 .backend
520 .try_get_spot_market_account_and_slot(market_index)
521 {
522 Some(market) => Ok(market.data),
523 None => {
524 debug!(target: "rpc", "fetch market: spot/{market_index}");
525 let market = derive_spot_market_account(market_index);
526 self.backend.get_account(&market).await
527 }
528 }
529 }
530
531 pub async fn get_perp_market_account(&self, market_index: u16) -> SdkResult<PerpMarket> {
537 match self
538 .backend
539 .try_get_perp_market_account_and_slot(market_index)
540 {
541 Some(market) => Ok(market.data),
542 None => {
543 debug!(target: "rpc", "fetch market: perp/{market_index}");
544 let market = derive_perp_market_account(market_index);
545 self.backend.get_account(&market).await
546 }
547 }
548 }
549
550 pub fn try_get_spot_market_account(&self, market_index: u16) -> SdkResult<SpotMarket> {
556 if let Some(market) = self
557 .backend
558 .try_get_spot_market_account_and_slot(market_index)
559 {
560 Ok(market.data)
561 } else {
562 Err(SdkError::NoMarketData(MarketId::spot(market_index)))
563 }
564 }
565
566 pub fn try_get_perp_market_account(&self, market_index: u16) -> SdkResult<PerpMarket> {
572 if let Some(market) = self
573 .backend
574 .try_get_perp_market_account_and_slot(market_index)
575 {
576 Ok(market.data)
577 } else {
578 Err(SdkError::NoMarketData(MarketId::perp(market_index)))
579 }
580 }
581
582 pub fn market_lookup(&self, symbol: &str) -> Option<MarketId> {
588 if symbol.to_ascii_lowercase().ends_with("-perp") {
589 let markets = self.program_data().perp_market_configs();
590 markets
591 .iter()
592 .find(|m| m.symbol().eq_ignore_ascii_case(symbol))
593 .map(|m| MarketId::perp(m.market_index))
594 } else {
595 let markets = self.program_data().spot_market_configs();
596 markets
597 .iter()
598 .find(|m| m.symbol().eq_ignore_ascii_case(symbol))
599 .map(|m| MarketId::spot(m.market_index))
600 }
601 }
602
603 pub async fn oracle_price(&self, market: MarketId) -> SdkResult<i64> {
606 self.backend.oracle_price(market).await
607 }
608
609 pub async fn init_tx(
620 &self,
621 account: &Pubkey,
622 delegated: bool,
623 ) -> SdkResult<TransactionBuilder> {
624 let account_data = self.get_user_account(account).await?;
625 Ok(TransactionBuilder::new(
626 self.program_data(),
627 *account,
628 Cow::Owned(account_data),
629 delegated,
630 ))
631 }
632
633 pub async fn get_recent_priority_fees(
634 &self,
635 writable_markets: &[MarketId],
636 window: Option<usize>,
637 ) -> SdkResult<Vec<u64>> {
638 self.backend
639 .get_recent_priority_fees(writable_markets, window)
640 .await
641 }
642
643 pub fn try_get_oracle_price_data_and_slot(&self, market: MarketId) -> Option<Oracle> {
647 self.backend.try_get_oracle_price_data_and_slot(market)
648 }
649
650 pub async fn get_oracle_price_data_and_slot(&self, market: MarketId) -> SdkResult<Oracle> {
654 self.backend.get_oracle(market).await
655 }
656
657 pub async fn subscribe_account(&self, account: &Pubkey) -> SdkResult<()> {
666 self.backend.account_map.subscribe_account(account).await
667 }
668
669 pub async fn subscribe_account_polled(
671 &self,
672 account: &Pubkey,
673 interval: Duration,
674 ) -> SdkResult<()> {
675 self.backend
676 .account_map
677 .subscribe_account_polled(account, Some(interval))
678 .await
679 }
680
681 pub fn unsubscribe_account(&self, account: &Pubkey) -> SdkResult<()> {
683 self.backend.account_map.unsubscribe_account(account);
684 Ok(())
685 }
686
687 pub fn check_libs() -> SdkResult<()> {
691 let libdrift_version = ffi::check_ffi_version();
692 let idl_version = drift_idl::IDL_VERSION;
693
694 if libdrift_version != idl_version {
695 log::warn!(
696 "libdrift_ffi_sys: {} does not match IDL: {}",
697 libdrift_version,
698 drift_idl::IDL_VERSION
699 );
700 return Err(SdkError::LibDriftVersion);
701 }
702
703 Ok(())
704 }
705
706 #[cfg(feature = "unsafe_pub")]
708 pub fn spot_market_map(&self) -> Arc<MapOf<u16, DataAndSlot<SpotMarket>>> {
709 self.backend.spot_market_map.map()
710 }
711
712 #[cfg(feature = "unsafe_pub")]
714 pub fn perp_market_map(&self) -> Arc<MapOf<u16, DataAndSlot<PerpMarket>>> {
715 self.backend.perp_market_map.map()
716 }
717
718 #[cfg(feature = "unsafe_pub")]
720 pub fn oracle_map(&self) -> Arc<MapOf<(Pubkey, u8), Oracle>> {
721 self.backend.oracle_map.map()
722 }
723
724 pub async fn grpc_subscribe(
737 &self,
738 endpoint: String,
739 x_token: String,
740 opts: GrpcSubscribeOpts,
741 ) -> SdkResult<()> {
742 self.backend.grpc_subscribe(endpoint, x_token, opts).await
743 }
744
745 pub fn grpc_unsubscribe(&self) {
747 self.backend.grpc_unsubscribe();
748 }
749
750 #[cfg(feature = "unsafe_pub")]
752 pub fn backend(&self) -> &'static DriftClientBackend {
753 self.backend
754 }
755}
756
757pub struct DriftClientBackend {
760 rpc_client: Arc<RpcClient>,
761 pubsub_client: Arc<PubsubClient>,
762 program_data: ProgramData,
763 blockhash_subscriber: BlockhashSubscriber,
764 account_map: AccountMap,
765 perp_market_map: MarketMap<PerpMarket>,
766 spot_market_map: MarketMap<SpotMarket>,
767 oracle_map: OracleMap,
768 grpc_unsub: RwLock<Option<UnsubHandle>>,
769}
770impl DriftClientBackend {
771 async fn new(context: Context, rpc_client: Arc<RpcClient>) -> SdkResult<Self> {
773 let pubsub_client =
774 Arc::new(PubsubClient::new(&get_ws_url(rpc_client.url().as_str())?).await?);
775
776 let perp_market_map =
777 MarketMap::<PerpMarket>::new(Arc::clone(&pubsub_client), rpc_client.commitment());
778 let spot_market_map =
779 MarketMap::<SpotMarket>::new(Arc::clone(&pubsub_client), rpc_client.commitment());
780
781 let lut_pubkeys = context.luts();
782
783 let (_, _, lut_accounts) = tokio::try_join!(
784 perp_market_map.sync(&rpc_client),
785 spot_market_map.sync(&rpc_client),
786 rpc_client
787 .get_multiple_accounts(lut_pubkeys)
788 .map_err(Into::into),
789 )?;
790
791 let lookup_tables = lut_pubkeys
792 .iter()
793 .zip(lut_accounts.iter())
794 .map(|(pubkey, account_data)| {
795 utils::deserialize_alt(*pubkey, account_data.as_ref().unwrap())
796 .expect("LUT decodes")
797 })
798 .collect();
799
800 let mut all_oracles = Vec::<(MarketId, Pubkey, OracleSource)>::with_capacity(
801 perp_market_map.len() + spot_market_map.len(),
802 );
803 for market_oracle_info in perp_market_map
804 .oracles()
805 .iter()
806 .chain(spot_market_map.oracles().iter())
807 {
808 all_oracles.push(*market_oracle_info);
809 }
810
811 let oracle_map = OracleMap::new(
812 Arc::clone(&pubsub_client),
813 all_oracles.as_slice(),
814 rpc_client.commitment(),
815 );
816 let account_map = AccountMap::new(
817 Arc::clone(&pubsub_client),
818 Arc::clone(&rpc_client),
819 rpc_client.commitment(),
820 );
821 account_map
822 .subscribe_account_polled(state_account(), Some(Duration::from_secs(180)))
823 .await?;
824
825 Ok(Self {
826 rpc_client: Arc::clone(&rpc_client),
827 pubsub_client,
828 blockhash_subscriber: BlockhashSubscriber::new(Duration::from_secs(2), rpc_client),
829 program_data: ProgramData::new(
830 spot_market_map.values(),
831 perp_market_map.values(),
832 lookup_tables,
833 ),
834 account_map,
835 perp_market_map,
836 spot_market_map,
837 oracle_map,
838 grpc_unsub: RwLock::default(),
839 })
840 }
841
842 pub fn is_grpc_subscribed(&self) -> bool {
844 let unsub = self.grpc_unsub.read().unwrap();
845 unsub.is_some()
846 }
847
848 async fn subscribe_blockhashes(&self) -> SdkResult<()> {
850 self.blockhash_subscriber.subscribe();
851 Ok(())
852 }
853
854 async fn subscribe_markets(&self, markets: &[MarketId]) -> SdkResult<()> {
856 if self.is_grpc_subscribed() {
857 log::info!("already subscribed markets via gRPC");
858 return Err(SdkError::AlreadySubscribed);
859 }
860
861 let (perps, spot) = markets
862 .iter()
863 .partition::<Vec<MarketId>, _>(|x| x.is_perp());
864 let _ = tokio::try_join!(
865 self.perp_market_map.subscribe(&perps),
866 self.spot_market_map.subscribe(&spot),
867 )?;
868
869 Ok(())
870 }
871
872 async fn subscribe_oracles(&self, markets: &[MarketId]) -> SdkResult<()> {
874 if self.is_grpc_subscribed() {
875 log::info!("already subscribed oracles via gRPC");
876 return Err(SdkError::AlreadySubscribed);
877 }
878
879 self.oracle_map.subscribe(markets).await
880 }
881
882 async fn grpc_subscribe(
884 &self,
885 endpoint: String,
886 x_token: String,
887 opts: GrpcSubscribeOpts,
888 ) -> SdkResult<()> {
889 let mut grpc =
890 DriftGrpcClient::new(endpoint, x_token).grpc_connection_opts(opts.connection_opts);
891
892 grpc.on_account(
893 AccountFilter::partial().with_discriminator(SpotMarket::DISCRIMINATOR),
894 self.spot_market_map.on_account_fn(),
895 );
896 grpc.on_account(
897 AccountFilter::partial().with_discriminator(PerpMarket::DISCRIMINATOR),
898 self.perp_market_map.on_account_fn(),
899 );
900 let oracles: Vec<Pubkey> = self
901 .oracle_map
902 .oracle_by_market
903 .iter()
904 .map(|x| x.1 .0)
905 .collect();
906 grpc.on_account(
907 AccountFilter::partial().with_accounts(oracles.into_iter()),
908 self.oracle_map.on_account_fn(),
909 );
910
911 if opts.usermap {
912 grpc.on_account(
913 AccountFilter::partial().with_discriminator(User::DISCRIMINATOR),
914 self.account_map.on_account_fn(),
915 );
916 } else {
917 grpc.on_account(
920 AccountFilter::full()
921 .with_discriminator(User::DISCRIMINATOR)
922 .with_accounts(opts.user_accounts.into_iter()),
923 self.account_map.on_account_fn(),
924 );
925 }
926
927 if opts.user_stats_map {
928 grpc.on_account(
929 AccountFilter::partial().with_discriminator(UserStats::DISCRIMINATOR),
930 self.account_map.on_account_fn(),
931 );
932 }
933
934 if let Some((filter, on_account)) = opts.on_account {
936 grpc.on_account(filter, on_account);
937 }
938 if let Some(f) = opts.on_slot {
939 grpc.on_slot(f);
940 }
941
942 let grpc_unsub = grpc
944 .subscribe(CommitmentLevel::Confirmed, GeyserSubscribeOpts::default())
945 .await
946 .map_err(SdkError::Grpc)?;
947
948 let mut unsub = self.grpc_unsub.write().unwrap();
949 let _ = unsub.insert(grpc_unsub);
950
951 Ok(())
952 }
953
954 fn grpc_unsubscribe(&self) {
956 let mut guard = self.grpc_unsub.write().unwrap();
957 let unsub = guard.take();
958 unsub.map(|u| u.send(()));
959 }
960
961 async fn unsubscribe(&self) -> SdkResult<()> {
963 self.blockhash_subscriber.unsubscribe();
964 self.perp_market_map.unsubscribe_all()?;
965 self.spot_market_map.unsubscribe_all()?;
966 self.account_map.unsubscribe_account(state_account());
967 self.oracle_map.unsubscribe_all()
968 }
969
970 pub fn try_get_perp_market_account_and_slot(
971 &self,
972 market_index: u16,
973 ) -> Option<DataAndSlot<PerpMarket>> {
974 if self.is_grpc_subscribed() || self.perp_market_map.is_subscribed(market_index) {
975 self.perp_market_map.get(&market_index)
976 } else {
977 None
978 }
979 }
980
981 pub fn try_get_spot_market_account_and_slot(
982 &self,
983 market_index: u16,
984 ) -> Option<DataAndSlot<SpotMarket>> {
985 if self.is_grpc_subscribed() || self.spot_market_map.is_subscribed(market_index) {
986 self.spot_market_map.get(&market_index)
987 } else {
988 None
989 }
990 }
991
992 pub fn try_get_oracle_price_data_and_slot(&self, market: MarketId) -> Option<Oracle> {
993 self.oracle_map.get_by_market(&market)
994 }
995
996 pub fn try_get_oracle_price_data_and_slot_checked(&self, market: MarketId) -> Option<Oracle> {
999 let current_oracle = self
1000 .oracle_map
1001 .get_by_market(&market)
1002 .expect("oracle")
1003 .pubkey;
1004
1005 let program_configured_oracle = if market.is_perp() {
1006 let market = self.try_get_perp_market_account_and_slot(market.index())?;
1007 market.data.amm.oracle
1008 } else {
1009 let market = self.try_get_spot_market_account_and_slot(market.index())?;
1010 market.data.oracle
1011 };
1012
1013 if program_configured_oracle != current_oracle {
1014 panic!("invalid oracle: {}", market.index());
1015 }
1016
1017 self.try_get_oracle_price_data_and_slot(market)
1018 }
1019
1020 fn client(&self) -> Arc<RpcClient> {
1022 Arc::clone(&self.rpc_client)
1023 }
1024
1025 fn ws(&self) -> Arc<PubsubClient> {
1027 Arc::clone(&self.pubsub_client)
1028 }
1029
1030 async fn get_recent_priority_fees(
1035 &self,
1036 writable_markets: &[MarketId],
1037 window: Option<usize>,
1038 ) -> SdkResult<Vec<u64>> {
1039 let addresses: Vec<Pubkey> = writable_markets
1040 .iter()
1041 .filter_map(|x| match x.kind() {
1042 MarketType::Spot => self
1043 .program_data
1044 .spot_market_config_by_index(x.index())
1045 .map(|x| x.pubkey),
1046 MarketType::Perp => self
1047 .program_data
1048 .perp_market_config_by_index(x.index())
1049 .map(|x| x.pubkey),
1050 })
1051 .collect();
1052
1053 let response = self
1054 .rpc_client
1055 .get_recent_prioritization_fees(addresses.as_slice())
1056 .await?;
1057 let window = window.unwrap_or(5).max(1);
1058 let fees = response
1059 .iter()
1060 .take(window)
1061 .map(|x| x.prioritization_fee)
1062 .collect();
1063
1064 Ok(fees)
1065 }
1066
1067 pub async fn get_account<T: AccountDeserialize>(&self, account: &Pubkey) -> SdkResult<T> {
1069 if let Some(value) = self.account_map.account_data(account) {
1070 Ok(value)
1071 } else {
1072 let account_data = self.rpc_client.get_account_data(account).await?;
1073 if account_data.is_empty() {
1074 return Err(SdkError::NoAccountData(*account));
1075 }
1076 T::try_deserialize(&mut account_data.as_slice())
1077 .map_err(|err| SdkError::Anchor(Box::new(err)))
1078 }
1079 }
1080
1081 pub async fn get_account_with_slot<T: AccountDeserialize>(
1083 &self,
1084 account: &Pubkey,
1085 ) -> SdkResult<DataAndSlot<T>> {
1086 if let Some(value) = self.account_map.account_data_and_slot(account) {
1087 Ok(value)
1088 } else {
1089 let (account, slot) = self.get_account_with_slot_raw(account).await?;
1090 Ok(DataAndSlot {
1091 slot,
1092 data: T::try_deserialize(&mut account.data.as_slice())
1093 .map_err(|err| SdkError::Anchor(Box::new(err)))?,
1094 })
1095 }
1096 }
1097
1098 pub async fn get_user_account(&self, account: &Pubkey) -> SdkResult<User> {
1102 self.get_account(account).await
1103 }
1104
1105 pub fn try_get_account<T: AccountDeserialize>(&self, account: &Pubkey) -> SdkResult<T> {
1108 self.account_map
1109 .account_data(account)
1110 .ok_or_else(|| SdkError::NoAccountData(*account))
1111 }
1112
1113 pub async fn get_latest_blockhash(&self) -> SdkResult<Hash> {
1117 match self.blockhash_subscriber.get_latest_blockhash() {
1118 Some(hash) => Ok(hash),
1119 None => self
1120 .rpc_client
1121 .get_latest_blockhash()
1122 .await
1123 .map_err(SdkError::Rpc),
1124 }
1125 }
1126
1127 pub async fn sign_and_send(
1131 &self,
1132 wallet: &Wallet,
1133 tx: VersionedMessage,
1134 recent_block_hash: Hash,
1135 ) -> SdkResult<Signature> {
1136 let tx = wallet.sign_tx(tx, recent_block_hash)?;
1137 self.rpc_client
1138 .send_transaction(&tx)
1139 .await
1140 .map_err(Into::into)
1141 }
1142
1143 pub async fn sign_and_send_with_config(
1148 &self,
1149 wallet: &Wallet,
1150 tx: VersionedMessage,
1151 recent_block_hash: Hash,
1152 config: RpcSendTransactionConfig,
1153 ) -> SdkResult<Signature> {
1154 let tx = wallet.sign_tx(tx, recent_block_hash)?;
1155 self.rpc_client
1156 .send_transaction_with_config(&tx, config)
1157 .await
1158 .map_err(Into::into)
1159 }
1160
1161 pub async fn oracle_price(&self, market: MarketId) -> SdkResult<i64> {
1165 self.get_oracle(market).await.map(|o| o.data.price)
1166 }
1167
1168 pub async fn get_oracle(&self, market: MarketId) -> SdkResult<Oracle> {
1172 if self.oracle_map.is_subscribed(&market) {
1173 Ok(self
1174 .try_get_oracle_price_data_and_slot(market)
1175 .expect("oracle exists"))
1176 } else {
1177 debug!(target: "rpc", "fetch oracle account: {market:?}");
1178 let (oracle, oracle_source) = match market.kind() {
1179 MarketType::Perp => {
1180 let market = self
1181 .program_data
1182 .perp_market_config_by_index(market.index())
1183 .ok_or(SdkError::InvalidOracle)?;
1184 (market.amm.oracle, market.amm.oracle_source)
1185 }
1186 MarketType::Spot => {
1187 let market = self
1188 .program_data
1189 .spot_market_config_by_index(market.index())
1190 .ok_or(SdkError::InvalidOracle)?;
1191 (market.oracle, market.oracle_source)
1192 }
1193 };
1194 let (account_data, slot) = self.get_account_with_slot_raw(&oracle).await?;
1195 let oracle_price_data =
1196 ffi::get_oracle_price(oracle_source, &mut (oracle, account_data.clone()), slot)?;
1197
1198 Ok(Oracle {
1199 pubkey: oracle,
1200 source: oracle_source,
1201 slot,
1202 data: oracle_price_data,
1203 raw: account_data.data,
1204 })
1205 }
1206 }
1207
1208 async fn get_account_with_slot_raw(&self, pubkey: &Pubkey) -> SdkResult<(Account, Slot)> {
1210 match self
1211 .rpc_client
1212 .get_account_with_commitment(pubkey, self.rpc_client.commitment())
1213 .await
1214 {
1215 Ok(Response {
1216 context,
1217 value: Some(account),
1218 }) => Ok((account, context.slot)),
1219 Ok(Response {
1220 context: _,
1221 value: None,
1222 }) => Err(SdkError::InvalidAccount),
1223 Err(err) => Err(err.into()),
1224 }
1225 }
1226
1227 #[cfg(feature = "unsafe_pub")]
1228 pub fn account_map(&self) -> &AccountMap {
1229 &self.account_map
1230 }
1231
1232 #[cfg(feature = "unsafe_pub")]
1233 pub fn perp_market_map(&self) -> &MarketMap<PerpMarket> {
1234 &self.perp_market_map
1235 }
1236
1237 #[cfg(feature = "unsafe_pub")]
1238 pub fn spot_market_map(&self) -> &MarketMap<SpotMarket> {
1239 &self.spot_market_map
1240 }
1241
1242 #[cfg(feature = "unsafe_pub")]
1243 pub fn oracle_map(&self) -> &OracleMap {
1244 &self.oracle_map
1245 }
1246}
1247
1248#[derive(Default)]
1253struct ForceMarkets {
1254 readable: Vec<MarketId>,
1256 writeable: Vec<MarketId>,
1258}
1259
1260impl ForceMarkets {
1261 pub fn with_readable(&mut self, markets: &[MarketId]) -> &mut Self {
1263 self.readable = markets.to_vec();
1264 self
1265 }
1266 pub fn with_writeable(&mut self, markets: &[MarketId]) -> &mut Self {
1268 self.writeable = markets.to_vec();
1269 self
1270 }
1271}
1272
1273pub struct TransactionBuilder<'a> {
1297 program_data: &'a ProgramData,
1299 account_data: Cow<'a, User>,
1301 sub_account: Pubkey,
1303 authority: Pubkey,
1305 ixs: Vec<Instruction>,
1307 legacy: bool,
1309 lookup_tables: Vec<AddressLookupTableAccount>,
1311 force_markets: ForceMarkets,
1313}
1314
1315impl<'a> TransactionBuilder<'a> {
1316 pub fn new<'b>(
1323 program_data: &'b ProgramData,
1324 sub_account: Pubkey,
1325 user: Cow<'b, User>,
1326 delegated: bool,
1327 ) -> Self
1328 where
1329 'b: 'a,
1330 {
1331 Self {
1332 authority: if delegated {
1333 user.delegate
1334 } else {
1335 user.authority
1336 },
1337 program_data,
1338 account_data: user,
1339 sub_account,
1340 ixs: Default::default(),
1341 lookup_tables: program_data.lookup_tables.to_vec(),
1342 legacy: false,
1343 force_markets: Default::default(),
1344 }
1345 }
1346 pub fn force_include_markets(&mut self, readable: &[MarketId], writeable: &[MarketId]) {
1348 self.force_markets.with_readable(readable);
1349 self.force_markets.with_writeable(writeable);
1350 }
1351 pub fn legacy(mut self) -> Self {
1353 self.legacy = true;
1354 self
1355 }
1356 pub fn lookup_tables(mut self, lookup_tables: &[AddressLookupTableAccount]) -> Self {
1358 self.lookup_tables = lookup_tables.to_vec();
1359 self.lookup_tables
1360 .extend(self.program_data.lookup_tables.iter().cloned());
1361
1362 self
1363 }
1364 pub fn with_priority_fee(mut self, microlamports_per_cu: u64, cu_limit: Option<u32>) -> Self {
1368 let cu_limit_ix = ComputeBudgetInstruction::set_compute_unit_price(microlamports_per_cu);
1369 self.ixs.insert(0, cu_limit_ix);
1370 if let Some(cu_limit) = cu_limit {
1371 let cu_price_ix = ComputeBudgetInstruction::set_compute_unit_limit(cu_limit);
1372 self.ixs.insert(1, cu_price_ix);
1373 }
1374
1375 self
1376 }
1377 pub fn add_ix(mut self, ix: Instruction) -> Self {
1379 self.ixs.push(ix);
1380 self
1381 }
1382
1383 pub fn deposit(
1385 mut self,
1386 amount: u64,
1387 spot_market_index: u16,
1388 user_token_account: Pubkey,
1389 reduce_only: Option<bool>,
1390 ) -> Self {
1391 let accounts = build_accounts(
1392 self.program_data,
1393 types::accounts::Deposit {
1394 state: *state_account(),
1395 user: self.sub_account,
1396 user_stats: Wallet::derive_stats_account(&self.authority),
1397 authority: self.authority,
1398 spot_market_vault: constants::derive_spot_market_vault(spot_market_index),
1399 user_token_account,
1400 token_program: constants::TOKEN_PROGRAM_ID,
1401 },
1402 &[self.account_data.as_ref()],
1403 self.force_markets.readable.iter(),
1404 [MarketId::spot(spot_market_index)].iter(),
1405 );
1406
1407 let ix = Instruction {
1408 program_id: constants::PROGRAM_ID,
1409 accounts,
1410 data: InstructionData::data(&drift_idl::instructions::Deposit {
1411 market_index: spot_market_index,
1412 amount,
1413 reduce_only: reduce_only.unwrap_or(false),
1414 }),
1415 };
1416
1417 self.ixs.push(ix);
1418
1419 self
1420 }
1421
1422 pub fn withdraw(
1424 mut self,
1425 amount: u64,
1426 spot_market_index: u16,
1427 user_token_account: Pubkey,
1428 reduce_only: Option<bool>,
1429 ) -> Self {
1430 let accounts = build_accounts(
1431 self.program_data,
1432 types::accounts::Withdraw {
1433 state: *state_account(),
1434 user: self.sub_account,
1435 user_stats: Wallet::derive_stats_account(&self.authority),
1436 authority: self.authority,
1437 spot_market_vault: constants::derive_spot_market_vault(spot_market_index),
1438 user_token_account,
1439 drift_signer: constants::derive_drift_signer(),
1440 token_program: constants::TOKEN_PROGRAM_ID,
1441 },
1442 &[self.account_data.as_ref()],
1443 self.force_markets.readable.iter(),
1444 [MarketId::spot(spot_market_index)]
1445 .iter()
1446 .chain(self.force_markets.writeable.iter()),
1447 );
1448
1449 let ix = Instruction {
1450 program_id: constants::PROGRAM_ID,
1451 accounts,
1452 data: InstructionData::data(&drift_idl::instructions::Withdraw {
1453 market_index: spot_market_index,
1454 amount,
1455 reduce_only: reduce_only.unwrap_or(false),
1456 }),
1457 };
1458
1459 self.ixs.push(ix);
1460
1461 self
1462 }
1463
1464 pub fn place_orders(mut self, orders: Vec<OrderParams>) -> Self {
1468 let mut readable_accounts: Vec<MarketId> = orders
1469 .iter()
1470 .map(|o| (o.market_index, o.market_type).into())
1471 .collect();
1472 readable_accounts.extend(&self.force_markets.readable);
1473
1474 let accounts = build_accounts(
1475 self.program_data,
1476 types::accounts::PlaceOrders {
1477 state: *state_account(),
1478 authority: self.authority,
1479 user: self.sub_account,
1480 },
1481 &[self.account_data.as_ref()],
1482 readable_accounts.iter(),
1483 self.force_markets.writeable.iter(),
1484 );
1485
1486 let ix = Instruction {
1487 program_id: constants::PROGRAM_ID,
1488 accounts,
1489 data: InstructionData::data(&drift_idl::instructions::PlaceOrders { params: orders }),
1490 };
1491
1492 self.ixs.push(ix);
1493
1494 self
1495 }
1496
1497 pub fn cancel_all_orders(mut self) -> Self {
1499 let accounts = build_accounts(
1500 self.program_data,
1501 types::accounts::CancelOrder {
1502 state: *state_account(),
1503 authority: self.authority,
1504 user: self.sub_account,
1505 },
1506 &[self.account_data.as_ref()],
1507 self.force_markets.readable.iter(),
1508 self.force_markets.writeable.iter(),
1509 );
1510
1511 let ix = Instruction {
1512 program_id: constants::PROGRAM_ID,
1513 accounts,
1514 data: InstructionData::data(&drift_idl::instructions::CancelOrders {
1515 market_index: None,
1516 market_type: None,
1517 direction: None,
1518 }),
1519 };
1520 self.ixs.push(ix);
1521
1522 self
1523 }
1524
1525 pub fn cancel_orders(
1530 mut self,
1531 market: (u16, MarketType),
1532 direction: Option<PositionDirection>,
1533 ) -> Self {
1534 let (idx, r#type) = market;
1535 let accounts = build_accounts(
1536 self.program_data,
1537 types::accounts::CancelOrder {
1538 state: *state_account(),
1539 authority: self.authority,
1540 user: self.sub_account,
1541 },
1542 &[self.account_data.as_ref()],
1543 [(idx, r#type).into()]
1544 .iter()
1545 .chain(self.force_markets.readable.iter()),
1546 self.force_markets.writeable.iter(),
1547 );
1548
1549 let ix = Instruction {
1550 program_id: constants::PROGRAM_ID,
1551 accounts,
1552 data: InstructionData::data(&drift_idl::instructions::CancelOrders {
1553 market_index: Some(idx),
1554 market_type: Some(r#type),
1555 direction,
1556 }),
1557 };
1558 self.ixs.push(ix);
1559
1560 self
1561 }
1562
1563 pub fn cancel_orders_by_id(mut self, order_ids: Vec<u32>) -> Self {
1565 let accounts = build_accounts(
1566 self.program_data,
1567 types::accounts::CancelOrder {
1568 state: *state_account(),
1569 authority: self.authority,
1570 user: self.sub_account,
1571 },
1572 &[self.account_data.as_ref()],
1573 self.force_markets.readable.iter(),
1574 self.force_markets.writeable.iter(),
1575 );
1576
1577 let ix = Instruction {
1578 program_id: constants::PROGRAM_ID,
1579 accounts,
1580 data: InstructionData::data(&drift_idl::instructions::CancelOrdersByIds { order_ids }),
1581 };
1582 self.ixs.push(ix);
1583
1584 self
1585 }
1586
1587 pub fn cancel_orders_by_user_id(mut self, user_order_ids: Vec<u8>) -> Self {
1589 let accounts = build_accounts(
1590 self.program_data,
1591 types::accounts::CancelOrder {
1592 state: *state_account(),
1593 authority: self.authority,
1594 user: self.sub_account,
1595 },
1596 &[self.account_data.as_ref()],
1597 self.force_markets.readable.iter(),
1598 self.force_markets.writeable.iter(),
1599 );
1600
1601 for user_order_id in user_order_ids {
1602 let ix = Instruction {
1603 program_id: constants::PROGRAM_ID,
1604 accounts: accounts.clone(),
1605 data: InstructionData::data(&drift_idl::instructions::CancelOrderByUserId {
1606 user_order_id,
1607 }),
1608 };
1609 self.ixs.push(ix);
1610 }
1611
1612 self
1613 }
1614
1615 pub fn modify_orders(mut self, orders: &[(u32, ModifyOrderParams)]) -> Self {
1617 let accounts = build_accounts(
1618 self.program_data,
1619 types::accounts::ModifyOrder {
1620 state: *state_account(),
1621 authority: self.authority,
1622 user: self.sub_account,
1623 },
1624 &[self.account_data.as_ref()],
1625 self.force_markets.readable.iter(),
1626 self.force_markets.writeable.iter(),
1627 );
1628
1629 for (order_id, params) in orders {
1630 let ix = Instruction {
1631 program_id: constants::PROGRAM_ID,
1632 accounts: accounts.clone(),
1633 data: InstructionData::data(&drift_idl::instructions::ModifyOrder {
1634 order_id: Some(*order_id),
1635 modify_order_params: *params,
1636 }),
1637 };
1638 self.ixs.push(ix);
1639 }
1640
1641 self
1642 }
1643
1644 pub fn modify_orders_by_user_id(mut self, orders: &[(u8, ModifyOrderParams)]) -> Self {
1646 let accounts = build_accounts(
1647 self.program_data,
1648 types::accounts::PlaceOrders {
1649 state: *state_account(),
1650 authority: self.authority,
1651 user: self.sub_account,
1652 },
1653 &[self.account_data.as_ref()],
1654 self.force_markets.readable.iter(),
1655 self.force_markets.writeable.iter(),
1656 );
1657
1658 for (user_order_id, params) in orders {
1659 let ix = Instruction {
1660 program_id: constants::PROGRAM_ID,
1661 accounts: accounts.clone(),
1662 data: InstructionData::data(&drift_idl::instructions::ModifyOrderByUserId {
1663 user_order_id: *user_order_id,
1664 modify_order_params: *params,
1665 }),
1666 };
1667 self.ixs.push(ix);
1668 }
1669
1670 self
1671 }
1672
1673 pub fn place_and_make(
1681 mut self,
1682 order: OrderParams,
1683 taker_info: &(Pubkey, User),
1684 taker_order_id: u32,
1685 referrer: Option<Pubkey>,
1686 fulfillment_type: Option<SpotFulfillmentType>,
1687 ) -> Self {
1688 let (taker, taker_account) = taker_info;
1689 let is_perp = order.market_type == MarketType::Perp;
1690 let perp_writable = [MarketId::perp(order.market_index)];
1691 let spot_writable = [MarketId::spot(order.market_index), MarketId::QUOTE_SPOT];
1692 let mut accounts = build_accounts(
1693 self.program_data,
1694 types::accounts::PlaceAndMakePerpOrder {
1695 state: *state_account(),
1696 authority: self.authority,
1697 user: self.sub_account,
1698 user_stats: Wallet::derive_stats_account(&self.authority),
1699 taker: *taker,
1700 taker_stats: Wallet::derive_stats_account(taker),
1701 },
1702 &[self.account_data.as_ref(), taker_account],
1703 self.force_markets.readable.iter(),
1704 if is_perp {
1705 perp_writable.iter()
1706 } else {
1707 spot_writable.iter()
1708 }
1709 .chain(self.force_markets.writeable.iter()),
1710 );
1711
1712 if let Some(referrer) = referrer {
1713 accounts.push(AccountMeta::new(
1714 Wallet::derive_stats_account(&referrer),
1715 false,
1716 ));
1717 accounts.push(AccountMeta::new(referrer, false));
1718 }
1719
1720 let ix = if order.market_type == MarketType::Perp {
1721 Instruction {
1722 program_id: constants::PROGRAM_ID,
1723 accounts,
1724 data: InstructionData::data(&drift_idl::instructions::PlaceAndMakePerpOrder {
1725 params: order,
1726 taker_order_id,
1727 }),
1728 }
1729 } else {
1730 Instruction {
1731 program_id: constants::PROGRAM_ID,
1732 accounts,
1733 data: InstructionData::data(&drift_idl::instructions::PlaceAndMakeSpotOrder {
1734 params: order,
1735 taker_order_id,
1736 fulfillment_type,
1737 }),
1738 }
1739 };
1740
1741 self.ixs.push(ix);
1742 self
1743 }
1744
1745 pub fn place_and_take(
1752 mut self,
1753 order: OrderParams,
1754 maker_info: Option<(Pubkey, User)>,
1755 referrer: Option<Pubkey>,
1756 fulfillment_type: Option<SpotFulfillmentType>,
1757 success_condition: Option<u32>,
1758 ) -> Self {
1759 let mut user_accounts = vec![self.account_data.as_ref()];
1760 if let Some((ref _maker_pubkey, ref maker)) = maker_info {
1761 user_accounts.push(maker);
1762 }
1763
1764 let is_perp = order.market_type == MarketType::Perp;
1765 let perp_writable = [MarketId::perp(order.market_index)];
1766 let spot_writable = [MarketId::spot(order.market_index), MarketId::QUOTE_SPOT];
1767
1768 let mut accounts = build_accounts(
1769 self.program_data,
1770 types::accounts::PlaceAndTakePerpOrder {
1771 state: *state_account(),
1772 authority: self.authority,
1773 user: self.sub_account,
1774 user_stats: Wallet::derive_stats_account(&self.authority),
1775 },
1776 user_accounts.as_slice(),
1777 self.force_markets.readable.iter(),
1778 if is_perp {
1779 perp_writable.iter()
1780 } else {
1781 spot_writable.iter()
1782 }
1783 .chain(self.force_markets.writeable.iter()),
1784 );
1785
1786 if referrer.is_some_and(|r| !maker_info.is_some_and(|(m, _)| m == r)) {
1787 let referrer = referrer.unwrap();
1788 accounts.push(AccountMeta::new(
1789 Wallet::derive_stats_account(&referrer),
1790 false,
1791 ));
1792 accounts.push(AccountMeta::new(referrer, false));
1793 }
1794
1795 let ix = if is_perp {
1796 Instruction {
1797 program_id: constants::PROGRAM_ID,
1798 accounts,
1799 data: InstructionData::data(&drift_idl::instructions::PlaceAndTakePerpOrder {
1800 params: order,
1801 success_condition,
1802 }),
1803 }
1804 } else {
1805 Instruction {
1806 program_id: constants::PROGRAM_ID,
1807 accounts,
1808 data: InstructionData::data(&drift_idl::instructions::PlaceAndTakeSpotOrder {
1809 params: order,
1810 maker_order_id: None,
1811 fulfillment_type,
1812 }),
1813 }
1814 };
1815
1816 self.ixs.push(ix);
1817 self
1818 }
1819
1820 pub fn place_and_make_swift_order(
1828 mut self,
1829 maker_order: OrderParams,
1830 signed_order_info: &SignedOrderInfo,
1831 taker_account: &User,
1832 taker_account_referrer: &Pubkey,
1833 ) -> Self {
1834 let order_params = signed_order_info.order_params();
1835 assert!(
1836 order_params.market_type == MarketType::Perp,
1837 "only swift perps are supported"
1838 );
1839 self = self.place_swift_order(signed_order_info, taker_account);
1840
1841 let perp_writable = [MarketId::perp(order_params.market_index)];
1842 let mut accounts = build_accounts(
1843 self.program_data,
1844 types::accounts::PlaceAndMakeSignedMsgPerpOrder {
1845 state: *state_account(),
1846 authority: self.authority,
1847 user: self.sub_account,
1848 user_stats: Wallet::derive_stats_account(&self.authority),
1849 taker: signed_order_info.taker_subaccount(),
1850 taker_stats: Wallet::derive_stats_account(&taker_account.authority),
1851 taker_signed_msg_user_orders: Wallet::derive_swift_order_account(
1852 &taker_account.authority,
1853 ),
1854 },
1855 &[self.account_data.as_ref(), taker_account],
1856 self.force_markets.readable.iter(),
1857 perp_writable
1858 .iter()
1859 .chain(self.force_markets.writeable.iter()),
1860 );
1861
1862 if taker_account_referrer != &DEFAULT_PUBKEY {
1863 accounts.push(AccountMeta::new(*taker_account_referrer, false));
1864 accounts.push(AccountMeta::new(
1865 Wallet::derive_stats_account(taker_account_referrer),
1866 false,
1867 ));
1868 }
1869
1870 self.ixs.push(Instruction {
1871 program_id: constants::PROGRAM_ID,
1872 accounts,
1873 data: InstructionData::data(&drift_idl::instructions::PlaceAndMakeSignedMsgPerpOrder {
1874 params: maker_order,
1875 signed_msg_order_uuid: signed_order_info.order_uuid(),
1876 }),
1877 });
1878
1879 self
1880 }
1881
1882 pub fn place_swift_order(
1892 mut self,
1893 signed_order_info: &SignedOrderInfo,
1894 taker_account: &User,
1895 ) -> Self {
1896 let order_params = signed_order_info.order_params();
1897 assert!(
1898 order_params.market_type == MarketType::Perp,
1899 "only swift perps are supported"
1900 );
1901
1902 let perp_readable = [MarketId::perp(order_params.market_index)];
1903 let accounts = build_accounts(
1904 self.program_data,
1905 types::accounts::PlaceSignedMsgTakerOrder {
1906 state: *state_account(),
1907 authority: self.authority,
1908 user: signed_order_info.taker_subaccount(),
1909 user_stats: Wallet::derive_stats_account(&taker_account.authority),
1910 signed_msg_user_orders: Wallet::derive_swift_order_account(
1911 &taker_account.authority,
1912 ),
1913 ix_sysvar: SYSVAR_INSTRUCTIONS_PUBKEY,
1914 },
1915 &[taker_account],
1916 perp_readable
1917 .iter()
1918 .chain(self.force_markets.readable.iter()),
1919 self.force_markets.writeable.iter(),
1920 );
1921
1922 let swift_taker_ix_data = signed_order_info.to_ix_data();
1923 let ed25519_verify_ix = crate::utils::new_ed25519_ix_ptr(
1924 swift_taker_ix_data.as_slice(),
1925 self.ixs.len() as u16 + 1,
1926 );
1927
1928 let place_swift_ix = Instruction {
1929 program_id: constants::PROGRAM_ID,
1930 accounts,
1931 data: InstructionData::data(&drift_idl::instructions::PlaceSignedMsgTakerOrder {
1932 signed_msg_order_params_message_bytes: swift_taker_ix_data,
1933 is_delegate_signer: signed_order_info.using_delegate_signing(),
1934 }),
1935 };
1936
1937 self.ixs
1938 .extend_from_slice(&[ed25519_verify_ix, place_swift_ix]);
1939 self
1940 }
1941
1942 pub fn set_max_initial_margin_ratio(mut self, margin_ratio: u32, sub_account_id: u16) -> Self {
1952 let accounts = build_accounts(
1953 self.program_data,
1954 types::accounts::UpdateUserCustomMarginRatio {
1955 authority: self.authority,
1956 user: Wallet::derive_user_account(&self.authority, sub_account_id),
1957 },
1958 &[self.account_data.as_ref()],
1959 std::iter::empty(),
1960 std::iter::empty(),
1961 );
1962 let ix = Instruction {
1963 program_id: constants::PROGRAM_ID,
1964 accounts,
1965 data: InstructionData::data(&drift_idl::instructions::UpdateUserCustomMarginRatio {
1966 sub_account_id,
1967 margin_ratio,
1968 }),
1969 };
1970 self.ixs.push(ix);
1971
1972 self
1973 }
1974
1975 pub fn build(self) -> VersionedMessage {
1977 if self.legacy {
1978 let message = Message::new(self.ixs.as_ref(), Some(&self.authority));
1979 VersionedMessage::Legacy(message)
1980 } else {
1981 let message = v0::Message::try_compile(
1982 &self.authority,
1983 self.ixs.as_slice(),
1984 self.lookup_tables.as_slice(),
1985 Default::default(),
1986 )
1987 .expect("ok");
1988 VersionedMessage::V0(message)
1989 }
1990 }
1991
1992 pub fn program_data(&self) -> &ProgramData {
1993 self.program_data
1994 }
1995
1996 pub fn account_data(&self) -> &Cow<'_, User> {
1997 &self.account_data
1998 }
1999}
2000
2001pub fn build_accounts<'a>(
2011 program_data: &ProgramData,
2012 base_accounts: impl ToAccountMetas,
2013 users: &[&User],
2014 markets_readable: impl Iterator<Item = &'a MarketId>,
2015 markets_writable: impl Iterator<Item = &'a MarketId>,
2016) -> Vec<AccountMeta> {
2017 let mut accounts = BTreeSet::<RemainingAccount>::new();
2019
2020 let mut include_market =
2022 |market_index: u16, market_type: MarketType, writable: bool| match market_type {
2023 MarketType::Spot => {
2024 let SpotMarket { pubkey, oracle, .. } = program_data
2025 .spot_market_config_by_index(market_index)
2026 .expect("exists");
2027 accounts.extend(
2028 [
2029 RemainingAccount::Spot {
2030 pubkey: *pubkey,
2031 writable,
2032 },
2033 RemainingAccount::Oracle { pubkey: *oracle },
2034 ]
2035 .iter(),
2036 )
2037 }
2038 MarketType::Perp => {
2039 let PerpMarket { pubkey, amm, .. } = program_data
2040 .perp_market_config_by_index(market_index)
2041 .expect("exists");
2042 accounts.extend(
2043 [
2044 RemainingAccount::Perp {
2045 pubkey: *pubkey,
2046 writable,
2047 },
2048 RemainingAccount::Oracle { pubkey: amm.oracle },
2049 ]
2050 .iter(),
2051 )
2052 }
2053 };
2054
2055 for market in markets_writable {
2056 include_market(market.index(), market.kind(), true);
2057 }
2058
2059 for market in markets_readable {
2060 include_market(market.index(), market.kind(), false);
2061 }
2062
2063 for user in users {
2064 for p in user.spot_positions.iter().filter(|p| !p.is_available()) {
2066 include_market(p.market_index, MarketType::Spot, false);
2067 }
2068 for p in user.perp_positions.iter().filter(|p| !p.is_available()) {
2069 include_market(p.market_index, MarketType::Perp, false);
2070 }
2071 }
2072 include_market(MarketId::QUOTE_SPOT.index(), MarketType::Spot, false);
2075
2076 let mut account_metas = base_accounts.to_account_metas();
2077 account_metas.extend(accounts.into_iter().map(Into::into));
2078 account_metas
2079}
2080
2081#[cfg(test)]
2082mod tests {
2083 use std::str::FromStr;
2084
2085 use serde_json::json;
2086 use solana_account_decoder_client_types::{UiAccount, UiAccountData, UiAccountEncoding};
2087 use solana_rpc_client::rpc_client::Mocks;
2088 use solana_rpc_client_api::{
2089 request::RpcRequest,
2090 response::{Response, RpcResponseContext},
2091 };
2092 use solana_sdk::signature::Keypair;
2093 use types::accounts::PerpMarket;
2094
2095 use super::*;
2096
2097 const ACCOUNT_DATA: &str = include_str!("../../res/9Jtc.hex");
2099 const DEVNET_ENDPOINT: &str = "https://api.devnet.solana.com";
2100
2101 async fn setup(rpc_mocks: Mocks, keypair: Keypair) -> DriftClient {
2103 let rpc_client = Arc::new(RpcClient::new_mock_with_mocks(
2104 DEVNET_ENDPOINT.to_string(),
2105 rpc_mocks,
2106 ));
2107
2108 let pubsub_client = Arc::new(
2109 PubsubClient::new(&get_ws_url(DEVNET_ENDPOINT).unwrap())
2110 .await
2111 .expect("ws connects"),
2112 );
2113
2114 let perp_market_map =
2115 MarketMap::<PerpMarket>::new(Arc::clone(&pubsub_client), rpc_client.commitment());
2116 let spot_market_map =
2117 MarketMap::<SpotMarket>::new(Arc::clone(&pubsub_client), rpc_client.commitment());
2118
2119 let backend = DriftClientBackend {
2120 rpc_client: Arc::clone(&rpc_client),
2121 pubsub_client: Arc::clone(&pubsub_client),
2122 program_data: ProgramData::uninitialized(),
2123 perp_market_map,
2124 spot_market_map,
2125 oracle_map: OracleMap::new(Arc::clone(&pubsub_client), &[], rpc_client.commitment()),
2126 blockhash_subscriber: BlockhashSubscriber::new(
2127 Duration::from_secs(2),
2128 Arc::clone(&rpc_client),
2129 ),
2130 account_map: AccountMap::new(
2131 Arc::clone(&pubsub_client),
2132 Arc::clone(&rpc_client),
2133 CommitmentConfig::processed(),
2134 ),
2135 grpc_unsub: Default::default(),
2136 };
2137
2138 DriftClient {
2139 context: Context::DevNet,
2140 backend: Box::leak(Box::new(backend)),
2141 wallet: Wallet::new(keypair),
2142 }
2143 }
2144
2145 #[tokio::test]
2146 async fn test_backend_send_sync() {
2147 let account_mocks = Mocks::default();
2148 let client = setup(account_mocks, Keypair::new()).await;
2149
2150 tokio::task::spawn(async move {
2151 let _ = client.clone();
2152 });
2153 }
2154
2155 #[tokio::test]
2156 #[cfg(feature = "rpc_tests")]
2157 async fn test_marketmap_subscribe() {
2158 use utils::test_envs::mainnet_endpoint;
2159
2160 let client = DriftClient::new(
2161 Context::MainNet,
2162 RpcAccountProvider::new(&mainnet_endpoint()),
2163 Keypair::new().into(),
2164 )
2165 .await
2166 .unwrap();
2167
2168 let _ = client.subscribe().await;
2169
2170 tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
2171
2172 for _ in 0..20 {
2173 tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
2174 let perp_market = client.get_perp_market_account_and_slot(0);
2175 let slot = perp_market.unwrap().slot;
2176 dbg!(slot);
2177 }
2178
2179 for _ in 0..20 {
2180 tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
2181 let spot_market = client.get_spot_market_account_and_slot(0);
2182 let slot = spot_market.unwrap().slot;
2183 dbg!(slot);
2184 }
2185 }
2186
2187 #[tokio::test]
2188 async fn get_orders() {
2189 let user = Pubkey::from_str("9JtczxrJjPM4J1xooxr2rFXmRivarb4BwjNiBgXDwe2p").unwrap();
2190 let account_data = hex::decode(ACCOUNT_DATA).expect("valid hex");
2191
2192 let mut account_mocks = Mocks::default();
2193 let account_response = json!(Response {
2194 context: RpcResponseContext::new(12_345),
2195 value: Some(UiAccount {
2196 data: UiAccountData::Binary(
2197 solana_sdk::bs58::encode(account_data).into_string(),
2198 UiAccountEncoding::Base58
2199 ),
2200 owner: user.to_string(),
2201 executable: false,
2202 lamports: 0,
2203 rent_epoch: 0,
2204 space: None,
2205 })
2206 });
2207 account_mocks.insert(RpcRequest::GetAccountInfo, account_response.clone());
2208
2209 let client = setup(account_mocks, Keypair::new()).await;
2210
2211 let orders = client.all_orders(&user).await.unwrap();
2212 assert_eq!(orders.len(), 3);
2213 }
2214
2215 #[tokio::test]
2216 async fn get_positions() {
2217 let user = Pubkey::from_str("9JtczxrJjPM4J1xooxr2rFXmRivarb4BwjNiBgXDwe2p").unwrap();
2218 let account_data = hex::decode(ACCOUNT_DATA).expect("valid hex");
2219
2220 let mut account_mocks = Mocks::default();
2221 let account_response = json!(Response {
2222 context: RpcResponseContext::new(12_345),
2223 value: Some(UiAccount {
2224 data: UiAccountData::Binary(
2225 solana_sdk::bs58::encode(account_data).into_string(),
2226 UiAccountEncoding::Base58
2227 ),
2228 owner: user.to_string(),
2229 executable: false,
2230 lamports: 0,
2231 rent_epoch: 0,
2232 space: None,
2233 })
2234 });
2235 account_mocks.insert(RpcRequest::GetAccountInfo, account_response.clone());
2236 let client = setup(account_mocks, Keypair::new()).await;
2237
2238 let (spot, perp) = client.all_positions(&user).await.unwrap();
2239 assert_eq!(spot.len(), 1);
2240 assert_eq!(perp.len(), 1);
2241 }
2242}