1use crate::utils;
2use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey};
3use std::cell::Ref;
4use std::fmt::{Display, Formatter, Result as FormatResult};
5use std::ops::{Add, Mul, Sub};
6
7pub const NATIVE_RAW_SHIFT: usize = 24;
8
9#[repr(packed)]
10#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
11pub struct RawAmt {
15 amt: u128,
16}
17
18impl RawAmt {
19 pub fn to_native_amount(&self) -> u64 {
20 (self.amt >> NATIVE_RAW_SHIFT) as u64
21 }
22}
23
24pub const MAX_ASSETS_PER_USER: usize = 16;
25
26#[repr(packed)]
27#[derive(Copy, Clone)]
28pub struct UserAssetInfo {
29 pub pool_id: u8,
30 pub use_as_collateral: u8,
31
32 pub deposit_amount: RawAmt,
33 pub deposit_interests: u64, pub deposit_index: f64,
35 pub reward_deposit_amount: f64, pub reward_deposit_index: f64,
37
38 pub borrow_amount: RawAmt,
39 pub borrow_interests: u64, pub borrow_index: f64,
41 pub reward_borrow_amount: f64, pub reward_borrow_index: f64,
43}
44
45impl Display for UserAssetInfo {
46 #[allow(unaligned_references)]
47 fn fmt(&self, f: &mut Formatter<'_>) -> FormatResult {
48 writeln!(
49 f,
50 "pool_id: {}, use_as_collateral: {}",
51 self.pool_id, self.use_as_collateral
52 )?;
53 writeln!(
54 f,
55 "deposit_native_amount: {}, deposit_native_interest: {}, deposit_apt_reward_native_amount: {}",
56 self.deposit_amount.to_native_amount(), self.deposit_interests, self.reward_deposit_amount
57 )?;
58 writeln!(
59 f,
60 "borrow_native_amount: {}, borrow_native_interest: {}, borrow_apt_reward_native_amount: {}",
61 self.borrow_amount.to_native_amount(), self.borrow_interests, self.reward_borrow_amount
62 )?;
63 Ok(())
64 }
65}
66
67#[repr(packed)]
68#[derive(Copy, Clone)]
69pub struct RewardInfo {
70 pub vesting: [f64; 4], pub prev_week_apt: f64,
72 pub unused: [f64; 2],
73 pub vesting_apt: f64,
74 pub available_apt: f64,
75 pub available_mnde: f64,
76 pub available_wldo: f64, pub available_b180socn: f64, pub available_wluna: f64, }
80
81impl Display for RewardInfo {
82 #[allow(unaligned_references)]
83 fn fmt(&self, f: &mut Formatter<'_>) -> FormatResult {
84 writeln!(
85 f,
86 "earning_apt: {}, vesting_apt: {}",
87 self.prev_week_apt, self.vesting_apt
88 )?;
89 writeln!(
90 f,
91 "available_apt: {}, available_mnde: {}, available_wldo: {}, available_b180socn: {}, available_wluna: {}",
92 self.available_apt,
93 self.available_mnde,
94 self.available_wldo,
95 self.available_b180socn,
96 self.available_wluna
97 )?;
98 Ok(())
99 }
100}
101
102#[repr(packed)]
103#[derive(Copy, Clone)]
104pub struct UserInfo {
105 pub page_id: u16,
106 pub num_assets: u8,
107 pub user_asset_info: [UserAssetInfo; MAX_ASSETS_PER_USER],
108 pub reward: RewardInfo,
109 pub pad: [u8; 8],
110 pub last_vest_cutoff_timestamp: u64,
111 pub last_update_timestamp: u64,
112}
113
114impl UserInfo {
115 pub fn from_account_info<'a>(acc_info: &'a AccountInfo) -> Result<Ref<'a, Self>, ProgramError> {
116 let data_ref = acc_info.try_borrow_data()?;
117 let bytes_ref = Ref::map(data_ref, |d| *d);
118 let user_info = Ref::map(bytes_ref, |b| utils::cast::<UserInfo>(b));
119 Ok(user_info)
120 }
121
122 pub fn from_bytes(data: &[u8]) -> &Self {
123 utils::cast::<Self>(data)
124 }
125}
126
127impl Display for UserInfo {
128 #[allow(unaligned_references)]
129 fn fmt(&self, f: &mut Formatter<'_>) -> FormatResult {
130 writeln!(
131 f,
132 "page_id: {}, num_assets: {}",
133 self.page_id, self.num_assets
134 )?;
135 for i in 0..self.num_assets as usize {
136 writeln!(f, "user_asset_info: {}", self.user_asset_info[i])?;
137 }
138 writeln!(f, "reward: {}", self.reward)?;
139 writeln!(
140 f,
141 "last_vest_cutoff_timestamp: {}, last_update_timestamp: {}",
142 self.last_vest_cutoff_timestamp, self.last_update_timestamp
143 )?;
144 Ok(())
145 }
146}
147
148pub const TOKEN_NAME_SIZE: usize = 32;
149#[repr(packed)]
150#[derive(Copy, Clone)]
151pub struct AssetPool {
152 pub token_name: [u8; TOKEN_NAME_SIZE],
153
154 pub mint_key: Pubkey,
155 pub mint_decimal_multiplier: u64,
157 pub pool_id: u8,
158
159 pub deposit_amount: RawAmt,
161 pub deposit_index: f64,
162
163 pub borrow_amount: RawAmt,
165 pub borrow_index: f64,
166
167 pub reserve_factor: f64,
169 pub fee_amount: RawAmt,
170 pub fee_withdrawn_amount: u64,
171 pub current_fee_rate: f64,
172
173 pub last_update_time: u64,
174
175 pub spl_key: Pubkey,
177 pub atoken_mint_key: Pubkey,
178 pub asset_price_key: Pubkey,
179 pub pyth_price_key: Pubkey,
180
181 pub serum_next_cl_id: u64,
183
184 pub ltv: f64,
185 pub safe_factor: f64,
186 pub flags: u8,
187
188 pub base_rate: f64,
190 pub multiplier: f64,
191 pub jump_multiplier: f64,
192 pub kink: f64,
193 pub current_borrow_rate: f64, pub current_deposit_rate: f64, pub reward_multiplier: f64,
197 pub reward_deposit_intra_share: f64, pub reward_apr_per_year: u64,
201 pub deposit_apt_reward_amount_per_year: u64,
202 pub borrow_apt_reward_amount_per_year: u64,
203 pub apt_reward_per_year_per_deposit: f64,
204 pub apt_reward_per_year_per_borrow: f64,
205
206 pub reward_deposit_index: f64,
208 pub reward_borrow_index: f64,
209
210 pub deposit_cap: u64,
211 pub is_disabled: u8,
212
213 pub farm_yield: f64,
214}
215
216impl AssetPool {
217 pub fn from_account_info<'a>(acc_info: &'a AccountInfo) -> Result<Ref<'a, Self>, ProgramError> {
218 let data_ref = acc_info.try_borrow_data()?;
219 let bytes_ref = Ref::map(data_ref, |d| *d);
220 let asset_pool = Ref::map(bytes_ref, |b| utils::cast::<AssetPool>(b));
221 Ok(asset_pool)
222 }
223
224 pub fn from_bytes(data: &[u8]) -> &Self {
225 utils::cast::<Self>(data)
226 }
227
228 pub fn calculate_new_interest_rate(
229 self,
230 deposit_native_amt: u64,
231 borrow_native_amt: u64
232 ) -> (f64, f64) {
233 let new_deposit_native_amt = deposit_native_amt.add(self.deposit_amount.to_native_amount()) as f64;
234 let new_borrow_native_amt = borrow_native_amt.add(self.borrow_amount.to_native_amount()) as f64;
235
236 Self::calculate_interest_rate(
237 new_deposit_native_amt,
238 new_borrow_native_amt,
239 self.base_rate,
240 self.multiplier,
241 self.jump_multiplier,
242 self.kink,
243 self.reserve_factor
244 )
245 }
246
247 pub fn calculate_interest_rate(
248 deposit_amt: f64,
249 borrow_amt: f64,
250 base_rate: f64,
251 multiplier: f64,
252 jump_multiplier: f64,
253 kink: f64,
254 reserve_factor: f64,
255 ) -> (f64, f64) {
256 let utilization_rate = if deposit_amt == 0.0 {
257 0.0
258 } else {
259 borrow_amt / deposit_amt
260 };
261 let mut borrow_rate = base_rate;
262 if utilization_rate <= kink {
263 borrow_rate = borrow_rate.add(multiplier.mul(utilization_rate));
264 } else {
265 borrow_rate = borrow_rate.add(multiplier.mul(kink));
266 borrow_rate = borrow_rate.add(jump_multiplier.mul(utilization_rate.sub(kink)));
267 }
268
269 let deposit_rate = borrow_rate.mul(utilization_rate).mul(1.0 - reserve_factor);
270 (deposit_rate, borrow_rate)
271 }
272}
273
274impl Display for AssetPool {
275 #[allow(unaligned_references)]
276 fn fmt(&self, f: &mut Formatter<'_>) -> FormatResult {
277 writeln!(f, "pool_id: {}", self.pool_id)?;
278 writeln!(f, "mint: {}", self.mint_key)?;
279 writeln!(f, "last_update_time: {}", self.last_update_time)?;
280 writeln!(f, "ltv: {}", self.ltv)?;
281 writeln!(f, "safe_factor: {}", self.safe_factor)?;
282 writeln!(f, "deposit_cap: {}", self.deposit_cap)?;
283 writeln!(
284 f,
285 "deposit_amount: {}",
286 self.deposit_amount.to_native_amount()
287 )?;
288 writeln!(f, "deposit_interest_rate: {}", self.current_deposit_rate)?;
289 writeln!(
290 f,
291 "deposit_apt_reward_amount_per_year: {}",
292 self.deposit_apt_reward_amount_per_year
293 )?;
294 writeln!(
295 f,
296 "borrow_amount: {}",
297 self.borrow_amount.to_native_amount()
298 )?;
299 writeln!(f, "borrow_interest_rate: {}", self.current_borrow_rate)?;
300 writeln!(
301 f,
302 "borrow_apt_reward_amount_per_year: {}",
303 self.borrow_apt_reward_amount_per_year
304 )?;
305 writeln!(f, "farm_yield: {}", self.farm_yield)?;
306 Ok(())
307 }
308}
309
310#[cfg(test)]
311pub mod asset_pool_test {
312 use super::*;
313
314 #[test]
315 fn test_calculate_interest_rate() {
316 let (mut deposit_rate, mut borrow_rate) = AssetPool::calculate_interest_rate(
317 6674310936768f64,
318 4307894688295f64,
319 0.01,
320 0.0823529411764706,
321 6.133333333333333,
322 0.85,
323 0.2,
324 );
325
326 assert!(0.032610 - deposit_rate < 1.0e-6);
327 assert!(0.063154 - borrow_rate < 1.0e-6);
328
329 (deposit_rate, borrow_rate) = AssetPool::calculate_interest_rate(
330 (6674310936768u64 + 10_000_000_000) as f64,
331 (4307894688295u64 + 100_000_000) as f64,
332 0.01,
333 0.0823529411764706,
334 6.133333333333333,
335 0.85,
336 0.2,
337 );
338
339 assert!(0.032522 - deposit_rate < 1.0e-6, "deposit_rate:{} doesn't match", deposit_rate);
340 assert!(0.063076 - borrow_rate < 1.0e-6, "borrow_rate:{} doesn't match", borrow_rate);
341 }
342}