Skip to main content

port_variable_rate_lending/state/
reserve.rs

1use std::{
2    convert::{TryFrom, TryInto},
3    u64,
4};
5
6use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs};
7use solana_program::{
8    clock::Slot,
9    entrypoint::ProgramResult,
10    msg,
11    program_error::ProgramError,
12    program_pack::{IsInitialized, Pack, Sealed},
13    pubkey::{Pubkey, PUBKEY_BYTES},
14};
15use solana_program::program_option::COption;
16
17use crate::{
18    error::LendingError,
19    math::{Decimal, Rate, TryAdd, TryDiv, TryMul, TrySub},
20};
21
22use super::*;
23
24/// Percentage of an obligation that can be repaid during each liquidation call
25pub const LIQUIDATION_CLOSE_FACTOR: u8 = 50;
26
27/// Obligation borrow amount that is small enough to close out
28pub const LIQUIDATION_CLOSE_AMOUNT: u64 = 2;
29
30/// Lending market reserve state
31#[derive(Clone, Debug, Default, PartialEq)]
32pub struct Reserve {
33    /// Version of the struct
34    pub version: u8,
35    /// Last slot when supply and rates updated
36    pub last_update: LastUpdate,
37    /// Lending market address
38    pub lending_market: Pubkey,
39    /// Reserve liquidity
40    pub liquidity: ReserveLiquidity,
41    /// Reserve collateral
42    pub collateral: ReserveCollateral,
43    /// Reserve configuration values
44    pub config: ReserveConfig,
45}
46
47impl Reserve {
48    /// Calculate the current borrow rate
49    pub fn current_borrow_rate(&self) -> Result<Rate, ProgramError> {
50        let utilization_rate = self.liquidity.utilization_rate()?;
51        let optimal_utilization_rate = Rate::from_percent(self.config.optimal_utilization_rate);
52        let low_utilization = utilization_rate < optimal_utilization_rate;
53        if low_utilization || self.config.optimal_utilization_rate == 100 {
54            let normalized_rate = utilization_rate.try_div(optimal_utilization_rate)?;
55            let min_rate = Rate::from_percent(self.config.min_borrow_rate);
56            let rate_range = Rate::from_percent(
57                self.config
58                    .optimal_borrow_rate
59                    .checked_sub(self.config.min_borrow_rate)
60                    .ok_or(LendingError::MathOverflow)?,
61            );
62
63            Ok(normalized_rate.try_mul(rate_range)?.try_add(min_rate)?)
64        } else {
65            let normalized_rate = utilization_rate
66                .try_sub(optimal_utilization_rate)?
67                .try_div(Rate::from_percent(
68                    100u8
69                        .checked_sub(self.config.optimal_utilization_rate)
70                        .ok_or(LendingError::MathOverflow)?,
71                ))?;
72            let min_rate = Rate::from_percent(self.config.optimal_borrow_rate);
73            let rate_range = Rate::from_percent(
74                self.config
75                    .max_borrow_rate
76                    .checked_sub(self.config.optimal_borrow_rate)
77                    .ok_or(LendingError::MathOverflow)?,
78            );
79
80            Ok(normalized_rate.try_mul(rate_range)?.try_add(min_rate)?)
81        }
82    }
83
84    /// Collateral exchange rate
85    pub fn collateral_exchange_rate(&self) -> Result<CollateralExchangeRate, ProgramError> {
86        let total_liquidity = self.liquidity.total_supply()?;
87        self.collateral.exchange_rate(total_liquidity)
88    }
89
90    /// Update borrow rate and accrue interest
91    pub fn accrue_interest(&mut self, current_slot: Slot) -> ProgramResult {
92        let slots_elapsed = self.last_update.slots_elapsed(current_slot)?;
93        if slots_elapsed > 0 {
94            let current_borrow_rate = self.current_borrow_rate()?;
95            self.liquidity
96                .compound_interest(current_borrow_rate, slots_elapsed)?;
97        }
98        Ok(())
99    }
100}
101
102/// Initialize a reserve
103pub struct InitReserveParams {
104    /// Last slot when supply and rates updated
105    pub current_slot: Slot,
106    /// Lending market address
107    pub lending_market: Pubkey,
108    /// Reserve liquidity
109    pub liquidity: ReserveLiquidity,
110    /// Reserve collateral
111    pub collateral: ReserveCollateral,
112    /// Reserve configuration values
113    pub config: ReserveConfig,
114}
115
116/// Calculate borrow result
117#[derive(Debug)]
118pub struct CalculateBorrowResult {
119    /// Total amount of borrow including fees
120    pub borrow_amount: Decimal,
121    /// Borrow amount portion of total amount
122    pub receive_amount: u64,
123    /// Loan origination fee
124    pub borrow_fee: u64,
125    /// Host fee portion of origination fee
126    pub host_fee: u64,
127}
128
129/// Calculate repay result
130#[derive(Debug)]
131pub struct CalculateRepayResult {
132    /// Amount of liquidity that is settled from the obligation.
133    pub settle_amount: Decimal,
134    /// Amount that will be repaid as u64
135    pub repay_amount: u64,
136}
137
138/// Calculate liquidation result
139#[derive(Debug)]
140pub struct CalculateLiquidationResult {
141    /// Amount of liquidity that is settled from the obligation. It includes
142    /// the amount of loan that was defaulted if collateral is depleted.
143    pub settle_amount: Decimal,
144    /// Amount that will be repaid as u64
145    pub repay_amount: u64,
146    /// Amount of collateral to withdraw in exchange for repay amount
147    pub withdraw_amount: u64,
148}
149
150/// Reserve liquidity
151#[derive(Clone, Debug, Default, PartialEq)]
152pub struct ReserveLiquidity {
153    /// Reserve liquidity mint address
154    pub mint_pubkey: Pubkey,
155    /// Reserve liquidity mint decimals
156    pub mint_decimals: u8,
157    /// Reserve liquidity supply address
158    pub supply_pubkey: Pubkey,
159    /// Reserve liquidity fee receiver address
160    pub fee_receiver: Pubkey,
161    /// Reserve liquidity oracle account
162    pub oracle_pubkey: COption<Pubkey>,
163    /// Reserve liquidity available
164    pub available_amount: u64,
165    /// Reserve liquidity borrowed
166    pub borrowed_amount_wads: Decimal,
167    /// Reserve liquidity cumulative borrow rate
168    pub cumulative_borrow_rate_wads: Decimal,
169    /// Reserve liquidity market price in quote currency
170    pub market_price: Decimal,
171}
172
173impl ReserveLiquidity {
174    /// Calculate the total reserve supply including active loans
175    pub fn total_supply(&self) -> Result<Decimal, ProgramError> {
176        Decimal::from(self.available_amount).try_add(self.borrowed_amount_wads)
177    }
178
179    /// Calculate the liquidity utilization rate of the reserve
180    pub fn utilization_rate(&self) -> Result<Rate, ProgramError> {
181        let total_supply = self.total_supply()?;
182        if total_supply == Decimal::zero() {
183            return Ok(Rate::zero());
184        }
185        self.borrowed_amount_wads.try_div(total_supply)?.try_into()
186    }
187
188    /// Compound current borrow rate over elapsed slots
189    fn compound_interest(
190        &mut self,
191        current_borrow_rate: Rate,
192        slots_elapsed: u64,
193    ) -> ProgramResult {
194        let slot_interest_rate = current_borrow_rate.try_div(SLOTS_PER_YEAR)?;
195        let compounded_interest_rate = Rate::one()
196            .try_add(slot_interest_rate)?
197            .try_pow(slots_elapsed)?;
198        self.cumulative_borrow_rate_wads = self
199            .cumulative_borrow_rate_wads
200            .try_mul(compounded_interest_rate)?;
201        self.borrowed_amount_wads = self
202            .borrowed_amount_wads
203            .try_mul(compounded_interest_rate)?;
204        Ok(())
205    }
206}
207
208/// Create a new reserve liquidity
209pub struct NewReserveLiquidityParams {
210    /// Reserve liquidity mint address
211    pub mint_pubkey: Pubkey,
212    /// Reserve liquidity mint decimals
213    pub mint_decimals: u8,
214    /// Reserve liquidity supply address
215    pub supply_pubkey: Pubkey,
216    /// Reserve liquidity fee receiver address
217    pub fee_receiver: Pubkey,
218    /// Reserve liquidity oracle account
219    pub oracle_pubkey: COption<Pubkey>,
220    /// Reserve liquidity market price in quote currency
221    pub market_price: Decimal,
222}
223
224/// Reserve collateral
225#[derive(Clone, Debug, Default, PartialEq)]
226pub struct ReserveCollateral {
227    /// Reserve collateral mint address
228    pub mint_pubkey: Pubkey,
229    /// Reserve collateral mint supply, used for exchange rate
230    pub mint_total_supply: u64,
231    /// Reserve collateral supply address
232    pub supply_pubkey: Pubkey,
233}
234
235impl ReserveCollateral {
236    /// Return the current collateral exchange rate.
237    fn exchange_rate(
238        &self,
239        total_liquidity: Decimal,
240    ) -> Result<CollateralExchangeRate, ProgramError> {
241        let rate = if self.mint_total_supply == 0 || total_liquidity == Decimal::zero() {
242            Rate::from_scaled_val(INITIAL_COLLATERAL_RATE)
243        } else {
244            let mint_total_supply = Decimal::from(self.mint_total_supply);
245            Rate::try_from(mint_total_supply.try_div(total_liquidity)?)?
246        };
247
248        Ok(CollateralExchangeRate(rate))
249    }
250}
251
252/// Create a new reserve collateral
253pub struct NewReserveCollateralParams {
254    /// Reserve collateral mint address
255    pub mint_pubkey: Pubkey,
256    /// Reserve collateral supply address
257    pub supply_pubkey: Pubkey,
258}
259
260/// Collateral exchange rate
261#[derive(Clone, Copy, Debug)]
262pub struct CollateralExchangeRate(Rate);
263
264impl CollateralExchangeRate {
265    /// Convert reserve collateral to liquidity
266    pub fn collateral_to_liquidity(&self, collateral_amount: u64) -> Result<u64, ProgramError> {
267        self.decimal_collateral_to_liquidity(collateral_amount.into())?
268            .try_floor_u64()
269    }
270
271    /// Convert reserve collateral to liquidity
272    pub fn decimal_collateral_to_liquidity(
273        &self,
274        collateral_amount: Decimal,
275    ) -> Result<Decimal, ProgramError> {
276        collateral_amount.try_div(self.0)
277    }
278
279    /// Convert reserve liquidity to collateral
280    pub fn liquidity_to_collateral(&self, liquidity_amount: u64) -> Result<u64, ProgramError> {
281        self.decimal_liquidity_to_collateral(liquidity_amount.into())?
282            .try_floor_u64()
283    }
284
285    /// Convert reserve liquidity to collateral
286    pub fn decimal_liquidity_to_collateral(
287        &self,
288        liquidity_amount: Decimal,
289    ) -> Result<Decimal, ProgramError> {
290        liquidity_amount.try_mul(self.0)
291    }
292}
293
294impl From<CollateralExchangeRate> for Rate {
295    fn from(exchange_rate: CollateralExchangeRate) -> Self {
296        exchange_rate.0
297    }
298}
299
300/// Reserve configuration values
301#[derive(Clone, Copy, Debug, Default, PartialEq)]
302pub struct ReserveConfig {
303    /// Optimal utilization rate, as a percentage
304    pub optimal_utilization_rate: u8,
305    /// Target ratio of the value of borrows to deposits, as a percentage
306    /// 0 if use as collateral is disabled
307    pub loan_to_value_ratio: u8,
308    /// Bonus a liquidator gets when repaying part of an unhealthy obligation, as a percentage
309    pub liquidation_bonus: u8,
310    /// Loan to value ratio at which an obligation can be liquidated, as a percentage
311    pub liquidation_threshold: u8,
312    /// Min borrow APY
313    pub min_borrow_rate: u8,
314    /// Optimal (utilization) borrow APY
315    pub optimal_borrow_rate: u8,
316    /// Max borrow APY
317    pub max_borrow_rate: u8,
318    /// Program owner fees assessed, separate from gains due to interest accrual
319    pub fees: ReserveFees,
320    /// corresponded staking pool pubkey of deposit
321    pub deposit_staking_pool: COption<Pubkey>,
322}
323
324/// Additional fee information on a reserve
325///
326/// These exist separately from interest accrual fees, and are specifically for the program owner
327/// and frontend host. The fees are paid out as a percentage of liquidity token amounts during
328/// repayments and liquidations.
329#[derive(Clone, Copy, Debug, Default, PartialEq)]
330pub struct ReserveFees {
331    /// Fee assessed on `BorrowObligationLiquidity`, expressed as a Wad.
332    /// Must be between 0 and 10^18, such that 10^18 = 1.  A few examples for
333    /// clarity:
334    /// 1% = 10_000_000_000_000_000
335    /// 0.01% (1 basis point) = 100_000_000_000_000
336    /// 0.00001% (Aave borrow fee) = 100_000_000_000
337    pub borrow_fee_wad: u64,
338    /// Fee for flash loan, expressed as a Wad.
339    pub flash_loan_fee_wad: u64,
340    /// Amount of fee going to host account, if provided in liquidate and repay
341    pub host_fee_percentage: u8,
342}
343
344impl ReserveFees {
345    /// Calculate the owner and host fees on borrow
346    pub fn calculate_borrow_fees(
347        &self,
348        borrow_amount: Decimal,
349        fee_calculation: FeeCalculation,
350    ) -> Result<(u64, u64), ProgramError> {
351        self.calculate_fees(borrow_amount, self.borrow_fee_wad, fee_calculation)
352    }
353
354    /// Calculate the owner and host fees on flash loan
355    pub fn calculate_flash_loan_fees(
356        &self,
357        flash_loan_amount: Decimal,
358    ) -> Result<(u64, u64), ProgramError> {
359        self.calculate_fees(
360            flash_loan_amount,
361            self.flash_loan_fee_wad,
362            FeeCalculation::Exclusive,
363        )
364    }
365
366    fn calculate_fees(
367        &self,
368        amount: Decimal,
369        fee_wad: u64,
370        fee_calculation: FeeCalculation,
371    ) -> Result<(u64, u64), ProgramError> {
372        let borrow_fee_rate = Rate::from_scaled_val(fee_wad);
373        let host_fee_rate = Rate::from_percent(self.host_fee_percentage);
374        if borrow_fee_rate > Rate::zero() && amount > Decimal::zero() {
375            let need_to_assess_host_fee = host_fee_rate > Rate::zero();
376            let minimum_fee = if need_to_assess_host_fee {
377                2u64 // 1 token to owner, 1 to host
378            } else {
379                1u64 // 1 token to owner, nothing else
380            };
381
382            let borrow_fee_amount = match fee_calculation {
383                // Calculate fee to be added to borrow: fee = amount * rate
384                FeeCalculation::Exclusive => amount.try_mul(borrow_fee_rate)?,
385                // Calculate fee to be subtracted from borrow: fee = amount * (rate / (rate + 1))
386                FeeCalculation::Inclusive => {
387                    let borrow_fee_rate =
388                        borrow_fee_rate.try_div(borrow_fee_rate.try_add(Rate::one())?)?;
389                    amount.try_mul(borrow_fee_rate)?
390                }
391            };
392
393            let borrow_fee_decimal = borrow_fee_amount.max(minimum_fee.into());
394            if borrow_fee_decimal >= amount {
395                msg!("Borrow amount is too small to receive liquidity after fees");
396                return Err(LendingError::BorrowTooSmall.into());
397            }
398
399            let borrow_fee = borrow_fee_decimal.try_round_u64()?;
400            let host_fee = if need_to_assess_host_fee {
401                borrow_fee_decimal
402                    .try_mul(host_fee_rate)?
403                    .try_round_u64()?
404                    .max(1u64)
405            } else {
406                0
407            };
408
409            Ok((borrow_fee, host_fee))
410        } else {
411            Ok((0, 0))
412        }
413    }
414}
415
416/// Calculate fees exlusive or inclusive of an amount
417pub enum FeeCalculation {
418    /// Fee added to amount: fee = rate * amount
419    Exclusive,
420    /// Fee included in amount: fee = (rate / (1 + rate)) * amount
421    Inclusive,
422}
423
424impl Sealed for Reserve {}
425impl IsInitialized for Reserve {
426    fn is_initialized(&self) -> bool {
427        self.version != UNINITIALIZED_VERSION
428    }
429}
430
431const RESERVE_LEN: usize = 575; // 1 + 8 + 1 + 32 + 32 + 1 + 32 + 32 + (4 + 32) + 8 + 16 + 16 + 16 + 32 + 8 + 32 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 8 + 8 + 1 + 33 + 33 + 182
432impl Pack for Reserve {
433    const LEN: usize = RESERVE_LEN;
434
435    // @TODO: break this up by reserve / liquidity / collateral / config https://git.io/JOCca
436    fn pack_into_slice(&self, output: &mut [u8]) {
437        let output = array_mut_ref![output, 0, RESERVE_LEN];
438        #[allow(clippy::ptr_offset_with_cast)]
439        let (
440            version,
441            last_update_slot,
442            last_update_stale,
443            lending_market,
444            liquidity_mint_pubkey,
445            liquidity_mint_decimals,
446            liquidity_supply_pubkey,
447            liquidity_fee_receiver,
448            liquidity_oracle_pubkey,
449            liquidity_available_amount,
450            liquidity_borrowed_amount_wads,
451            liquidity_cumulative_borrow_rate_wads,
452            liquidity_market_price,
453            collateral_mint_pubkey,
454            collateral_mint_total_supply,
455            collateral_supply_pubkey,
456            config_optimal_utilization_rate,
457            config_loan_to_value_ratio,
458            config_liquidation_bonus,
459            config_liquidation_threshold,
460            config_min_borrow_rate,
461            config_optimal_borrow_rate,
462            config_max_borrow_rate,
463            config_fees_borrow_fee_wad,
464            config_fees_flash_loan_fee_wad,
465            config_fees_host_fee_percentage,
466            config_deposit_staking_pool,
467            _padding,
468        ) = mut_array_refs![
469            output,
470            1,
471            8,
472            1,
473            PUBKEY_BYTES,
474            PUBKEY_BYTES,
475            1,
476            PUBKEY_BYTES,
477            PUBKEY_BYTES,
478            4 + PUBKEY_BYTES,
479            8,
480            16,
481            16,
482            16,
483            PUBKEY_BYTES,
484            8,
485            PUBKEY_BYTES,
486            1,
487            1,
488            1,
489            1,
490            1,
491            1,
492            1,
493            8,
494            8,
495            1,
496            33,
497            215
498        ];
499
500        // reserve
501        *version = self.version.to_le_bytes();
502        *last_update_slot = self.last_update.slot.to_le_bytes();
503        pack_bool(self.last_update.stale, last_update_stale);
504        lending_market.copy_from_slice(self.lending_market.as_ref());
505
506        // liquidity
507        liquidity_mint_pubkey.copy_from_slice(self.liquidity.mint_pubkey.as_ref());
508        *liquidity_mint_decimals = self.liquidity.mint_decimals.to_le_bytes();
509        liquidity_supply_pubkey.copy_from_slice(self.liquidity.supply_pubkey.as_ref());
510        liquidity_fee_receiver.copy_from_slice(self.liquidity.fee_receiver.as_ref());
511        pack_coption_key(&self.liquidity.oracle_pubkey, liquidity_oracle_pubkey);
512        *liquidity_available_amount = self.liquidity.available_amount.to_le_bytes();
513        pack_decimal(
514            self.liquidity.borrowed_amount_wads,
515            liquidity_borrowed_amount_wads,
516        );
517        pack_decimal(
518            self.liquidity.cumulative_borrow_rate_wads,
519            liquidity_cumulative_borrow_rate_wads,
520        );
521        pack_decimal(self.liquidity.market_price, liquidity_market_price);
522
523        // collateral
524        collateral_mint_pubkey.copy_from_slice(self.collateral.mint_pubkey.as_ref());
525        *collateral_mint_total_supply = self.collateral.mint_total_supply.to_le_bytes();
526        collateral_supply_pubkey.copy_from_slice(self.collateral.supply_pubkey.as_ref());
527
528        // config
529        *config_optimal_utilization_rate = self.config.optimal_utilization_rate.to_le_bytes();
530        *config_loan_to_value_ratio = self.config.loan_to_value_ratio.to_le_bytes();
531        *config_liquidation_bonus = self.config.liquidation_bonus.to_le_bytes();
532        *config_liquidation_threshold = self.config.liquidation_threshold.to_le_bytes();
533        *config_min_borrow_rate = self.config.min_borrow_rate.to_le_bytes();
534        *config_optimal_borrow_rate = self.config.optimal_borrow_rate.to_le_bytes();
535        *config_max_borrow_rate = self.config.max_borrow_rate.to_le_bytes();
536        *config_fees_borrow_fee_wad = self.config.fees.borrow_fee_wad.to_le_bytes();
537        *config_fees_flash_loan_fee_wad = self.config.fees.flash_loan_fee_wad.to_le_bytes();
538        *config_fees_host_fee_percentage = self.config.fees.host_fee_percentage.to_le_bytes();
539        pack_coption_key_compact(
540            &self.config.deposit_staking_pool,
541            config_deposit_staking_pool,
542        );
543    }
544
545    /// Unpacks a byte buffer into a [ReserveInfo](struct.ReserveInfo.html).
546    fn unpack_from_slice(input: &[u8]) -> Result<Self, ProgramError> {
547        let input = array_ref![input, 0, RESERVE_LEN];
548        #[allow(clippy::ptr_offset_with_cast)]
549        let (
550            version,
551            last_update_slot,
552            last_update_stale,
553            lending_market,
554            liquidity_mint_pubkey,
555            liquidity_mint_decimals,
556            liquidity_supply_pubkey,
557            liquidity_fee_receiver,
558            liquidity_oracle_pubkey,
559            liquidity_available_amount,
560            liquidity_borrowed_amount_wads,
561            liquidity_cumulative_borrow_rate_wads,
562            liquidity_market_price,
563            collateral_mint_pubkey,
564            collateral_mint_total_supply,
565            collateral_supply_pubkey,
566            config_optimal_utilization_rate,
567            config_loan_to_value_ratio,
568            config_liquidation_bonus,
569            config_liquidation_threshold,
570            config_min_borrow_rate,
571            config_optimal_borrow_rate,
572            config_max_borrow_rate,
573            config_fees_borrow_fee_wad,
574            config_fees_flash_loan_fee_wad,
575            config_fees_host_fee_percentage,
576            config_deposit_staking_pool,
577            _padding,
578        ) = array_refs![
579            input,
580            1,
581            8,
582            1,
583            PUBKEY_BYTES,
584            PUBKEY_BYTES,
585            1,
586            PUBKEY_BYTES,
587            PUBKEY_BYTES,
588            4 + PUBKEY_BYTES,
589            8,
590            16,
591            16,
592            16,
593            PUBKEY_BYTES,
594            8,
595            PUBKEY_BYTES,
596            1,
597            1,
598            1,
599            1,
600            1,
601            1,
602            1,
603            8,
604            8,
605            1,
606            33,
607            215
608        ];
609
610        let version = u8::from_le_bytes(*version);
611        if version > PROGRAM_VERSION {
612            msg!("Reserve version does not match lending program version");
613            return Err(ProgramError::InvalidAccountData);
614        }
615
616        Ok(Self {
617            version,
618            last_update: LastUpdate {
619                slot: u64::from_le_bytes(*last_update_slot),
620                stale: unpack_bool(last_update_stale)?,
621            },
622            lending_market: Pubkey::new_from_array(*lending_market),
623            liquidity: ReserveLiquidity {
624                mint_pubkey: Pubkey::new_from_array(*liquidity_mint_pubkey),
625                mint_decimals: u8::from_le_bytes(*liquidity_mint_decimals),
626                supply_pubkey: Pubkey::new_from_array(*liquidity_supply_pubkey),
627                fee_receiver: Pubkey::new_from_array(*liquidity_fee_receiver),
628                oracle_pubkey: unpack_coption_key(liquidity_oracle_pubkey)?,
629                available_amount: u64::from_le_bytes(*liquidity_available_amount),
630                borrowed_amount_wads: unpack_decimal(liquidity_borrowed_amount_wads),
631                cumulative_borrow_rate_wads: unpack_decimal(liquidity_cumulative_borrow_rate_wads),
632                market_price: unpack_decimal(liquidity_market_price),
633            },
634            collateral: ReserveCollateral {
635                mint_pubkey: Pubkey::new_from_array(*collateral_mint_pubkey),
636                mint_total_supply: u64::from_le_bytes(*collateral_mint_total_supply),
637                supply_pubkey: Pubkey::new_from_array(*collateral_supply_pubkey),
638            },
639            config: ReserveConfig {
640                optimal_utilization_rate: u8::from_le_bytes(*config_optimal_utilization_rate),
641                loan_to_value_ratio: u8::from_le_bytes(*config_loan_to_value_ratio),
642                liquidation_bonus: u8::from_le_bytes(*config_liquidation_bonus),
643                liquidation_threshold: u8::from_le_bytes(*config_liquidation_threshold),
644                min_borrow_rate: u8::from_le_bytes(*config_min_borrow_rate),
645                optimal_borrow_rate: u8::from_le_bytes(*config_optimal_borrow_rate),
646                max_borrow_rate: u8::from_le_bytes(*config_max_borrow_rate),
647                fees: ReserveFees {
648                    borrow_fee_wad: u64::from_le_bytes(*config_fees_borrow_fee_wad),
649                    flash_loan_fee_wad: u64::from_le_bytes(*config_fees_flash_loan_fee_wad),
650                    host_fee_percentage: u8::from_le_bytes(*config_fees_host_fee_percentage),
651                },
652                deposit_staking_pool: unpack_coption_key_compact(config_deposit_staking_pool)?,
653            },
654        })
655    }
656}