Skip to content

Commit

Permalink
Freeze bank settings (#261)
Browse files Browse the repository at this point in the history
* Fix a bug where config doesn't update the protocol origination fee

* Enable freezing of bank settings
  • Loading branch information
jgur-psyops authored Dec 6, 2024
1 parent 063a5de commit 244a3b3
Show file tree
Hide file tree
Showing 17 changed files with 798 additions and 79 deletions.
5 changes: 4 additions & 1 deletion .github/actions/setup-common/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ runs:
components: rustfmt, clippy
default: true

- run: (cargo install cargo-nextest || true)
- run: cargo install cargo-nextest --locked
shell: bash

- run: cargo nextest --version
shell: bash
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 @@ -304,6 +304,11 @@ pub enum BankCommand {
help = "Permissionless bad debt settlement, if true the group admin is not required to settle bad debt"
)]
permissionless_bad_debt_settlement: Option<bool>,
#[clap(
long,
help = "If enabled, will prevent this Update ix from ever running against after this invokation"
)]
freeze_settings: Option<bool>,
},
InspectPriceOracle {
bank_pk: Pubkey,
Expand Down Expand Up @@ -740,6 +745,7 @@ fn bank(subcmd: BankCommand, global_options: &GlobalOptions) -> Result<()> {
usd_init_limit,
oracle_max_age,
permissionless_bad_debt_settlement,
freeze_settings,
} => {
let bank = config
.mfi_program
Expand Down Expand Up @@ -789,6 +795,7 @@ fn bank(subcmd: BankCommand, global_options: &GlobalOptions) -> Result<()> {
total_asset_value_init_limit: usd_init_limit,
oracle_max_age,
permissionless_bad_debt_settlement,
freeze_settings,
},
)
}
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"@coral-xyz/spl-token": "^0.30.1",
"@solana/spl-token": "^0.4.8",
"@solana/web3.js": "^1.95.2",
"@mrgnlabs/mrgn-common": "^1.7.0",
"@mrgnlabs/marginfi-client-v2": "^3.1.0",
"@mrgnlabs/mrgn-common": "^1.8.0",
"@mrgnlabs/marginfi-client-v2": "^4.0.0",
"mocha": "^10.2.0",
"ts-mocha": "^10.0.0",
"bignumber.js": "^9.1.2"
Expand Down
3 changes: 2 additions & 1 deletion programs/marginfi/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,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 const FREEZE_SETTINGS: u64 = 1 << 3;

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;
pub(crate) const GROUP_FLAGS: u64 = PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG | FREEZE_SETTINGS;

/// 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 @@ -98,6 +98,8 @@ pub enum MarginfiError {
T22MintRequired,
#[msg("Invalid ATA for global fee account")] // 6048
InvalidFeeAta,
#[msg("Bank settings are frozen and cannot be updated")] // 6049
BankSettingsFrozen,
}

impl From<MarginfiError> for ProgramError {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::constants::{EMISSIONS_AUTH_SEED, EMISSIONS_TOKEN_ACCOUNT_SEED};
use crate::constants::{EMISSIONS_AUTH_SEED, EMISSIONS_TOKEN_ACCOUNT_SEED, FREEZE_SETTINGS};
use crate::events::{GroupEventHeader, LendingPoolBankConfigureEvent};
use crate::prelude::MarginfiError;
use crate::{check, math_error, utils};
Expand All @@ -17,6 +17,11 @@ pub fn lending_pool_configure_bank(
) -> MarginfiResult {
let mut bank = ctx.accounts.bank.load_mut()?;

check!(
!bank.get_flag(FREEZE_SETTINGS),
MarginfiError::BankSettingsFrozen
);

bank.configure(&bank_config)?;

if bank_config.oracle.is_some() {
Expand Down
15 changes: 14 additions & 1 deletion programs/marginfi/src/state/marginfi_group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use super::{
marginfi_account::{BalanceSide, RequirementType},
price::{OraclePriceFeedAdapter, OracleSetup},
};
use crate::borsh::{BorshDeserialize, BorshSerialize};
#[cfg(not(feature = "client"))]
use crate::events::{GroupEventHeader, LendingPoolBankAccrueInterestEvent};
use crate::{
Expand All @@ -20,6 +19,10 @@ use crate::{
state::marginfi_account::calc_value,
MarginfiResult,
};
use crate::{
borsh::{BorshDeserialize, BorshSerialize},
constants::FREEZE_SETTINGS,
};
use anchor_lang::prelude::borsh;
use anchor_lang::prelude::*;
use anchor_spl::token_interface::*;
Expand Down Expand Up @@ -269,6 +272,10 @@ impl InterestRateConfig {
ir_config.protocol_fixed_fee_apr
);
set_if_some!(self.protocol_ir_fee, ir_config.protocol_ir_fee);
set_if_some!(
self.protocol_origination_fee,
ir_config.protocol_origination_fee
);
}
}

Expand Down Expand Up @@ -706,6 +713,10 @@ impl Bank {
self.update_flag(flag, PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG);
}

if let Some(flag) = config.freeze_settings {
self.update_flag(flag, FREEZE_SETTINGS);
}

self.config.validate()?;

Ok(())
Expand Down Expand Up @@ -1494,6 +1505,8 @@ pub struct BankConfigOpt {
pub oracle_max_age: Option<u16>,

pub permissionless_bad_debt_settlement: Option<bool>,

pub freeze_settings: Option<bool>,
}

#[cfg_attr(
Expand Down
13 changes: 12 additions & 1 deletion programs/marginfi/tests/admin_actions/setup_bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use fixed::types::I80F48;
use fixed_macro::types::I80F48;
use fixtures::{assert_custom_error, prelude::*};
use marginfi::{
constants::{INIT_BANK_ORIGINATION_FEE_DEFAULT, PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG},
constants::{
FREEZE_SETTINGS, INIT_BANK_ORIGINATION_FEE_DEFAULT, PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG,
},
prelude::MarginfiError,
state::marginfi_group::{Bank, BankConfig, BankConfigOpt, BankVaultType},
};
Expand Down Expand Up @@ -344,6 +346,7 @@ async fn configure_bank_success(bank_mint: BankMint) -> anyhow::Result<()> {
total_asset_value_init_limit,
oracle_max_age,
permissionless_bad_debt_settlement,
freeze_settings,
} = &config_bank_opt;
// Compare bank field to opt field if Some, otherwise compare to old bank field
macro_rules! check_bank_field {
Expand Down Expand Up @@ -374,6 +377,7 @@ async fn configure_bank_success(bank_mint: BankMint) -> anyhow::Result<()> {
check_bank_field!(interest_rate_config, insurance_ir_fee);
check_bank_field!(interest_rate_config, protocol_fixed_fee_apr);
check_bank_field!(interest_rate_config, protocol_ir_fee);
check_bank_field!(interest_rate_config, protocol_origination_fee);

check_bank_field!(asset_weight_init);
check_bank_field!(asset_weight_maint);
Expand All @@ -395,6 +399,13 @@ async fn configure_bank_success(bank_mint: BankMint) -> anyhow::Result<()> {
.unwrap_or( bank.get_flag(PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG) == old_bank.get_flag(PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG))
);

assert!(freeze_settings
// If Some(...) check flag set properly
.map(|set| set == bank.get_flag(FREEZE_SETTINGS))
// If None check flag is unchanged
.unwrap_or( bank.get_flag(FREEZE_SETTINGS) == old_bank.get_flag(FREEZE_SETTINGS))
);

assert_eq!(
bank.config.oracle_keys,
// If Some(...) check keys set properly
Expand Down
2 changes: 1 addition & 1 deletion tests/01_initGroup.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe("Init group", () => {
})
);

await groupAdmin.userMarginProgram.provider.sendAndConfirm(tx, [
await groupAdmin.mrgnProgram.provider.sendAndConfirm(tx, [
marginfiGroup,
]);

Expand Down
6 changes: 3 additions & 3 deletions tests/02_configGroup.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe("Config group", () => {
const program = workspace.Marginfi as Program<Marginfi>;

it("(admin) Config group - no change", async () => {
await groupAdmin.userMarginProgram!.provider.sendAndConfirm!(
await groupAdmin.mrgnProgram!.provider.sendAndConfirm!(
new Transaction().add(
await groupConfigure(program, {
newAdmin: null,
Expand All @@ -33,7 +33,7 @@ describe("Config group", () => {

it("(admin) Config group - set new admin", async () => {
let newAdmin = Keypair.generate();
await groupAdmin.userMarginProgram!.provider.sendAndConfirm!(
await groupAdmin.mrgnProgram!.provider.sendAndConfirm!(
new Transaction().add(
await groupConfigure(program, {
newAdmin: newAdmin.publicKey,
Expand All @@ -49,7 +49,7 @@ describe("Config group", () => {
assertKeysEqual(group.admin, newAdmin.publicKey);

// Restore original
await groupAdmin.userMarginProgram!.provider.sendAndConfirm!(
await groupAdmin.mrgnProgram!.provider.sendAndConfirm!(
new Transaction().add(
await groupConfigure(program, {
newAdmin: groupAdmin.wallet.publicKey,
Expand Down
13 changes: 6 additions & 7 deletions tests/03_addBank.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import {
} from "./utils/pdas";
import { assert } from "chai";
import { printBufferGroups } from "./utils/tools";
import { wrappedI80F48toBigNumber } from "@mrgnlabs/mrgn-common";

describe("Lending pool add bank (add bank to group)", () => {
const program = workspace.Marginfi as Program<Marginfi>;
Expand All @@ -45,7 +44,7 @@ describe("Lending pool add bank (add bank to group)", () => {
globalFeeWallet
);

await groupAdmin.userMarginProgram!.provider.sendAndConfirm!(
await groupAdmin.mrgnProgram!.provider.sendAndConfirm!(
new Transaction().add(
await addBank(program, {
marginfiGroup: marginfiGroup.publicKey,
Expand Down Expand Up @@ -127,7 +126,7 @@ describe("Lending pool add bank (add bank to group)", () => {
assertI80F48Equal(config.assetWeightInit, 1);
assertI80F48Equal(config.assetWeightMaint, 1);
assertI80F48Equal(config.liabilityWeightInit, 1);
assertBNEqual(config.depositLimit, 1_000_000_000);
assertBNEqual(config.depositLimit, 100_000_000_000);

const tolerance = 0.000001;
assertI80F48Approx(interest.optimalUtilizationRate, 0.5, tolerance);
Expand All @@ -138,12 +137,13 @@ describe("Lending pool add bank (add bank to group)", () => {
assertI80F48Approx(interest.insuranceIrFee, 0.02, tolerance);
assertI80F48Approx(interest.protocolFixedFeeApr, 0.03, tolerance);
assertI80F48Approx(interest.protocolIrFee, 0.04, tolerance);
assertI80F48Approx(interest.protocolOriginationFee, 0.01, tolerance);

assert.deepEqual(config.operationalState, { operational: {} });
assert.deepEqual(config.oracleSetup, { pythLegacy: {} });
assertBNEqual(config.borrowLimit, 1_000_000_000);
assertBNEqual(config.borrowLimit, 100_000_000_000);
assert.deepEqual(config.riskTier, { collateral: {} });
assertBNEqual(config.totalAssetValueInitLimit, 100_000_000_000);
assertBNEqual(config.totalAssetValueInitLimit, 1_000_000_000_000);
assert.equal(config.oracleMaxAge, 100);

assertI80F48Equal(bank.collectedProgramFeesOutstanding, 0);
Expand All @@ -153,7 +153,7 @@ describe("Lending pool add bank (add bank to group)", () => {
let config = defaultBankConfig(oracles.tokenAOracle.publicKey);
let bankKey = bankKeypairA.publicKey;

await groupAdmin.userMarginProgram!.provider.sendAndConfirm!(
await groupAdmin.mrgnProgram!.provider.sendAndConfirm!(
new Transaction().add(
await addBank(program, {
marginfiGroup: marginfiGroup.publicKey,
Expand All @@ -176,7 +176,6 @@ describe("Lending pool add bank (add bank to group)", () => {
it("Decodes a mainnet bank configured before manual padding", async () => {
// mainnet program ID
const id = new PublicKey("MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA");
const tolerance = 0.000001;
const group = new PublicKey("4qp6Fx6tnZkY5Wropq9wUYgtFxXKwE6viZxFHg3rdAG8");

let bonkBankKey = new PublicKey(
Expand Down
Loading

0 comments on commit 244a3b3

Please sign in to comment.