Skip to main content

klend_interface/state/
obligation.rs

1use bytemuck::{Pod, Zeroable};
2use solana_pubkey::Pubkey;
3use spl_discriminator::SplDiscriminate;
4use spl_pod::primitives::PodU128;
5
6use super::common::{BigFractionBytes, LastUpdate};
7
8// ---------------------------------------------------------------------------
9// Obligation
10// ---------------------------------------------------------------------------
11
12/// Lending market obligation state.
13#[derive(Debug, Clone, Copy, Pod, Zeroable, SplDiscriminate)]
14#[discriminator_hash_input("account:Obligation")]
15#[repr(C)]
16pub struct Obligation {
17    pub tag: u64,
18    pub last_update: LastUpdate,
19    pub lending_market: Pubkey,
20    pub owner: Pubkey,
21    /// Deposited collateral (up to 8 positions).
22    pub deposits: [ObligationCollateral; 8],
23    pub lowest_reserve_deposit_liquidation_ltv: u64,
24    /// Market value of deposits (scaled fraction).
25    pub deposited_value_sf: PodU128,
26    /// Borrowed liquidity (up to 5 positions).
27    pub borrows: [ObligationLiquidity; 5],
28    /// Risk-adjusted debt value (scaled fraction).
29    pub borrow_factor_adjusted_debt_value_sf: PodU128,
30    /// Market value of borrows (scaled fraction).
31    pub borrowed_assets_market_value_sf: PodU128,
32    /// Max borrow value at weighted-avg LTV (scaled fraction).
33    pub allowed_borrow_value_sf: PodU128,
34    /// Dangerous borrow value at liquidation threshold (scaled fraction).
35    pub unhealthy_borrow_value_sf: PodU128,
36    pub padding_deprecated_asset_tiers: [u8; 13],
37    pub elevation_group: u8,
38    pub num_of_obsolete_deposit_reserves: u8,
39    /// 1 if borrows array is non-empty.
40    pub has_debt: u8,
41    pub referrer: Pubkey,
42    pub borrowing_disabled: u8,
43    pub autodeleverage_target_ltv_pct: u8,
44    pub lowest_reserve_deposit_max_ltv_pct: u8,
45    pub num_of_obsolete_borrow_reserves: u8,
46    pub reserved: [u8; 4],
47    pub highest_borrow_factor_pct: u64,
48    pub autodeleverage_margin_call_started_timestamp: u64,
49    pub obligation_orders: [ObligationOrder; 2],
50    pub borrow_order: BorrowOrder,
51    pub padding_3: [u64; 73],
52}
53
54const _: () = assert!(core::mem::size_of::<Obligation>() == 3336);
55
56impl Obligation {
57    /// Number of active deposit positions.
58    pub fn num_deposits(&self) -> usize {
59        self.deposits
60            .iter()
61            .filter(|d| d.deposit_reserve != Pubkey::default())
62            .count()
63    }
64
65    /// Number of active borrow positions.
66    pub fn num_borrows(&self) -> usize {
67        self.borrows
68            .iter()
69            .filter(|b| b.borrow_reserve != Pubkey::default())
70            .count()
71    }
72
73    /// Total deposited value as a raw u128 scaled fraction.
74    pub fn deposited_value(&self) -> u128 {
75        u128::from(self.deposited_value_sf)
76    }
77
78    /// Allowed borrow value (weighted-avg LTV) as a raw u128 scaled fraction.
79    pub fn allowed_borrow_value(&self) -> u128 {
80        u128::from(self.allowed_borrow_value_sf)
81    }
82
83    /// Unhealthy borrow value (liquidation threshold) as a raw u128 scaled fraction.
84    pub fn unhealthy_borrow_value(&self) -> u128 {
85        u128::from(self.unhealthy_borrow_value_sf)
86    }
87
88    /// Borrow-factor-adjusted debt value as a raw u128 scaled fraction.
89    pub fn borrow_factor_adjusted_debt_value(&self) -> u128 {
90        u128::from(self.borrow_factor_adjusted_debt_value_sf)
91    }
92
93    /// Market value of borrowed assets as a raw u128 scaled fraction.
94    pub fn borrowed_assets_market_value(&self) -> u128 {
95        u128::from(self.borrowed_assets_market_value_sf)
96    }
97
98    /// Returns `true` if the obligation can be liquidated
99    /// (borrow-factor-adjusted debt exceeds the unhealthy borrow value).
100    pub fn is_liquidatable(&self) -> bool {
101        let debt = self.borrow_factor_adjusted_debt_value();
102        let unhealthy = self.unhealthy_borrow_value();
103        debt > unhealthy && unhealthy > 0
104    }
105
106    /// Returns `true` if the obligation has no borrow capacity left
107    /// (borrow-factor-adjusted debt >= allowed borrow value).
108    pub fn is_borrowing_disabled(&self) -> bool {
109        self.borrow_factor_adjusted_debt_value() >= self.allowed_borrow_value()
110    }
111}
112
113impl ObligationCollateral {
114    /// Returns `true` if this deposit slot is active (non-default reserve).
115    pub fn is_active(&self) -> bool {
116        self.deposit_reserve != Pubkey::default()
117    }
118
119    /// Market value as a raw u128 scaled fraction.
120    pub fn market_value(&self) -> u128 {
121        u128::from(self.market_value_sf)
122    }
123}
124
125impl ObligationLiquidity {
126    /// Returns `true` if this borrow slot is active (non-default reserve).
127    pub fn is_active(&self) -> bool {
128        self.borrow_reserve != Pubkey::default()
129    }
130
131    /// Borrowed amount as a raw u128 scaled fraction.
132    pub fn borrowed_amount(&self) -> u128 {
133        u128::from(self.borrowed_amount_sf)
134    }
135
136    /// Market value as a raw u128 scaled fraction.
137    pub fn market_value(&self) -> u128 {
138        u128::from(self.market_value_sf)
139    }
140
141    /// Borrow-factor-adjusted market value as a raw u128 scaled fraction.
142    pub fn borrow_factor_adjusted_market_value(&self) -> u128 {
143        u128::from(self.borrow_factor_adjusted_market_value_sf)
144    }
145}
146
147// ---------------------------------------------------------------------------
148// Nested types
149// ---------------------------------------------------------------------------
150
151/// A single collateral deposit within an obligation.
152#[derive(Debug, Clone, Copy, Pod, Zeroable)]
153#[repr(C)]
154pub struct ObligationCollateral {
155    pub deposit_reserve: Pubkey,
156    pub deposited_amount: u64,
157    pub market_value_sf: PodU128,
158    pub borrowed_amount_against_this_collateral_in_elevation_group: u64,
159    pub padding: [u64; 9],
160}
161
162/// A single borrow position within an obligation.
163#[derive(Debug, Clone, Copy, Pod, Zeroable)]
164#[repr(C)]
165pub struct ObligationLiquidity {
166    pub borrow_reserve: Pubkey,
167    pub cumulative_borrow_rate_bsf: BigFractionBytes,
168    pub last_borrowed_at_timestamp: u64,
169    pub borrowed_amount_sf: PodU128,
170    pub market_value_sf: PodU128,
171    pub borrow_factor_adjusted_market_value_sf: PodU128,
172    pub borrowed_amount_outside_elevation_groups: u64,
173    pub fixed_term_borrow_rollover_config: FixedTermBorrowRolloverConfig,
174    pub borrowed_amount_at_expiration: u64,
175    pub padding2: [u64; 4],
176}
177
178/// Fixed-term borrow rollover configuration on an obligation liquidity position.
179#[derive(Debug, Clone, Copy, Zeroable, Pod)]
180#[repr(C)]
181pub struct FixedTermBorrowRolloverConfig {
182    pub auto_rollover_enabled: u8,
183    pub open_term_allowed: u8,
184    pub migration_to_fixed_enabled: u8,
185    pub alignment_padding: [u8; 1],
186    pub max_borrow_rate_bps: u32,
187    pub min_debt_term_seconds: u64,
188}
189
190/// Owner-defined, permissionlessly-executed repay order.
191#[derive(Debug, Clone, Copy, Pod, Zeroable)]
192#[repr(C)]
193pub struct ObligationOrder {
194    pub condition_threshold_sf: PodU128,
195    pub opportunity_parameter_sf: PodU128,
196    pub min_execution_bonus_bps: u16,
197    pub max_execution_bonus_bps: u16,
198    pub condition_type: u8,
199    pub opportunity_type: u8,
200    pub padding1: [u8; 10],
201    pub padding2: [PodU128; 5],
202}
203
204/// Owner-defined, permissionlessly-executed borrow order.
205#[derive(Debug, Clone, Copy, Zeroable, Pod)]
206#[repr(C)]
207pub struct BorrowOrder {
208    pub debt_liquidity_mint: Pubkey,
209    pub remaining_debt_amount: u64,
210    pub filled_debt_destination: Pubkey,
211    pub min_debt_term_seconds: u64,
212    pub fillable_until_timestamp: u64,
213    pub placed_at_timestamp: u64,
214    pub last_updated_at_timestamp: u64,
215    pub requested_debt_amount: u64,
216    pub max_borrow_rate_bps: u32,
217    pub active: u8,
218    pub enable_auto_rollover_on_filled_borrows: u8,
219    pub padding1: [u8; 2],
220    pub end_padding: [u64; 5],
221}