1mod cache;
8mod drift;
9mod pyra;
10
11pub use cache::Cache;
12pub use drift::{
13 DriftUser, HistoricalOracleData, InsuranceFund, SpotBalanceType, SpotMarket, SpotPosition,
14};
15pub use pyra::{
16 DepositAddressSolAccount, DepositAddressSplAccount, SpendLimitsOrderAccount, TimeLock, Vault,
17 WithdrawOrderAccount,
18};
19
20pub type VaultCache = Cache<Vault>;
21pub type SpotMarketCache = Cache<SpotMarket>;
22pub type DriftUserCache = Cache<DriftUser>;
23pub type WithdrawOrderCache = Cache<WithdrawOrderAccount>;
24pub type SpendLimitsOrderCache = Cache<SpendLimitsOrderAccount>;
25pub type DepositAddressSplCache = Cache<DepositAddressSplAccount>;
26pub type DepositAddressSolCache = Cache<DepositAddressSolAccount>;
27
28#[cfg(test)]
29mod tests {
30 use super::*;
31
32 #[test]
33 fn cache_deserialize_with_slot() {
34 let json = r#"{"account":{"spot_positions":[]},"last_updated_slot":285847350}"#;
35 let cache: Cache<DriftUser> = serde_json::from_str(json).unwrap();
36 assert_eq!(cache.last_updated_slot, 285847350);
37 assert!(cache.account.spot_positions.is_empty());
38 }
39
40 #[test]
41 fn cache_deserialize_without_slot() {
42 let json = r#"{"account":{"spot_positions":[]}}"#;
43 let cache: Cache<DriftUser> = serde_json::from_str(json).unwrap();
44 assert_eq!(cache.last_updated_slot, 0);
45 }
46
47 #[test]
48 fn spot_market_partial_fields() {
49 let json = r#"{"market_index":1,"decimals":6,"cumulative_deposit_interest":100,"cumulative_borrow_interest":50,"initial_asset_weight":8000,"initial_liability_weight":12000,"imf_factor":0,"scale_initial_asset_weight_start":0}"#;
52 let market: SpotMarket = serde_json::from_str(json).unwrap();
53 assert_eq!(market.market_index, 1);
54 assert_eq!(market.decimals, 6);
55 assert_eq!(market.initial_asset_weight, 8000);
56 assert_eq!(market.initial_liability_weight, 12000);
57 assert_eq!(market.deposit_balance, 0);
59 }
60
61 #[test]
62 fn spot_market_missing_core_field_fails() {
63 let json = r#"{"market_index":1}"#;
65 let result = serde_json::from_str::<SpotMarket>(json);
66 assert!(result.is_err());
67
68 let json = r#"{"market_index":1,"decimals":6,"cumulative_deposit_interest":100,"cumulative_borrow_interest":50}"#;
70 let result = serde_json::from_str::<SpotMarket>(json);
71 assert!(result.is_err());
72 }
73
74 #[test]
75 fn spot_position_with_balance_type() {
76 let json = r#"{"scaled_balance":1000000,"market_index":0,"balance_type":"Deposit"}"#;
77 let pos: SpotPosition = serde_json::from_str(json).unwrap();
78 assert_eq!(pos.scaled_balance, 1000000);
79 assert_eq!(pos.balance_type, SpotBalanceType::Deposit);
80 }
81
82 #[test]
83 fn spot_position_borrow() {
84 let json =
85 r#"{"scaled_balance":500,"market_index":1,"balance_type":"Borrow","open_orders":2}"#;
86 let pos: SpotPosition = serde_json::from_str(json).unwrap();
87 assert_eq!(pos.balance_type, SpotBalanceType::Borrow);
88 assert_eq!(pos.open_orders, 2);
89 }
90
91 #[test]
92 fn drift_user_with_positions() {
93 let json = r#"{
94 "spot_positions": [
95 {"scaled_balance":1000,"market_index":0,"balance_type":"Deposit"},
96 {"scaled_balance":500,"market_index":1,"balance_type":"Borrow"}
97 ]
98 }"#;
99 let user: DriftUser = serde_json::from_str(json).unwrap();
100 assert_eq!(user.spot_positions.len(), 2);
101 assert_eq!(user.spot_positions[0].market_index, 0);
102 assert_eq!(user.spot_positions[1].balance_type, SpotBalanceType::Borrow);
103 }
104
105 #[test]
106 fn vault_all_fields_required() {
107 let json = r#"{"owner":[1,2,3],"bump":1,"spend_limit_per_transaction":100,"spend_limit_per_timeframe":1000,"remaining_spend_limit_per_timeframe":500,"next_timeframe_reset_timestamp":12345,"timeframe_in_seconds":86400}"#;
108 let vault: Vault = serde_json::from_str(json).unwrap();
109 assert_eq!(vault.owner, vec![1, 2, 3]);
110 assert_eq!(vault.spend_limit_per_transaction, 100);
111 assert_eq!(vault.timeframe_in_seconds, 86400);
112 }
113
114 #[test]
115 fn vault_missing_field_fails() {
116 let json = r#"{"owner":[1,2,3]}"#;
118 let result = serde_json::from_str::<Vault>(json);
119 assert!(result.is_err());
120 }
121
122 #[test]
123 fn time_lock_deserializes_snake_case() {
124 let zero_pubkey = serde_json::to_string(&solana_pubkey::Pubkey::default()).unwrap();
126 let json = format!(
127 r#"{{"owner":{pk},"payer":{pk},"release_slot":42}}"#,
128 pk = zero_pubkey
129 );
130 let tl: TimeLock = serde_json::from_str(&json).unwrap();
131 assert_eq!(tl.release_slot, 42);
132 }
133
134 #[test]
135 fn time_lock_serializes_camel_case() {
136 let tl = TimeLock {
137 owner: solana_pubkey::Pubkey::default(),
138 payer: solana_pubkey::Pubkey::default(),
139 release_slot: 42,
140 };
141 let json = serde_json::to_string(&tl).unwrap();
142 assert!(json.contains("releaseSlot"));
143 assert!(!json.contains("release_slot"));
144 }
145
146 #[test]
147 fn withdraw_order_deserializes_snake_case() {
148 let pk = serde_json::to_string(&solana_pubkey::Pubkey::default()).unwrap();
149 let json = format!(
150 concat!(
151 r#"{{"time_lock":{{"owner":{pk},"payer":{pk},"release_slot":100}},"#,
152 r#""amount_base_units":5000,"drift_market_index":0,"reduce_only":false,"#,
153 r#""destination":{pk}}}"#,
154 ),
155 pk = pk
156 );
157 let order: WithdrawOrderAccount = serde_json::from_str(&json).unwrap();
158 assert_eq!(order.amount_base_units, 5000);
159 assert_eq!(order.drift_market_index, 0);
160 }
161
162 #[test]
163 fn spot_market_roundtrip() {
164 let market = SpotMarket {
165 pubkey: vec![],
166 market_index: 1,
167 initial_asset_weight: 8000,
168 initial_liability_weight: 0,
169 imf_factor: 0,
170 scale_initial_asset_weight_start: 0,
171 decimals: 9,
172 cumulative_deposit_interest: 1_050_000_000_000,
173 cumulative_borrow_interest: 0,
174 deposit_balance: 0,
175 borrow_balance: 0,
176 optimal_utilization: 0,
177 optimal_borrow_rate: 0,
178 max_borrow_rate: 0,
179 min_borrow_rate: 0,
180 insurance_fund: InsuranceFund::default(),
181 historical_oracle_data: HistoricalOracleData::default(),
182 oracle: None,
183 };
184 let json = serde_json::to_string(&market).unwrap();
185 let deserialized: SpotMarket = serde_json::from_str(&json).unwrap();
186 assert_eq!(deserialized.market_index, 1);
187 assert_eq!(deserialized.cumulative_deposit_interest, 1_050_000_000_000);
188 }
189
190 #[test]
191 fn cache_spot_market_roundtrip() {
192 let json = r#"{"account":{"market_index":0,"decimals":6,"cumulative_deposit_interest":100,"cumulative_borrow_interest":50,"initial_asset_weight":8000,"initial_liability_weight":12000,"imf_factor":0,"scale_initial_asset_weight_start":0},"last_updated_slot":12345}"#;
193 let deserialized: Cache<SpotMarket> = serde_json::from_str(json).unwrap();
194 assert_eq!(deserialized.account.market_index, 0);
195 assert_eq!(deserialized.last_updated_slot, 12345);
196 }
197
198 #[test]
199 fn spot_market_ignores_unknown_fields() {
200 let json = r#"{"market_index":1,"some_future_field":"value","decimals":6,"cumulative_deposit_interest":0,"cumulative_borrow_interest":0,"initial_asset_weight":8000,"initial_liability_weight":12000,"imf_factor":0,"scale_initial_asset_weight_start":0}"#;
201 let market: SpotMarket = serde_json::from_str(json).unwrap();
203 assert_eq!(market.market_index, 1);
204 }
205}