drift_rs/
constants.rs

1use std::sync::OnceLock;
2
3use solana_sdk::{address_lookup_table::AddressLookupTableAccount, pubkey::Pubkey};
4
5use crate::{
6    drift_idl::accounts::{PerpMarket, SpotMarket},
7    types::Context,
8    MarketId, MarketType, OracleSource,
9};
10
11/// https://github.com/solana-labs/solana-web3.js/blob/4e9988cfc561f3ed11f4c5016a29090a61d129a8/src/sysvar.ts#L11
12pub const SYSVAR_INSTRUCTIONS_PUBKEY: Pubkey =
13    solana_sdk::pubkey!("Sysvar1nstructions1111111111111111111111111");
14
15/// Drift program address
16pub const PROGRAM_ID: Pubkey = solana_sdk::pubkey!("dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH");
17
18/// Vault program address
19pub const VAULT_PROGRAM_ID: Pubkey =
20    solana_sdk::pubkey!("vAuLTsyrvSfZRuRB3XgvkPwNGgYSs9YRYymVebLKoxR");
21
22/// JIT proxy program address
23pub const JIT_PROXY_ID: Pubkey =
24    solana_sdk::pubkey!("J1TnP8zvVxbtF5KFp5xRmWuvG9McnhzmBd9XGfCyuxFP");
25/// Empty pubkey
26pub const DEFAULT_PUBKEY: Pubkey = solana_sdk::pubkey!("11111111111111111111111111111111");
27
28static STATE_ACCOUNT: OnceLock<Pubkey> = OnceLock::new();
29
30pub const TOKEN_PROGRAM_ID: Pubkey =
31    solana_sdk::pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
32
33/// Drift market lookup table (DevNet)
34pub const LUTS_DEVNET: &[Pubkey] = &[solana_sdk::pubkey!(
35    "FaMS3U4uBojvGn5FSDEPimddcXsCfwkKsFgMVVnDdxGb"
36)];
37/// Drift market lookup table (MainNet)
38pub const LUTS_MAINNET: &[Pubkey] = &[
39    solana_sdk::pubkey!("Fpys8GRa5RBWfyeN7AaDUwFGD1zkDCA4z3t4CJLV8dfL"),
40    solana_sdk::pubkey!("EiWSskK5HXnBTptiS5DH6gpAJRVNQ3cAhTKBGaiaysAb"),
41];
42
43/// Drift state account
44pub fn state_account() -> &'static Pubkey {
45    STATE_ACCOUNT.get_or_init(|| {
46        let (state_account, _seed) =
47            Pubkey::find_program_address(&[&b"drift_state"[..]], &PROGRAM_ID);
48        state_account
49    })
50}
51
52/// calculate the PDA of a drift spot market given index
53pub fn derive_spot_market_account(market_index: u16) -> Pubkey {
54    let (account, _seed) = Pubkey::find_program_address(
55        &[&b"spot_market"[..], &market_index.to_le_bytes()],
56        &PROGRAM_ID,
57    );
58    account
59}
60
61/// calculate the PDA of a drift perp market given index
62pub fn derive_perp_market_account(market_index: u16) -> Pubkey {
63    let (account, _seed) = Pubkey::find_program_address(
64        &[&b"perp_market"[..], &market_index.to_le_bytes()],
65        &PROGRAM_ID,
66    );
67    account
68}
69
70/// calculate the PDA for a drift spot market vault given index
71pub fn derive_spot_market_vault(market_index: u16) -> Pubkey {
72    let (account, _seed) = Pubkey::find_program_address(
73        &[&b"spot_market_vault"[..], &market_index.to_le_bytes()],
74        &PROGRAM_ID,
75    );
76    account
77}
78
79/// calculate the PDA for the drift signer
80pub fn derive_drift_signer() -> Pubkey {
81    let (account, _seed) = Pubkey::find_program_address(&[&b"drift_signer"[..]], &PROGRAM_ID);
82    account
83}
84
85/// Helper methods for market data structs
86pub trait MarketExt {
87    fn market_type(&self) -> &'static str;
88    fn symbol(&self) -> &str;
89}
90
91impl MarketExt for PerpMarket {
92    fn market_type(&self) -> &'static str {
93        "perp"
94    }
95    fn symbol(&self) -> &str {
96        unsafe { core::str::from_utf8_unchecked(&self.name) }.trim_end()
97    }
98}
99
100impl MarketExt for SpotMarket {
101    fn market_type(&self) -> &'static str {
102        "spot"
103    }
104    fn symbol(&self) -> &str {
105        unsafe { core::str::from_utf8_unchecked(&self.name) }.trim_end()
106    }
107}
108
109/// Static-ish metadata from onchain drift program
110///
111/// useful for market info suchas as pubkeys, decimal places, which rarely change.
112///
113/// it should not be relied upon for live values such as OI, total borrows, etc.
114/// instead subscribe to a marketmap
115#[derive(Clone)]
116pub struct ProgramData {
117    spot_markets: &'static [SpotMarket],
118    perp_markets: &'static [PerpMarket],
119    pub lookup_tables: &'static [AddressLookupTableAccount],
120}
121
122impl ProgramData {
123    /// Return an uninitialized instance of `ProgramData` (useful for bootstrapping)
124    pub const fn uninitialized() -> Self {
125        Self {
126            spot_markets: &[],
127            perp_markets: &[],
128            lookup_tables: &[],
129        }
130    }
131    /// Initialize `ProgramData`
132    pub fn new(
133        mut spot: Vec<SpotMarket>,
134        mut perp: Vec<PerpMarket>,
135        lookup_tables: Vec<AddressLookupTableAccount>,
136    ) -> Self {
137        spot.sort_by(|a, b| a.market_index.cmp(&b.market_index));
138        perp.sort_by(|a, b| a.market_index.cmp(&b.market_index));
139        // other code relies on aligned indexes for fast lookups
140        assert!(
141            spot.iter()
142                .enumerate()
143                .all(|(idx, x)| idx == x.market_index as usize),
144            "spot indexes unaligned"
145        );
146        assert!(
147            perp.iter()
148                .enumerate()
149                .all(|(idx, x)| idx == x.market_index as usize),
150            "perp indexes unaligned"
151        );
152
153        Self {
154            spot_markets: Box::leak(spot.into_boxed_slice()),
155            perp_markets: Box::leak(perp.into_boxed_slice()),
156            lookup_tables: Box::leak(lookup_tables.into_boxed_slice()),
157        }
158    }
159
160    /// Return known spot markets
161    pub fn spot_market_configs(&self) -> &'static [SpotMarket] {
162        self.spot_markets
163    }
164
165    /// Return known perp markets
166    pub fn perp_market_configs(&self) -> &'static [PerpMarket] {
167        self.perp_markets
168    }
169
170    /// Return the spot market config given a market index
171    pub fn spot_market_config_by_index(&self, market_index: u16) -> Option<&'static SpotMarket> {
172        self.spot_markets.get(market_index as usize)
173    }
174
175    /// Return the perp market config given a market index
176    pub fn perp_market_config_by_index(&self, market_index: u16) -> Option<&'static PerpMarket> {
177        self.perp_markets.get(market_index as usize)
178    }
179
180    /// Given some drift `MarketId`'s maps them to associated public keys
181    pub fn markets_to_accounts(&self, markets: &[MarketId]) -> Vec<Pubkey> {
182        let accounts: Vec<Pubkey> = markets
183            .iter()
184            .filter_map(|x| match x.kind() {
185                MarketType::Spot => self
186                    .spot_market_config_by_index(x.index())
187                    .map(|x| x.pubkey),
188                MarketType::Perp => self
189                    .perp_market_config_by_index(x.index())
190                    .map(|x| x.pubkey),
191            })
192            .collect();
193
194        accounts
195    }
196}
197
198/// Map oracle `source` to its owner pubkey (network depdendent)
199pub fn oracle_source_to_owner(context: Context, source: OracleSource) -> Pubkey {
200    match source {
201        OracleSource::Pyth
202        | OracleSource::Pyth1K
203        | OracleSource::Pyth1M
204        | OracleSource::PythStableCoin => context.pyth(),
205        OracleSource::PythPull
206        | OracleSource::Pyth1KPull
207        | OracleSource::Pyth1MPull
208        | OracleSource::PythStableCoinPull => ids::drift_oracle_receiver_program::ID,
209        OracleSource::Switchboard => ids::switchboard_program::ID,
210        OracleSource::SwitchboardOnDemand => ids::switchboard_on_demand::ID,
211        OracleSource::QuoteAsset => DEFAULT_PUBKEY,
212        OracleSource::Prelaunch
213        | OracleSource::PythLazer
214        | OracleSource::PythLazer1K
215        | OracleSource::PythLazer1M
216        | OracleSource::PythLazerStableCoin => PROGRAM_ID,
217    }
218}
219
220pub mod ids {
221    pub mod pyth_program {
222        use solana_sdk::pubkey::Pubkey;
223        pub const ID: Pubkey = solana_sdk::pubkey!("FsJ3A3u2vn5cTVofAjvy6y5kwABJAqYWpe4975bi2epH");
224        pub const ID_DEVNET: Pubkey =
225            solana_sdk::pubkey!("gSbePebfvPy7tRqimPoVecS2UsBvYv46ynrzWocc92s");
226    }
227
228    pub mod wormhole_program {
229        use solana_sdk::pubkey::Pubkey;
230
231        pub const ID: Pubkey = solana_sdk::pubkey!("HDwcJBJXjL9FpJ7UBsYBtaDjsBUhuLCUYoz3zr8SWWaQ");
232    }
233
234    pub mod drift_oracle_receiver_program {
235        use solana_sdk::pubkey::Pubkey;
236
237        pub const ID: Pubkey = solana_sdk::pubkey!("G6EoTTTgpkNBtVXo96EQp2m6uwwVh2Kt6YidjkmQqoha");
238    }
239
240    pub mod switchboard_program {
241        use solana_sdk::pubkey::Pubkey;
242
243        pub const ID: Pubkey = solana_sdk::pubkey!("SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f");
244    }
245
246    pub mod switchboard_on_demand {
247        use solana_sdk::pubkey::Pubkey;
248
249        pub const ID: Pubkey = solana_sdk::pubkey!("SBondMDrcV3K4kxZR1HNVT7osZxAHVHgYXL5Ze1oMUv");
250    }
251
252    pub mod bonk_oracle {
253        use solana_sdk::pubkey::Pubkey;
254
255        pub const ID: Pubkey = solana_sdk::pubkey!("8ihFLu5FimgTQ1Unh4dVyEHUGodJ5gJQCrQf4KUVB9bN");
256    }
257
258    pub mod bonk_pull_oracle {
259        use solana_sdk::pubkey::Pubkey;
260
261        pub const ID: Pubkey = solana_sdk::pubkey!("GojbSnJuPdKDT1ZuHuAM5t9oz6bxTo1xhUKpTua2F72p");
262    }
263
264    pub mod pepe_oracle {
265        use solana_sdk::pubkey::Pubkey;
266
267        pub const ID: Pubkey = solana_sdk::pubkey!("FSfxunDmjjbDV2QxpyxFCAPKmYJHSLnLuvQXDLkMzLBm");
268    }
269
270    pub mod pepe_pull_oracle {
271        use solana_sdk::pubkey::Pubkey;
272
273        pub const ID: Pubkey = solana_sdk::pubkey!("CLxofhtzvLiErpn25wvUzpZXEqBhuZ6WMEckEraxyuGt");
274    }
275
276    pub mod wen_oracle {
277        use solana_sdk::pubkey::Pubkey;
278
279        pub const ID: Pubkey = solana_sdk::pubkey!("6Uo93N83iF5U9KwC8eQpogx4XptMT4wSKfje7hB1Ufko");
280    }
281
282    pub mod wen_pull_oracle {
283        use solana_sdk::pubkey::Pubkey;
284
285        pub const ID: Pubkey = solana_sdk::pubkey!("F47c7aJgYkfKXQ9gzrJaEpsNwUKHprysregTWXrtYLFp");
286    }
287
288    pub mod usdc_oracle {
289        use solana_sdk::pubkey::Pubkey;
290
291        pub const ID: Pubkey = solana_sdk::pubkey!("Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD");
292    }
293
294    pub mod usdc_pull_oracle {
295        use solana_sdk::pubkey::Pubkey;
296
297        pub const ID: Pubkey = solana_sdk::pubkey!("En8hkHLkRe9d9DraYmBTrus518BvmVH448YcvmrFM6Ce");
298    }
299
300    pub mod jupiter_mainnet_6 {
301        use solana_sdk::pubkey::Pubkey;
302
303        pub const ID: Pubkey = solana_sdk::pubkey!("JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4");
304    }
305    pub mod jupiter_mainnet_4 {
306        use solana_sdk::pubkey::Pubkey;
307
308        pub const ID: Pubkey = solana_sdk::pubkey!("JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB");
309    }
310    pub mod jupiter_mainnet_3 {
311        use solana_sdk::pubkey::Pubkey;
312
313        pub const ID: Pubkey = solana_sdk::pubkey!("JUP3c2Uh3WA4Ng34tw6kPd2G4C5BB21Xo36Je1s32Ph");
314    }
315
316    pub mod marinade_mainnet {
317        use solana_sdk::pubkey::Pubkey;
318
319        pub const ID: Pubkey = solana_sdk::pubkey!("MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD");
320    }
321
322    pub mod usdt_oracle {
323        use solana_sdk::pubkey::Pubkey;
324
325        pub const ID: Pubkey = solana_sdk::pubkey!("3vxLXJqLqF3JG5TCbYycbKWRBbCJQLxQmBGCkyqEEefL");
326    }
327
328    pub mod usdt_pull_oracle {
329        use solana_sdk::pubkey::Pubkey;
330
331        pub const ID: Pubkey = solana_sdk::pubkey!("BekJ3P5G3iFeC97sXHuKnUHofCFj9Sbo7uyF2fkKwvit");
332    }
333
334    pub mod admin_hot_wallet {
335        use solana_sdk::pubkey::Pubkey;
336
337        pub const ID: Pubkey = solana_sdk::pubkey!("5hMjmxexWu954pX9gB9jkHxMqdjpxArQS2XdvkaevRax");
338    }
339}