Skip to main content

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