Skip to content

Commit

Permalink
feat: cdp instructions
Browse files Browse the repository at this point in the history
  • Loading branch information
jkbpvsc committed Oct 27, 2023
1 parent 71d857e commit ca83929
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 14 deletions.
2 changes: 2 additions & 0 deletions programs/marginfi/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ pub const FEE_VAULT_SEED: &str = "fee_vault";
pub const EMISSIONS_AUTH_SEED: &str = "emissions_auth_seed";
pub const EMISSIONS_TOKEN_ACCOUNT_SEED: &str = "emissions_token_account_seed";

pub const CDP_MINT_AUTH_SEED: &str = "cdp_auth_seed";

cfg_if::cfg_if! {
if #[cfg(feature = "devnet")] {
pub const PYTH_ID: Pubkey = pubkey!("gSbePebfvPy7tRqimPoVecS2UsBvYv46ynrzWocc92s");
Expand Down
162 changes: 162 additions & 0 deletions programs/marginfi/src/instructions/cdp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use std::cell::RefMut;

use anchor_lang::prelude::*;
use anchor_spl::token::{Mint, Token, Transfer};
use fixed::types::I80F48;

use crate::{
constants::{CDP_MINT_AUTH_SEED, LIQUIDITY_VAULT_SEED},
prelude::MarginfiResult,
state::{
cdp::{Cdp, CdpBank, CdpCollateralBank, CdpCollateralBankStatus},
marginfi_group::{Bank, MarginfiGroup},
},
};

pub fn create_cdp_bank(ctx: Context<CreateCdpBank>) -> MarginfiResult {
assert_eq!(ctx.accounts.mint.supply, 0, "Mint has existing supply");

let mut cdp_bank = ctx.accounts.cdp_bank.load_init()?;

*cdp_bank = CdpBank {
group: ctx.accounts.group.key(),
mint: ctx.accounts.mint.key(),
mint_authority: ctx.accounts.mint_authority.key(),
mint_authority_bump: *ctx.bumps.get("mint_authority").unwrap(),
total_liability_shares: I80F48::ZERO.into(),
liability_share_value: I80F48::ZERO.into(),
liability_interest_rate: I80F48::ZERO.into(),
liability_limit: 0,
};

Ok(())
}

/// TODO: Make `cdp_bank` unique per (group, mint) -- this should already be the case becasue `cdp_bank` owns the `mint_authority`,
/// but we should make it explicit
/// TODO: What to do with freeze authority, should be probably controlled by the CDP admin.
#[derive(Accounts)]
pub struct CreateCdpBank<'info> {
pub group: AccountLoader<'info, MarginfiGroup>,
#[account(mut, address = group.load()?.admin)]
pub admin: Signer<'info>,
#[account(mint::authority = mint_authority)]
pub mint: Box<Account<'info, Mint>>,
#[account(
seeds = [
CDP_MINT_AUTH_SEED.as_bytes(),
mint.key().as_ref()
],
bump
)]
pub mint_authority: AccountInfo<'info>,
#[account(
init,
space = 8 + std::mem::size_of::<CdpBank>(),
payer = admin,
)]
pub cdp_bank: AccountLoader<'info, CdpBank>,

pub rent: Sysvar<'info, Rent>,
pub system_program: Program<'info, System>,
}

pub fn create_cdp_collateral_bank(ctx: Context<CreateCdpCollateralBank>) -> MarginfiResult {
let mut cdp_collateral_bank = ctx.accounts.cdp_collateral_bank.load_init()?;

*cdp_collateral_bank = CdpCollateralBank {
lending_bank: ctx.accounts.bank.key(),
cdp_bank: ctx.accounts.cdp_bank.key(),
status: CdpCollateralBankStatus::Disabled,
};

Ok(())
}

/// TODO: Make `cdp_collateral_bank` unique per (bank, cpd_bank)
#[derive(Accounts)]
pub struct CreateCdpCollateralBank<'info> {
pub group: AccountLoader<'info, MarginfiGroup>,
#[account(mut, address = group.load()?.admin)]
pub admin: Signer<'info>,
#[account(constraint = bank.load()?.group == group.key())]
pub bank: AccountLoader<'info, Bank>,
#[account(constraint = cdp_bank.load()?.group == group.key())]
pub cdp_bank: AccountLoader<'info, CdpBank>,
#[account(
init,
space = 8 + std::mem::size_of::<CdpCollateralBank>(),
payer = admin,
)]
pub cdp_collateral_bank: AccountLoader<'info, CdpCollateralBank>,
pub rent: Sysvar<'info, Rent>,
pub system_program: Program<'info, System>,
}

pub fn create_cdp(ctx: Context<CreateCdp>) -> MarginfiResult {
let cdp = ctx.accounts.cdp.load_init()?;

*cdp = Cdp {
authority: ctx.accounts.authority.key(),
cdp_collateral_bank: ctx.accounts.cdp_collateral_bank.key(),
collateral_shares: I80F48::ZERO.into(),
liability_shares: I80F48::ZERO.into(),
flags: 0,
};

Ok(())
}

#[derive(Accounts)]
pub struct CreateCdp<'info> {
#[account(mut)]
pub fee_payer: Signer<'info>,
pub authority: AccountInfo<'info>,
pub cdp_collateral_bank: AccountLoader<'info, CdpCollateralBank>,
#[account(
init,
payer = fee_payer,
space = 8 + std::mem::size_of::<Cdp>(),
)]
pub cdp: AccountLoader<'info, Cdp>,
pub system_program: Program<'info, System>,
}

pub fn cdp_deposit(ctx: Context<CdpDeposit>, amount: u64) -> MarginfiResult {
let mut bank: RefMut<Bank> = ctx.accounts.bank.load_mut()?;

bank.accrue_interest(Clock::get()?.unix_timestamp)?;

let mut cdp: RefMut<Cdp> = ctx.accounts.cdp.load_mut()?;

let deposit_shares = bank.get_asset_shares(I80F48::from_num(amount))?;

bank.change_asset_shares(deposit_shares);
cdp.change_collateral_shares(deposit_shares);

bank.deposit_spl_transfer(
amount,
Transfer {
from: ctx.accounts.cdp_authority_ta,
to: ctx.accounts.bank_vault,
authority: ctx.accounts.cdp_authority.to_account_info(),
},
ctx.accounts.token_program.to_account_info(),
);

Ok(())
}

#[derive(Accounts)]
pub struct CdpDeposit<'info> {
pub group: AccountLoader<'info, MarginfiGroup>,
pub cdp_bank: AccountLoader<'info, CdpBank>,
pub cdp_collateral_bank: AccountLoader<'info, CdpCollateralBank>,
pub bank: AccountLoader<'info, Bank>,
pub bank_vault: AccountInfo<'info>,
pub cdp: AccountLoader<'info, Cdp>,
pub cdp_authority: Signer<'info>,
pub cdp_authority_ta: AccountInfo<'info>,
pub token_program: Program<'info, Token>,
}
2 changes: 2 additions & 0 deletions programs/marginfi/src/instructions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
pub mod cdp;
pub mod marginfi_account;
pub mod marginfi_group;

pub use cdp::*;
pub use marginfi_account::*;
pub use marginfi_group::*;
58 changes: 48 additions & 10 deletions programs/marginfi/src/state/cdp.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use anchor_lang::prelude::*;
use fixed::types::I80F48;

use crate::{math_error, prelude::MarginfiResult};

use super::marginfi_group::WrappedI80F48;

Expand Down Expand Up @@ -26,14 +29,16 @@ use super::marginfi_group::WrappedI80F48;
// Discount difference is split between the liquidator and the bank insurance fund.

/// Multi collateral CDP bank
#[zero_copy]
#[account(zero_copy)]
#[repr(C)]
#[derive(AnchorDeserialize, AnchorSerialize)]
struct CdpBank {
pub struct CdpBank {
pub group: Pubkey,
pub mint_authority: Pubkey,
pub mint: Pubkey,

pub mint_authority: Pubkey,
pub mint_authority_bump: u8,

pub total_liability_shares: WrappedI80F48,
/// Accrues interest
pub liability_share_value: WrappedI80F48,
Expand All @@ -45,32 +50,65 @@ struct CdpBank {
pub liability_limit: u64,
}

#[repr(u8)]
#[derive(Copy, Clone, Debug, AnchorDeserialize, AnchorSerialize)]
pub enum CdpCollateralBankStatus {
Disabled,
Enabled,
/// Desposits and minting are disabled
/// Repayments, withdrawing, and liquidations are enabled
ReduceOnly,
}

/// Supported collateral for a bank
#[zero_copy]
#[account(zero_copy)]
#[repr(C)]
#[derive(AnchorDeserialize, AnchorSerialize)]
struct CdpCollateral {
pub struct CdpCollateralBank {
pub lending_bank: Pubkey,
pub cdp_bank: Pubkey,
/// - 0: Disabled
/// - 1: Enabled
/// - 2: ReduceOnly
pub status: u8,
pub status: CdpCollateralBankStatus,
// Optional parameters for additional margin requirement configurations
// pub asset_weight_init_haircut: WrappedI80F48,
// pub asset_weight_maint_haircut: WrappedI80F48,
}

/// Cdp account
#[zero_copy]
#[account(zero_copy)]
#[repr(C)]
#[derive(AnchorDeserialize, AnchorSerialize)]
struct Cdp {
pub group: Pubkey,
pub struct Cdp {
pub authority: Pubkey,
pub cdp_collateral_config: Pubkey,
pub cdp_collateral_bank: Pubkey,

pub collateral_shares: WrappedI80F48,
pub liability_shares: WrappedI80F48,
pub flags: u64,
}

impl Cdp {
pub fn change_collateral_shares(&mut self, shares: I80F48) -> MarginfiResult {
let current_coll_shares = I80F48::from(self.collateral_shares);

self.collateral_shares = current_coll_shares
.checked_add(shares)
.ok_or_else(math_error!())?
.into();

Ok(())
}

pub fn change_liability_shares(&mut self, shares: I80F48) -> MarginfiResult {
let current_liab_shares = I80F48::from(self.liability_shares);

self.liability_shares = current_liab_shares
.checked_add(shares)
.ok_or_else(math_error!())?
.into();

Ok(())
}
}
8 changes: 4 additions & 4 deletions programs/marginfi/src/state/marginfi_group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,14 +337,14 @@ impl Bank {
.ok_or_else(math_error!())?)
}

pub fn get_liability_shares(&self, value: I80F48) -> MarginfiResult<I80F48> {
Ok(value
pub fn get_liability_shares(&self, amount: I80F48) -> MarginfiResult<I80F48> {
Ok(amount
.checked_div(self.liability_share_value.into())
.ok_or_else(math_error!())?)
}

pub fn get_asset_shares(&self, value: I80F48) -> MarginfiResult<I80F48> {
Ok(value
pub fn get_asset_shares(&self, amount: I80F48) -> MarginfiResult<I80F48> {
Ok(amount
.checked_div(self.asset_share_value.into())
.ok_or_else(math_error!())?)
}
Expand Down

0 comments on commit ca83929

Please sign in to comment.