Skip to content

Commit

Permalink
Permissionless Bad Debt handling (#207)
Browse files Browse the repository at this point in the history
* feat: add permissionless bad debt flag

* fix: changed account names

* fix: fuzz + tests

---------

Co-authored-by: Jakob <[email protected]>
  • Loading branch information
jkbpvsc and jkbpvsc authored May 8, 2024
1 parent 5828906 commit ce714ea
Show file tree
Hide file tree
Showing 13 changed files with 286 additions and 28 deletions.
7 changes: 7 additions & 0 deletions clients/rust/marginfi-cli/src/entrypoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,11 @@ pub enum BankCommand {
usd_init_limit: Option<u64>,
#[clap(long, help = "Oracle max age in seconds, 0 to use default value (60s)")]
oracle_max_age: Option<u16>,
#[clap(
long,
help = "Permissionless bad debt settlement, if true the group admin is not required to settle bad debt"
)]
permissionless_bad_debt_settlement: Option<bool>,
},
#[cfg(feature = "dev")]
InspectPriceOracle {
Expand Down Expand Up @@ -614,6 +619,7 @@ fn bank(subcmd: BankCommand, global_options: &GlobalOptions) -> Result<()> {
oracle_key,
usd_init_limit,
oracle_max_age,
permissionless_bad_debt_settlement,
} => {
let bank = config
.mfi_program
Expand Down Expand Up @@ -661,6 +667,7 @@ fn bank(subcmd: BankCommand, global_options: &GlobalOptions) -> Result<()> {
risk_tier: risk_tier.map(|x| x.into()),
total_asset_value_init_limit: usd_init_limit,
oracle_max_age,
permissionless_bad_debt_settlement,
},
)
}
Expand Down
6 changes: 3 additions & 3 deletions clients/rust/marginfi-cli/src/processor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ Last Update: {:?}h ago ({})
bank.config.oracle_setup,
bank.config.oracle_keys,
bank.config.get_oracle_max_age(),
bank.emissions_flags,
bank.flags,
I80F48::from(bank.emissions_rate),
bank.emissions_mint,
I80F48::from(bank.emissions_remaining),
Expand Down Expand Up @@ -736,7 +736,7 @@ fn handle_bankruptcy_for_an_account(
program_id: config.program_id,
accounts: marginfi::accounts::LendingPoolHandleBankruptcy {
marginfi_group: profile.marginfi_group.unwrap(),
admin: config.authority(),
signer: config.authority(),
bank: bank_pk,
marginfi_account: marginfi_account_pk,
liquidity_vault: find_bank_vault_pda(
Expand Down Expand Up @@ -881,7 +881,7 @@ fn make_bankruptcy_ix(
program_id: config.program_id,
accounts: marginfi::accounts::LendingPoolHandleBankruptcy {
marginfi_group: profile.marginfi_group.unwrap(),
admin: config.fee_payer.pubkey(),
signer: config.fee_payer.pubkey(),
bank: bank_pk,
marginfi_account: marginfi_account_pk,
liquidity_vault: find_bank_vault_pda(
Expand Down
2 changes: 1 addition & 1 deletion programs/marginfi/fuzz/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,7 @@ impl<'bump> MarginfiFuzzContext<'bump> {
&marginfi::ID,
&mut marginfi::instructions::LendingPoolHandleBankruptcy {
marginfi_group: AccountLoader::try_from(&self.marginfi_group.clone())?,
admin: Signer::try_from(&self.owner)?,
signer: Signer::try_from(&self.owner)?,
bank: AccountLoader::try_from(&bank.bank.clone())?,
marginfi_account: AccountLoader::try_from(
&marginfi_account.margin_account.clone(),
Expand Down
4 changes: 4 additions & 0 deletions programs/marginfi/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ pub const ZERO_AMOUNT_THRESHOLD: I80F48 = I80F48!(0.0001);

pub const EMISSIONS_FLAG_BORROW_ACTIVE: u64 = 1 << 0;
pub const EMISSIONS_FLAG_LENDING_ACTIVE: u64 = 1 << 1;
pub const PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG: u64 = 1 << 2;

pub(crate) const EMISSION_FLAGS: u64 = EMISSIONS_FLAG_BORROW_ACTIVE | EMISSIONS_FLAG_LENDING_ACTIVE;
pub(crate) const GROUP_FLAGS: u64 = PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG;

/// Cutoff timestamp for balance last_update used in accounting collected emissions.
/// Any balance updates before this timestamp are ignored, and current_timestamp is used instead.
Expand Down
2 changes: 2 additions & 0 deletions programs/marginfi/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ pub enum MarginfiError {
IllegalBalanceState,
#[msg("Illegal account authority transfer")] // 6044
IllegalAccountAuthorityTransfer,
#[msg("Unauthorized")] // 6045
Unauthorized,
}

impl From<MarginfiError> for ProgramError {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ pub fn lending_pool_setup_emissions(
);

bank.emissions_mint = ctx.accounts.emissions_mint.key();
bank.emissions_flags = emissions_flags;

bank.override_emissions_flag(emissions_flags);

bank.emissions_rate = emissions_rate;
bank.emissions_remaining = I80F48::from_num(total_emissions).into();

Expand Down Expand Up @@ -155,7 +157,7 @@ pub fn lending_pool_update_emissions_parameters(

if let Some(flags) = emissions_flags {
msg!("Updating emissions flags to {:#010b}", flags);
bank.emissions_flags = flags;
bank.flags = flags;
}

if let Some(rate) = emissions_rate {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::constants::ZERO_AMOUNT_THRESHOLD;
use crate::constants::{PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG, ZERO_AMOUNT_THRESHOLD};
use crate::events::{AccountEventHeader, LendingPoolBankHandleBankruptcyEvent};
use crate::state::marginfi_account::DISABLED_FLAG;
use crate::{
Expand Down Expand Up @@ -29,8 +29,19 @@ pub fn lending_pool_handle_bankruptcy(ctx: Context<LendingPoolHandleBankruptcy>)
insurance_vault,
token_program,
bank: bank_loader,
marginfi_group: marginfi_group_loader,
..
} = ctx.accounts;
let bank = bank_loader.load()?;

if !bank.get_flag(PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG) {
check!(
ctx.accounts.signer.key() == marginfi_group_loader.load()?.admin,
MarginfiError::Unauthorized
);
}

drop(bank);

let mut marginfi_account = marginfi_account_loader.load_mut()?;

Expand Down Expand Up @@ -107,7 +118,7 @@ pub fn lending_pool_handle_bankruptcy(ctx: Context<LendingPoolHandleBankruptcy>)

emit!(LendingPoolBankHandleBankruptcyEvent {
header: AccountEventHeader {
signer: Some(ctx.accounts.admin.key()),
signer: Some(ctx.accounts.signer.key()),
marginfi_account: marginfi_account_loader.key(),
marginfi_account_authority: marginfi_account.authority,
marginfi_group: marginfi_account.group,
Expand All @@ -126,8 +137,8 @@ pub fn lending_pool_handle_bankruptcy(ctx: Context<LendingPoolHandleBankruptcy>)
pub struct LendingPoolHandleBankruptcy<'info> {
pub marginfi_group: AccountLoader<'info, MarginfiGroup>,

#[account(address = marginfi_group.load()?.admin)]
pub admin: Signer<'info>,
// #[account(address = marginfi_group.load()?.admin)]
pub signer: Signer<'info>,

#[account(
mut,
Expand Down
4 changes: 2 additions & 2 deletions programs/marginfi/src/state/marginfi_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1150,8 +1150,8 @@ impl<'a> BankAccountWrapper<'a> {
pub fn claim_emissions(&mut self, current_timestamp: u64) -> MarginfiResult {
if let Some(balance_amount) = match (
self.balance.get_side(),
self.bank.get_emissions_flag(EMISSIONS_FLAG_LENDING_ACTIVE),
self.bank.get_emissions_flag(EMISSIONS_FLAG_BORROW_ACTIVE),
self.bank.get_flag(EMISSIONS_FLAG_LENDING_ACTIVE),
self.bank.get_flag(EMISSIONS_FLAG_BORROW_ACTIVE),
) {
(Some(BalanceSide::Assets), true, _) => Some(
self.bank
Expand Down
47 changes: 39 additions & 8 deletions programs/marginfi/src/state/marginfi_group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ use crate::events::{GroupEventHeader, LendingPoolBankAccrueInterestEvent};
use crate::{
assert_struct_align, assert_struct_size, check,
constants::{
FEE_VAULT_AUTHORITY_SEED, FEE_VAULT_SEED, INSURANCE_VAULT_AUTHORITY_SEED,
INSURANCE_VAULT_SEED, LIQUIDITY_VAULT_AUTHORITY_SEED, LIQUIDITY_VAULT_SEED,
MAX_ORACLE_KEYS, MAX_PRICE_AGE_SEC, PYTH_ID, SECONDS_PER_YEAR,
EMISSION_FLAGS, FEE_VAULT_AUTHORITY_SEED, FEE_VAULT_SEED, GROUP_FLAGS,
INSURANCE_VAULT_AUTHORITY_SEED, INSURANCE_VAULT_SEED, LIQUIDITY_VAULT_AUTHORITY_SEED,
LIQUIDITY_VAULT_SEED, MAX_ORACLE_KEYS, MAX_PRICE_AGE_SEC,
PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG, PYTH_ID, SECONDS_PER_YEAR,
TOTAL_ASSET_VALUE_INIT_LIMIT_INACTIVE,
},
debug, math_error,
Expand Down Expand Up @@ -316,12 +317,13 @@ pub struct Bank {

pub config: BankConfig,

/// Emissions Config Flags
/// Bank Config Flags
///
/// - EMISSIONS_FLAG_BORROW_ACTIVE: 1
/// - EMISSIONS_FLAG_LENDING_ACTIVE: 2
/// - PERMISSIONLESS_BAD_DEBT_SETTLEMENT: 4
///
pub emissions_flags: u64,
pub flags: u64,
/// Emissions APR.
/// Number of emitted tokens (emissions_mint) per 1e(bank.mint_decimal) tokens (bank mint) (native amount) per 1 YEAR.
pub emissions_rate: u64,
Expand Down Expand Up @@ -371,7 +373,7 @@ impl Bank {
total_asset_shares: I80F48::ZERO.into(),
last_update: current_timestamp,
config,
emissions_flags: 0,
flags: 0,
emissions_rate: 0,
emissions_remaining: I80F48::ZERO.into(),
emissions_mint: Pubkey::default(),
Expand Down Expand Up @@ -541,6 +543,10 @@ impl Bank {

set_if_some!(self.config.oracle_max_age, config.oracle_max_age);

if let Some(flag) = config.permissionless_bad_debt_settlement {
self.update_flag(flag, PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG);
}

self.config.validate()?;

Ok(())
Expand Down Expand Up @@ -717,8 +723,31 @@ impl Bank {
}
}

pub fn get_emissions_flag(&self, flag: u64) -> bool {
(self.emissions_flags & flag) == flag
pub fn get_flag(&self, flag: u64) -> bool {
(self.flags & flag) == flag
}

pub(crate) fn override_emissions_flag(&mut self, flag: u64) {
assert!(Self::verify_emissions_flags(flag));
self.flags = flag;
}

pub(crate) fn update_flag(&mut self, value: bool, flag: u64) {
assert!(Self::verify_group_flags(flag));

if value {
self.flags |= flag;
} else {
self.flags &= !flag;
}
}

const fn verify_emissions_flags(flags: u64) -> bool {
flags & EMISSION_FLAGS == flags
}

const fn verify_group_flags(flags: u64) -> bool {
flags & GROUP_FLAGS == flags
}
}

Expand Down Expand Up @@ -1151,6 +1180,8 @@ pub struct BankConfigOpt {
pub total_asset_value_init_limit: Option<u64>,

pub oracle_max_age: Option<u16>,

pub permissionless_bad_debt_settlement: Option<bool>,
}

#[cfg_attr(
Expand Down
Loading

0 comments on commit ce714ea

Please sign in to comment.