Skip to content

Commit

Permalink
[CAS-189] Add vault halting (#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
charlieyou authored May 5, 2022
1 parent 041f2e7 commit 8bab532
Show file tree
Hide file tree
Showing 17 changed files with 356 additions and 34 deletions.
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion programs/castle-vault/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ homepage = "https://castle.finance"
license = "GPL-3.0-or-later"
name = "castle-vault"
repository = "https://github.com/castle-finance/castle-vault/"
version = "2.0.1"
version = "2.1.0"

[lib]
crate-type = ["cdylib", "lib"]
Expand All @@ -32,6 +32,7 @@ no-idl = []
[dependencies]
anchor-lang = "0.18.2"
anchor-spl = "0.18.2"
bitflags = "1.3"
boolinator = "2.4.0"
itertools = "0.10"
jet = {git = "https://github.com/jet-lab/jet-v1/", version = "0.2.0", features = ["no-entrypoint", "cpi"]}
Expand Down
6 changes: 6 additions & 0 deletions programs/castle-vault/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,10 @@ pub enum ErrorCode {

#[msg("Allocation cap cannot set to under 1/(number of assets) or over 100%")]
InvalidAllocationCap,

#[msg("Bits passed in do not result in valid vault flags")]
InvalidVaultFlags,

#[msg("Vault is halted")]
HaltedVault,
}
18 changes: 16 additions & 2 deletions programs/castle-vault/src/instructions/deposit.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
use std::convert::Into;

use boolinator::Boolinator;

use anchor_lang::prelude::*;
use anchor_spl::token::{self, Mint, MintTo, Token, TokenAccount, Transfer};

use crate::{errors::ErrorCode, state::Vault};
use std::convert::Into;
use crate::{
errors::ErrorCode,
state::{Vault, VaultFlags},
};

#[event]
pub struct DepositEvent {
Expand Down Expand Up @@ -87,6 +93,14 @@ pub fn handler(ctx: Context<Deposit>, reserve_token_amount: u64) -> ProgramResul
#[cfg(feature = "debug")]
msg!("Depositing {} reserve tokens", reserve_token_amount);

// Check that deposits are not halted
(!ctx
.accounts
.vault
.flags()
.contains(VaultFlags::HALT_DEPOSITS_WITHDRAWS))
.ok_or::<ProgramError>(ErrorCode::HaltedVault.into())?;

let vault = &ctx.accounts.vault;

let lp_tokens_to_mint = crate::math::calc_reserve_to_lp(
Expand Down
10 changes: 5 additions & 5 deletions programs/castle-vault/src/instructions/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,8 @@ pub fn handler(

let vault = &mut ctx.accounts.vault;
vault.version = get_version_arr();
vault.vault_authority = ctx.accounts.vault_authority.key();
vault.owner = ctx.accounts.owner.key();
vault.vault_authority = ctx.accounts.vault_authority.key();
vault.authority_seed = vault.key();
vault.authority_bump = [bumps.authority];
vault.solend_reserve = ctx.accounts.solend_reserve.key();
Expand All @@ -210,10 +210,10 @@ pub fn handler(
vault.reserve_token_mint = ctx.accounts.reserve_token_mint.key();
vault.fee_receiver = ctx.accounts.fee_receiver.key();
vault.referral_fee_receiver = ctx.accounts.referral_fee_receiver.key();

vault.value = SlotTrackedValue::default();
vault.value.update(0, clock.slot);

vault.value = SlotTrackedValue {
value: 0,
last_update: LastUpdate::new(clock.slot),
};
vault.config = VaultConfig::new(config)?;

// Initialize fee receiver account
Expand Down
2 changes: 2 additions & 0 deletions programs/castle-vault/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod rebalance;
pub mod reconcile;
pub mod refresh;
pub mod update_config;
pub mod update_flags;
pub mod withdraw;

pub use deposit::*;
Expand All @@ -12,4 +13,5 @@ pub use rebalance::*;
pub use reconcile::*;
pub use refresh::*;
pub use update_config::*;
pub use update_flags::*;
pub use withdraw::*;
15 changes: 14 additions & 1 deletion programs/castle-vault/src/instructions/reconcile.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
use std::cmp;

use anchor_lang::prelude::*;
use boolinator::Boolinator;

use crate::{errors::ErrorCode, reserves::Provider, state::Vault};
use crate::{
errors::ErrorCode,
reserves::Provider,
state::{Vault, VaultFlags},
};

const MAX_SLOTS_SINCE_ALLOC_UPDATE: u64 = 100;

Expand Down Expand Up @@ -48,6 +53,14 @@ pub fn handler<T: LendingMarket + HasVault>(
ctx: Context<T>,
withdraw_option: u64,
) -> ProgramResult {
// Check that reconciles are not halted
(!ctx
.accounts
.vault()
.flags()
.contains(VaultFlags::HALT_RECONCILES))
.ok_or::<ProgramError>(ErrorCode::HaltedVault.into())?;

let provider = ctx.accounts.provider();
match withdraw_option {
// Normal case where reconcile is being called after rebalance
Expand Down
12 changes: 11 additions & 1 deletion programs/castle-vault/src/instructions/refresh.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
#![allow(dead_code)]
#![allow(unused_imports)]

use boolinator::Boolinator;

use anchor_lang::prelude::*;
use anchor_spl::token::{self, Mint, MintTo, Token, TokenAccount};
use port_anchor_adaptor::{port_lending_id, PortReserve};

use crate::{
adapters::{solend, SolendReserve},
errors::ErrorCode,
state::Vault,
state::{Vault, VaultFlags},
};

// NOTE: having all accounts for each lending market reserve here is not scalable
Expand Down Expand Up @@ -191,6 +193,14 @@ pub fn handler<'info>(
#[cfg(feature = "debug")]
msg!("Refreshing vault");

// Check that refreshes are not halted
(!ctx
.accounts
.vault
.flags()
.contains(VaultFlags::HALT_REFRESHES))
.ok_or::<ProgramError>(ErrorCode::HaltedVault.into())?;

// Refresh lending market reserves
solend::refresh_reserve(ctx.accounts.solend_refresh_reserve_context())?;
port_anchor_adaptor::refresh_port_reserve(
Expand Down
23 changes: 23 additions & 0 deletions programs/castle-vault/src/instructions/update_flags.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use anchor_lang::prelude::*;

use std::convert::Into;

use crate::state::Vault;

#[derive(Accounts)]
pub struct UpdateFlags<'info> {
#[account(
mut,
has_one = owner,
)]
pub vault: Box<Account<'info, Vault>>,

pub owner: Signer<'info>,
}

pub fn handler(ctx: Context<UpdateFlags>, flags: u32) -> ProgramResult {
#[cfg(feature = "debug")]
msg!("New flags: {:?}", flags);

ctx.accounts.vault.set_flags(flags)
}
19 changes: 16 additions & 3 deletions programs/castle-vault/src/instructions/withdraw.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use std::convert::Into;

use boolinator::Boolinator;

use anchor_lang::prelude::*;
use anchor_spl::token::{self, Burn, Mint, Token, TokenAccount, Transfer};

use std::convert::Into;

use crate::{errors::ErrorCode, state::Vault};
use crate::{
errors::ErrorCode,
state::{Vault, VaultFlags},
};

#[event]
pub struct WithdrawEvent {
Expand Down Expand Up @@ -88,6 +93,14 @@ pub fn handler(ctx: Context<Withdraw>, lp_token_amount: u64) -> ProgramResult {
#[cfg(feature = "debug")]
msg!("Withdrawing {} lp tokens", lp_token_amount);

// Check that withdrawals are not halted
(!ctx
.accounts
.vault
.flags()
.contains(VaultFlags::HALT_DEPOSITS_WITHDRAWS))
.ok_or::<ProgramError>(ErrorCode::HaltedVault.into())?;

let vault = &ctx.accounts.vault;

let reserve_tokens_to_transfer = crate::math::calc_lp_to_reserve(
Expand Down
4 changes: 4 additions & 0 deletions programs/castle-vault/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ pub mod castle_vault {
instructions::init::handler(ctx, _bumps, config)
}

pub fn update_flags(ctx: Context<UpdateFlags>, flags: u32) -> ProgramResult {
instructions::update_flags::handler(ctx, flags)
}

pub fn update_config(ctx: Context<UpdateConfig>, new_config: VaultConfigArg) -> ProgramResult {
instructions::update_config::handler(ctx, new_config)
}
Expand Down
34 changes: 32 additions & 2 deletions programs/castle-vault/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub struct Vault {

pub referral_fee_receiver: Pubkey,

_padding: [u8; 4],
bitflags: u32,

/// Total value of vault denominated in the reserve token
pub value: SlotTrackedValue,
Expand All @@ -80,7 +80,18 @@ pub struct Vault {
}

impl Vault {
// TODO use a more specific error type
pub fn flags(&self) -> VaultFlags {
VaultFlags::from_bits(self.bitflags)
.unwrap_or_else(|| panic!("{:?} does not resolve to valid VaultFlags", self.bitflags))
}

pub fn set_flags(&mut self, bits: u32) -> ProgramResult {
VaultFlags::from_bits(bits)
.ok_or_else::<ProgramError, _>(|| ErrorCode::InvalidVaultFlags.into())?;
self.bitflags = bits;
Ok(())
}

pub fn calculate_fees(&self, new_vault_value: u64, slot: u64) -> Result<u64, ProgramError> {
let vault_value_diff = new_vault_value.saturating_sub(self.value.value);
let slots_elapsed = self.value.last_update.slots_elapsed(slot)?;
Expand Down Expand Up @@ -195,6 +206,25 @@ pub enum StrategyType {
EqualAllocation,
}

bitflags::bitflags! {
pub struct VaultFlags: u32 {
/// Disable reconciles
const HALT_RECONCILES = 1 << 0;

/// Disable refreshes
const HALT_REFRESHES = 1 << 1;

/// Disable deposits + withdrawals
const HALT_DEPOSITS_WITHDRAWS = 1 << 2;

/// Disable all operations
const HALT_ALL = Self::HALT_RECONCILES.bits
| Self::HALT_REFRESHES.bits
| Self::HALT_DEPOSITS_WITHDRAWS.bits;

}
}

#[assert_size(aligns, 72)]
#[repr(C, align(8))]
#[derive(AnchorDeserialize, AnchorSerialize, Clone, Copy, Debug, Default)]
Expand Down
2 changes: 1 addition & 1 deletion sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@castlefinance/vault-sdk",
"version": "2.0.1",
"version": "2.1.0",
"license": "MIT",
"main": "./lib/index.js",
"typings": "./lib/index.d.ts",
Expand Down
25 changes: 25 additions & 0 deletions sdk/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
RebalanceDataEvent,
Vault,
VaultConfig,
VaultFlags,
} from "./types";

export class VaultClient {
Expand Down Expand Up @@ -349,6 +350,26 @@ export class VaultClient {
};
}

/**
* @param new_value
* @returns
*/
async updateFlags(
owner: Keypair,
flags: number
): Promise<TransactionSignature> {
const updateTx = new Transaction();
updateTx.add(
this.program.instruction.updateFlags(flags, {
accounts: {
vault: this.vaultId,
owner: owner.publicKey,
},
})
);
return await this.program.provider.send(updateTx, [owner]);
}

/**
* @param new_value
* @returns
Expand Down Expand Up @@ -1037,6 +1058,10 @@ export class VaultClient {
getManagementFee(): number {
return this.vaultState.config.feeMgmtBps / 10000;
}

getFlags(): VaultFlags {
return this.vaultState.bitflags;
}
}

const createAta = (
Expand Down
Loading

0 comments on commit 8bab532

Please sign in to comment.