Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat #266: Delegate admin to raise deposit caps and increase emissions #269

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions programs/marginfi/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,22 +50,24 @@ pub enum MarginfiError {
OperationWithdrawOnly,
#[msg("Operation is borrow-only")] // 6023
OperationBorrowOnly,
#[msg("Operation is repay-only")] // 6024
#[msg("Invalid delegate operation")] // 6024
InvalidDelegateOperation,
#[msg("Operation is repay-only")] // 6025
OperationRepayOnly,
#[msg("No asset found")] // 6025
#[msg("No asset found")] // 6026
NoAssetFound,
#[msg("No liability found")] // 6026
#[msg("No liability found")] // 6027
NoLiabilityFound,
#[msg("Invalid oracle setup")] // 6027
#[msg("Invalid oracle setup")] // 6028
InvalidOracleSetup,
#[msg("Invalid bank utilization ratio")] // 6028
#[msg("Invalid bank utilization ratio")] // 6029
IllegalUtilizationRatio,
#[msg("Bank borrow cap exceeded")] // 6029
#[msg("Bank borrow cap exceeded")] // 6030
BankLiabilityCapacityExceeded,
#[msg("Invalid Price")] // 6030
#[msg("Invalid Price")] // 6031
InvalidPrice,
#[msg("Account can have only one liability when account is under isolated risk")] // 6031
IsolatedAccountIllegalState, // 6032
#[msg("Account can have only one liability when account is under isolated risk")] // 6032
IsolatedAccountIllegalState,
#[msg("Emissions already setup")]
EmissionsAlreadySetup,
#[msg("Oracle is not set")] // 6033
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use crate::{
state::marginfi_group::{Bank, MarginfiGroup},
MarginfiResult,
};
use crate::{check, MarginfiError};
use anchor_lang::prelude::*;

pub fn lending_pool_configure_bank_delegate(
ctx: Context<LendingPoolConfigureBankDelegate>,
delegate_admin: Pubkey,
) -> MarginfiResult {
let mut bank = ctx.accounts.bank.load_mut()?;
bank.delegate_admin = delegate_admin;
Ok(())
}

#[derive(Accounts)]
pub struct LendingPoolConfigureBankDelegate<'info> {
pub marginfi_group: AccountLoader<'info, MarginfiGroup>,

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

#[account(
mut,
constraint = bank.load()?.group == marginfi_group.key(),
)]
pub bank: AccountLoader<'info, Bank>,
}

pub fn lending_pool_increase_deposit_limit(
ctx: Context<LendingPoolDelegateOperation>,
new_deposit_limit: u64,
) -> MarginfiResult {
let mut bank = ctx.accounts.bank.load_mut()?;

// Only allow increasing the limit
check!(new_deposit_limit <= bank.config.deposit_limit, MarginfiError::InvalidDelegateOperation);

bank.config.deposit_limit = new_deposit_limit;
Ok(())
}

pub fn lending_pool_increase_emissions_rate(
ctx: Context<LendingPoolDelegateOperation>,
new_emissions_rate: u64,
) -> MarginfiResult {
let mut bank = ctx.accounts.bank.load_mut()?;

// Only allow increasing the rate
check!(new_emissions_rate <= bank.emissions_rate, MarginfiError::InvalidDelegateOperation);

bank.emissions_rate = new_emissions_rate;
Ok(())
}

#[derive(Accounts)]
pub struct LendingPoolDelegateOperation<'info> {
pub marginfi_group: AccountLoader<'info, MarginfiGroup>,

#[account(
constraint = bank.load()?.delegate_admin == delegate_admin.key(),
)]
pub delegate_admin: Signer<'info>,

#[account(
mut,
constraint = bank.load()?.group == marginfi_group.key(),
)]
pub bank: AccountLoader<'info, Bank>,
}
2 changes: 2 additions & 0 deletions programs/marginfi/src/instructions/marginfi_group/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod collect_bank_fees;
mod config_group_fee;
mod configure;
mod configure_bank;
mod configure_bank_delegate;
mod edit_global_fee;
mod edit_stake_settings;
mod handle_bankruptcy;
Expand All @@ -23,6 +24,7 @@ pub use collect_bank_fees::*;
pub use config_group_fee::*;
pub use configure::*;
pub use configure_bank::*;
pub use configure_bank_delegate::*;
pub use edit_global_fee::*;
pub use edit_stake_settings::*;
pub use handle_bankruptcy::*;
Expand Down
22 changes: 22 additions & 0 deletions programs/marginfi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,27 @@ pub mod marginfi {
marginfi_group::lending_pool_configure_bank(ctx, bank_config_opt)
}

pub fn lending_pool_configure_bank_delegate(
ctx: Context<LendingPoolConfigureBankDelegate>,
delegate_admin: Pubkey,
) -> MarginfiResult {
marginfi_group::lending_pool_configure_bank_delegate(ctx, delegate_admin)
}

pub fn lending_pool_increase_deposit_limit(
ctx: Context<LendingPoolDelegateOperation>,
new_deposit_limit: u64,
) -> MarginfiResult {
marginfi_group::lending_pool_increase_deposit_limit(ctx, new_deposit_limit)
}

pub fn lending_pool_increase_emissions_rate(
ctx: Context<LendingPoolDelegateOperation>,
new_emissions_rate: u64,
) -> MarginfiResult {
marginfi_group::lending_pool_increase_emissions_rate(ctx, new_emissions_rate)
}

pub fn lending_pool_setup_emissions(
ctx: Context<LendingPoolSetupEmissions>,
flags: u64,
Expand Down Expand Up @@ -293,6 +314,7 @@ pub mod marginfi {
pub fn propagate_staked_settings(ctx: Context<PropagateStakedSettings>) -> MarginfiResult {
marginfi_group::propagate_staked_settings(ctx)
}

}

#[cfg(not(feature = "no-entrypoint"))]
Expand Down
4 changes: 3 additions & 1 deletion programs/marginfi/src/state/marginfi_group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ pub struct GroupBankConfig {
pub program_fees: bool,
}

assert_struct_size!(Bank, 1856);
assert_struct_size!(Bank, 1888);
assert_struct_align!(Bank, 8);
#[account(zero_copy(unsafe))]
#[repr(C)]
Expand All @@ -445,6 +445,7 @@ pub struct Bank {
pub mint_decimals: u8,

pub group: Pubkey,
pub delegate_admin: Pubkey, // Added delegate admin key

// Note: The padding is here, not after mint_decimals. Pubkey has alignment 1, so those 32
// bytes can cross the alignment 8 threshold, but WrappedI80F48 has alignment 8 and cannot
Expand Down Expand Up @@ -524,6 +525,7 @@ impl Bank {
mint,
mint_decimals,
group: marginfi_group_pk,
delegate_admin: Pubkey::default(),
asset_share_value: I80F48::ONE.into(),
liability_share_value: I80F48::ONE.into(),
liquidity_vault,
Expand Down
2 changes: 1 addition & 1 deletion programs/marginfi/src/state/price.rs
Original file line number Diff line number Diff line change
Expand Up @@ -976,7 +976,7 @@ impl From<Ref<'_, PullFeedAccountData>> for LitePullFeedAccountData {
/// A slimmed down version of the AggregatorAccountData struct copied from the switchboard-v2/src/aggregator.rs
#[cfg_attr(feature = "client", derive(Clone, Debug))]
struct LiteAggregatorAccountData {
/// Use sliding windoe or round based resolution
/// Use sliding window or round based resolution
/// NOTE: This changes result propagation in latest_round_result
pub resolution_mode: AggregatorResolutionMode,
/// Latest confirmed update request result that has been accepted as valid.
Expand Down
127 changes: 127 additions & 0 deletions programs/marginfi/tests/user_actions/delegate_admin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use fixtures::{
assert_custom_error,
test::{BankMint, TestFixture, TestSettings},
};
use marginfi::errors::MarginfiError;
use pretty_assertions::assert_eq;
use solana_program_test::tokio;
use solana_sdk::{signature::Keypair, signer::Signer};

#[tokio::test]
async fn bank_delegate_admin_success() -> anyhow::Result<()> {
// -------------------------------------------------------------------------
// Setup
// -------------------------------------------------------------------------

let mut test_settings = TestSettings::all_banks_payer_not_admin();
if let Some(group_config) = &mut test_settings.group_config {
group_config.admin = Some(solana_sdk::pubkey::Pubkey::default());
}
let mut test_f = TestFixture::new(Some(test_settings)).await;
let delegate = Keypair::new();
let usdc_bank_f = test_f.get_bank(&BankMint::Usdc);

// -------------------------------------------------------------------------
// Test: Set delegate admin
// -------------------------------------------------------------------------

usdc_bank_f.try_configure_delegate(delegate.pubkey()).await?;
let bank = usdc_bank_f.load().await;
assert_eq!(bank.delegate_admin, delegate.pubkey());

// -------------------------------------------------------------------------
// Test: Increase deposit limit
// -------------------------------------------------------------------------

let initial_limit = bank.config.deposit_limit;
let new_limit = initial_limit + 1000;
usdc_bank_f.try_increase_deposit_limit(&delegate, new_limit).await?;

let bank = usdc_bank_f.load().await;
assert_eq!(bank.config.deposit_limit, new_limit);

// -------------------------------------------------------------------------
// Test: Increase emissions rate
// -------------------------------------------------------------------------

let initial_rate = bank.emissions_rate;
let new_rate = initial_rate + 100;
usdc_bank_f.try_increase_emissions_rate(&delegate, new_rate).await?;

let bank = usdc_bank_f.load().await;
assert_eq!(bank.emissions_rate, new_rate);

Ok(())
}

#[tokio::test]
async fn bank_delegate_admin_failure_decrease_limit() -> anyhow::Result<()> {
// -------------------------------------------------------------------------
// Setup
// -------------------------------------------------------------------------

let mut test_settings = TestSettings::all_banks_payer_not_admin();
if let Some(group_config) = &mut test_settings.group_config {
group_config.admin = Some(solana_sdk::pubkey::Pubkey::default());
}
let mut test_f = TestFixture::new(Some(test_settings)).await;
let delegate = Keypair::new();
let usdc_bank_f = test_f.get_bank(&BankMint::Usdc);

// Set up delegate
usdc_bank_f.try_configure_delegate(delegate.pubkey()).await?;
let bank = usdc_bank_f.load().await;
let initial_limit = bank.config.deposit_limit;

// -------------------------------------------------------------------------
// Test: Try to decrease deposit limit (should fail)
// -------------------------------------------------------------------------

let lower_limit = initial_limit - 1000;
let res = usdc_bank_f.try_increase_deposit_limit(&delegate, lower_limit).await;

assert!(res.is_err());
assert_custom_error!(res.unwrap_err(), MarginfiError::InvalidDelegateOperation);

Ok(())
}

#[tokio::test]
async fn bank_delegate_admin_failure_wrong_signer() -> anyhow::Result<()> {
// -------------------------------------------------------------------------
// Setup
// -------------------------------------------------------------------------

let mut test_settings = TestSettings::all_banks_payer_not_admin();
if let Some(group_config) = &mut test_settings.group_config {
group_config.admin = Some(solana_sdk::pubkey::Pubkey::default());
}
let mut test_f = TestFixture::new(Some(test_settings)).await;
let delegate = Keypair::new();
let wrong_signer = Keypair::new();
let usdc_bank_f = test_f.get_bank(&BankMint::Usdc);

// Set up delegate
usdc_bank_f.try_configure_delegate(delegate.pubkey()).await?;
let bank = usdc_bank_f.load().await;

// -------------------------------------------------------------------------
// Test: Try operations with wrong signer
// -------------------------------------------------------------------------

// Try to increase deposit limit
let new_limit = bank.config.deposit_limit + 1000;
let res = usdc_bank_f.try_increase_deposit_limit(&wrong_signer, new_limit).await;

assert!(res.is_err());
assert_custom_error!(res.unwrap_err(), MarginfiError::InvalidDelegateOperation);

// Try to increase emissions rate
let new_rate = bank.emissions_rate + 100;
let res = usdc_bank_f.try_increase_emissions_rate(&wrong_signer, new_rate).await;

assert!(res.is_err());
assert_custom_error!(res.unwrap_err(), MarginfiError::InvalidDelegateOperation);

Ok(())
}
1 change: 1 addition & 0 deletions programs/marginfi/tests/user_actions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod flash_loan;
mod liquidate;
mod repay;
mod withdraw;
mod delegate_admin;

use anchor_lang::prelude::Clock;
use fixed::types::I80F48;
Expand Down
Loading