Skip to main content

klend_interface/state/
reserve.rs

1use bytemuck::{Pod, Zeroable};
2use solana_pubkey::Pubkey;
3use spl_discriminator::SplDiscriminate;
4
5use super::{
6    common::{BigFractionBytes, LastUpdate},
7    pod::PodU128,
8};
9
10// ---------------------------------------------------------------------------
11// Reserve
12// ---------------------------------------------------------------------------
13
14/// Lending reserve account state.
15#[derive(Debug, Clone, Copy, Pod, Zeroable, SplDiscriminate)]
16#[discriminator_hash_input("account:Reserve")]
17#[repr(C)]
18pub struct Reserve {
19    pub version: u64,
20    pub last_update: LastUpdate,
21    pub lending_market: Pubkey,
22    pub farm_collateral: Pubkey,
23    pub farm_debt: Pubkey,
24    pub liquidity: ReserveLiquidity,
25    pub reserve_liquidity_padding: [u64; 150],
26    pub collateral: ReserveCollateral,
27    pub reserve_collateral_padding: [u64; 150],
28    pub config: ReserveConfig,
29    pub config_padding: [u64; 112],
30    pub borrowed_amount_outside_elevation_group: u64,
31    pub borrowed_amounts_against_this_reserve_in_elevation_groups: [u64; 32],
32    pub withdraw_queue: WithdrawQueue,
33    pub padding: [u64; 204],
34}
35
36const _: () = assert!(core::mem::size_of::<Reserve>() == 8616);
37
38impl Reserve {
39    /// Total available (unborrowed) liquidity in native token units.
40    pub fn available_liquidity(&self) -> u64 {
41        self.liquidity.total_available_amount
42    }
43
44    /// Total borrowed amount as a raw u128 scaled fraction.
45    pub fn borrowed_amount(&self) -> u128 {
46        u128::from(self.liquidity.borrowed_amount_sf)
47    }
48
49    /// Market price of the reserve token as a raw u128 scaled fraction.
50    pub fn market_price(&self) -> u128 {
51        u128::from(self.liquidity.market_price_sf)
52    }
53
54    /// Decimals of the liquidity mint.
55    pub fn mint_decimals(&self) -> u64 {
56        self.liquidity.mint_decimals
57    }
58
59    /// Total supply of collateral tokens (cTokens).
60    pub fn collateral_total_supply(&self) -> u64 {
61        self.collateral.mint_total_supply
62    }
63
64    /// Accumulated protocol fees as a raw u128 scaled fraction.
65    pub fn accumulated_protocol_fees(&self) -> u128 {
66        u128::from(self.liquidity.accumulated_protocol_fees_sf)
67    }
68
69    /// Accumulated referrer fees as a raw u128 scaled fraction.
70    pub fn accumulated_referrer_fees(&self) -> u128 {
71        u128::from(self.liquidity.accumulated_referrer_fees_sf)
72    }
73
74    /// Reserve status: 0 = Active, 1 = Obsolete, 2 = Hidden.
75    pub fn status(&self) -> u8 {
76        self.config.status
77    }
78
79    /// Whether the reserve is in emergency mode.
80    pub fn is_emergency_mode(&self) -> bool {
81        self.config.emergency_mode != 0
82    }
83
84    /// Loan-to-value percentage.
85    pub fn loan_to_value_pct(&self) -> u8 {
86        self.config.loan_to_value_pct
87    }
88
89    /// Liquidation threshold percentage.
90    pub fn liquidation_threshold_pct(&self) -> u8 {
91        self.config.liquidation_threshold_pct
92    }
93
94    /// Borrow factor percentage (100 = 1x).
95    pub fn borrow_factor_pct(&self) -> u64 {
96        self.config.borrow_factor_pct
97    }
98
99    /// Deposit limit in native token units.
100    pub fn deposit_limit(&self) -> u64 {
101        self.config.deposit_limit
102    }
103
104    /// Borrow limit in native token units.
105    pub fn borrow_limit(&self) -> u64 {
106        self.config.borrow_limit
107    }
108
109    /// Next sequence number for withdraw tickets.
110    pub fn next_withdraw_ticket_sequence_number(&self) -> u64 {
111        self.withdraw_queue.next_issued_ticket_sequence_number
112    }
113}
114
115// ---------------------------------------------------------------------------
116// ReserveLiquidity
117// ---------------------------------------------------------------------------
118
119/// Reserve liquidity state.
120#[derive(Debug, Clone, Copy, Pod, Zeroable)]
121#[repr(C)]
122pub struct ReserveLiquidity {
123    pub mint_pubkey: Pubkey,
124    pub supply_vault: Pubkey,
125    pub fee_vault: Pubkey,
126    pub total_available_amount: u64,
127    /// Total borrowed (scaled fraction).
128    pub borrowed_amount_sf: PodU128,
129    /// Market price in quote currency (scaled fraction).
130    pub market_price_sf: PodU128,
131    pub market_price_last_updated_ts: u64,
132    pub mint_decimals: u64,
133    pub deposit_limit_crossed_timestamp: u64,
134    pub borrow_limit_crossed_timestamp: u64,
135    /// Cumulative borrow rate (big scaled fraction).
136    pub cumulative_borrow_rate_bsf: BigFractionBytes,
137    /// Protocol fees (scaled fraction).
138    pub accumulated_protocol_fees_sf: PodU128,
139    /// Referrer fees (scaled fraction).
140    pub accumulated_referrer_fees_sf: PodU128,
141    /// Pending referrer fees (scaled fraction).
142    pub pending_referrer_fees_sf: PodU128,
143    /// Referral rate (scaled fraction).
144    pub absolute_referral_rate_sf: PodU128,
145    pub token_program: Pubkey,
146    /// Reserve rewards budget remaining for distribution
147    pub rewards_amount_available: u64,
148    pub padding2: [u64; 50],
149    pub padding3: [PodU128; 32],
150}
151
152// ---------------------------------------------------------------------------
153// ReserveCollateral
154// ---------------------------------------------------------------------------
155
156/// Reserve collateral state.
157#[derive(Debug, Clone, Copy, Pod, Zeroable)]
158#[repr(C)]
159pub struct ReserveCollateral {
160    pub mint_pubkey: Pubkey,
161    pub mint_total_supply: u64,
162    pub supply_vault: Pubkey,
163    pub padding1: [PodU128; 32],
164    pub padding2: [PodU128; 32],
165}
166
167// ---------------------------------------------------------------------------
168// ReserveConfig
169// ---------------------------------------------------------------------------
170
171/// Reserve configuration parameters.
172#[derive(Debug, Clone, Copy, Pod, Zeroable)]
173#[repr(C)]
174pub struct ReserveConfig {
175    /// 0 = Active, 1 = Obsolete, 2 = Hidden.
176    pub status: u8,
177    pub padding_deprecated_asset_tier: u8,
178    pub host_fixed_interest_rate_bps: u16,
179    pub min_deleveraging_bonus_bps: u16,
180    pub block_ctoken_usage: u8,
181    pub early_repay_remaining_interest_pct: u8,
182    /// Whether the reserve is in emergency mode.
183    pub emergency_mode: u8,
184    pub reserved_1: [u8; 4],
185    pub protocol_order_execution_fee_pct: u8,
186    pub protocol_take_rate_pct: u8,
187    pub protocol_liquidation_fee_pct: u8,
188    pub loan_to_value_pct: u8,
189    pub liquidation_threshold_pct: u8,
190    pub min_liquidation_bonus_bps: u16,
191    pub max_liquidation_bonus_bps: u16,
192    pub bad_debt_liquidation_bonus_bps: u16,
193    pub deleveraging_margin_call_period_secs: u64,
194    pub deleveraging_threshold_decrease_bps_per_day: u64,
195    pub fees: ReserveFees,
196    pub borrow_rate_curve: BorrowRateCurve,
197    pub borrow_factor_pct: u64,
198    pub deposit_limit: u64,
199    pub borrow_limit: u64,
200    pub token_info: TokenInfo,
201    pub deposit_withdrawal_cap: WithdrawalCaps,
202    pub debt_withdrawal_cap: WithdrawalCaps,
203    pub elevation_groups: [u8; 20],
204    pub disable_usage_as_coll_outside_emode: u8,
205    pub utilization_limit_block_borrowing_above_pct: u8,
206    pub autodeleverage_enabled: u8,
207    pub proposer_authority_locked: u8,
208    pub borrow_limit_outside_elevation_group: u64,
209    pub borrow_limit_against_this_collateral_in_elevation_group: [u64; 32],
210    pub deleveraging_bonus_increase_bps_per_day: u64,
211    pub debt_maturity_timestamp: u64,
212    pub debt_term_seconds: u64,
213    /// Rewards token amount distributed per slot to depositors
214    pub rewards_amount_per_slot: u64,
215    pub permissioned_ops: u64,
216}
217
218const _: () = assert!(core::mem::size_of::<ReserveConfig>() == 952);
219
220// ---------------------------------------------------------------------------
221// ReserveFees
222// ---------------------------------------------------------------------------
223
224#[derive(Debug, Clone, Copy, Zeroable, Pod)]
225#[repr(C)]
226pub struct ReserveFees {
227    /// Borrow origination fee (scaled fraction).
228    pub origination_fee_sf: u64,
229    /// Flash loan fee (u64::MAX = disabled).
230    pub flash_loan_fee_sf: u64,
231    pub padding: [u8; 8],
232}
233
234// ---------------------------------------------------------------------------
235// BorrowRateCurve / CurvePoint
236// ---------------------------------------------------------------------------
237
238#[derive(Debug, Clone, Copy, Zeroable, Pod)]
239#[repr(C)]
240pub struct BorrowRateCurve {
241    pub points: [CurvePoint; 11],
242}
243
244#[derive(Debug, Clone, Copy, Zeroable, Pod)]
245#[repr(C)]
246pub struct CurvePoint {
247    pub utilization_rate_bps: u32,
248    pub borrow_rate_bps: u32,
249}
250
251// ---------------------------------------------------------------------------
252// TokenInfo and oracle configs
253// ---------------------------------------------------------------------------
254
255#[derive(Debug, Clone, Copy, Pod, Zeroable)]
256#[repr(C)]
257pub struct TokenInfo {
258    pub name: [u8; 32],
259    pub heuristic: PriceHeuristic,
260    pub max_twap_divergence_bps: u64,
261    pub max_age_price_seconds: u64,
262    pub max_age_twap_seconds: u64,
263    pub scope_configuration: ScopeConfiguration,
264    pub switchboard_configuration: SwitchboardConfiguration,
265    pub pyth_configuration: PythConfiguration,
266    pub block_price_usage: u8,
267    pub reserved: [u8; 7],
268    pub _padding: [u64; 19],
269}
270
271const _: () = assert!(core::mem::size_of::<TokenInfo>() == 384);
272
273impl TokenInfo {
274    /// Token name as a UTF-8 string, trimmed of null padding.
275    ///
276    /// Returns `None` if the name bytes are not valid UTF-8.
277    pub fn name_str(&self) -> Option<&str> {
278        let end = self
279            .name
280            .iter()
281            .position(|&b| b == 0)
282            .unwrap_or(self.name.len());
283        core::str::from_utf8(&self.name[..end]).ok()
284    }
285}
286
287#[derive(Debug, Clone, Copy, Zeroable, Pod)]
288#[repr(C)]
289pub struct PriceHeuristic {
290    pub lower: u64,
291    pub upper: u64,
292    pub exp: u64,
293}
294
295#[derive(Debug, Clone, Copy, Zeroable, Pod)]
296#[repr(C)]
297pub struct ScopeConfiguration {
298    pub price_feed: Pubkey,
299    pub price_chain: [u16; 4],
300    pub twap_chain: [u16; 4],
301}
302
303#[derive(Debug, Clone, Copy, Zeroable, Pod)]
304#[repr(C)]
305pub struct SwitchboardConfiguration {
306    pub price_aggregator: Pubkey,
307    pub twap_aggregator: Pubkey,
308}
309
310#[derive(Debug, Clone, Copy, Zeroable, Pod)]
311#[repr(C)]
312pub struct PythConfiguration {
313    pub price: Pubkey,
314}
315
316// ---------------------------------------------------------------------------
317// WithdrawalCaps
318// ---------------------------------------------------------------------------
319
320#[derive(Debug, Clone, Copy, Zeroable, Pod)]
321#[repr(C)]
322pub struct WithdrawalCaps {
323    pub config_capacity: i64,
324    pub current_total: i64,
325    pub last_interval_start_timestamp: u64,
326    pub config_interval_length_seconds: u64,
327}
328
329// ---------------------------------------------------------------------------
330// WithdrawQueue
331// ---------------------------------------------------------------------------
332
333#[derive(Debug, Clone, Copy, Zeroable, Pod)]
334#[repr(C)]
335pub struct WithdrawQueue {
336    pub queued_collateral_amount: u64,
337    pub next_issued_ticket_sequence_number: u64,
338    pub next_withdrawable_ticket_sequence_number: u64,
339}