diff --git a/programs/marginfi/src/instructions/cdp.rs b/programs/marginfi/src/instructions/cdp.rs index 5db75f2f..b4b53a1d 100644 --- a/programs/marginfi/src/instructions/cdp.rs +++ b/programs/marginfi/src/instructions/cdp.rs @@ -1,7 +1,7 @@ use std::cell::RefMut; use anchor_lang::prelude::*; -use anchor_spl::token::{Mint, Token, Transfer}; +use anchor_spl::token::{Mint, MintTo, Token, Transfer}; use fixed::types::I80F48; use crate::{ @@ -9,7 +9,7 @@ use crate::{ math_error, prelude::MarginfiResult, state::{ - cdp::{Cdp, CdpBank, CdpCollateralBank, CdpCollateralBankStatus}, + cdp::{Cdp, CdpBank, CdpCollateralBank, CdpCollateralBankStatus, CdpRiskEngine}, marginfi_group::{Bank, MarginfiGroup}, }, }; @@ -133,7 +133,7 @@ pub fn cdp_deposit(ctx: Context, amount: u64) -> MarginfiResult { let deposit_shares = bank.get_asset_shares(I80F48::from_num(amount))?; - bank.change_asset_shares(deposit_shares)?; + bank.change_asset_shares(deposit_shares, false)?; cdp.change_collateral_shares(deposit_shares)?; bank.deposit_spl_transfer( @@ -165,6 +165,8 @@ pub struct CdpDeposit<'info> { pub fn cdp_mint(ctx: Context, amount: u64) -> MarginfiResult { let mut cdp_bank = ctx.accounts.cdp_bank.load_mut()?; let mut cdp = ctx.accounts.cdp.load_mut()?; + let mut cdp_collateral_bank = ctx.accounts.cdp.load_mut()?; + let mut lending_bank = ctx.accounts.bank.load_mut()?; let liab_shares = cdp_bank .get_liability_shares(I80F48::from_num(amount)) @@ -173,6 +175,33 @@ pub fn cdp_mint(ctx: Context, amount: u64) -> MarginfiResult { cdp_bank.update_liability_share_amount(liab_shares)?; cdp.change_liability_shares(liab_shares)?; + CdpRiskEngine::new( + &cdp, + &cdp_bank, + &cdp_collateral_bank, + &lending_bank, + ctx.remaining_accounts, + Clock::get()?.unix_timestamp, + )? + .check_init_health(); + + anchor_spl::token::mint_to( + CpiContext::new_with_signer( + ctx.accounts.token_program.to_account_info(), + MintTo { + mint: ctx.accounts.cdp_mint.to_account_info(), + to: ctx.accounts.cdp_authority_ta.to_account_info(), + authority: ctx.accounts.cdp_mint_authority.to_account_info(), + }, + &[&[ + CDP_MINT_AUTH_SEED.as_bytes(), + ctx.accounts.cdp_mint.key().as_ref(), + &[cdp_bank.mint_authority_bump], + ]], + ), + amount, + )?; + Ok(()) } @@ -190,3 +219,5 @@ pub struct CdpMint<'info> { pub cdp_authority_ta: AccountInfo<'info>, pub token_program: Program<'info, Token>, } + +pub fn cdp_liquidate() diff --git a/programs/marginfi/src/state/cdp.rs b/programs/marginfi/src/state/cdp.rs index 725622d4..d4a0857a 100644 --- a/programs/marginfi/src/state/cdp.rs +++ b/programs/marginfi/src/state/cdp.rs @@ -1,9 +1,16 @@ use anchor_lang::prelude::*; -use fixed::types::I80F48; +use bytemuck::Zeroable; +use fixed::types::{I80F48, I89F39}; -use crate::{math_error, prelude::MarginfiResult}; +use crate::{ + check, constants::MAX_PRICE_AGE_SEC, math_error, prelude::MarginfiResult, + state::price::PriceAdapter, +}; -use super::marginfi_group::WrappedI80F48; +use super::{ + marginfi_group::{Bank, WrappedI80F48}, + price::{OraclePriceFeedAdapter, OraclePriceType, PriceBias}, +}; // CDP Actions // ========== @@ -80,6 +87,9 @@ pub enum CdpCollateralBankStatus { ReduceOnly, } +unsafe impl Pod for CdpCollateralBank {} +unsafe impl Zeroable for CdpCollateralBank {} + /// Supported collateral for a bank #[account(zero_copy)] #[repr(C)] @@ -132,3 +142,65 @@ impl Cdp { Ok(()) } } + +pub struct CdpRiskEngine<'a> { + deposit: &'a Cdp, + bank: &'a CdpBank, + collateral_bank: &'a CdpCollateralBank, + lending_bank: &'a Bank, + oracle_adapter: OraclePriceFeedAdapter, +} + +impl<'a> CdpRiskEngine<'a> { + pub fn new( + deposit: &'a Cdp, + bank: &'a CdpBank, + collateral_bank: &'a CdpCollateralBank, + lending_bank: &'a Bank, + oracle_ais: &[AccountInfo], + current_timestamp: i64, + ) -> Result { + let oracle_adapter = OraclePriceFeedAdapter::try_from_bank_config( + &lending_bank.config, + oracle_ais, + current_timestamp, + MAX_PRICE_AGE_SEC, + )?; + + Ok(Self { + deposit, + bank, + collateral_bank, + lending_bank, + oracle_adapter, + }) + } + + pub fn check_init_health(&self) -> Result<()> { + let liab_shares = I80F48::from(self.deposit.liability_shares); + let liab_value = self + .bank + .get_liability_amount(liab_shares) + .ok_or_else(math_error!())?; + + let collateral_shares = I80F48::from(self.deposit.collateral_shares); + let collateral_price = self + .oracle_adapter + .get_price_of_type(OraclePriceType::TimeWeighted, Some(PriceBias::Low))?; + + let collateral_init_weight: I80F48 = self.lending_bank.config.asset_weight_init.into(); + + let weighted_collateral_value = collateral_shares + .checked_mul(collateral_price) + .ok_or_else(math_error!())? + .checked_mul(collateral_init_weight) + .ok_or_else(math_error!())?; + + check!( + weighted_collateral_value >= liab_value, + crate::prelude::MarginfiError::BadAccountHealth + ); + + Ok(()) + } +}