Skip to content

Commit

Permalink
program: manager can force_withdraw (#22)
Browse files Browse the repository at this point in the history
* program: manager can force_withdraw

* let vault delegate call force withdraw

* update types

* remove authority
  • Loading branch information
crispheaney authored Aug 9, 2023
1 parent ba2b6c0 commit 49935e5
Show file tree
Hide file tree
Showing 5 changed files with 304 additions and 0 deletions.
14 changes: 14 additions & 0 deletions programs/drift_vaults/src/instructions/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ use crate::{Vault, VaultDepositor};

use anchor_lang::prelude::*;

pub fn is_vault_for_vault_depositor(
vault_depositor: &AccountLoader<VaultDepositor>,
vault: &AccountLoader<Vault>,
) -> anchor_lang::Result<bool> {
Ok(vault_depositor.load()?.vault.eq(&vault.key()))
}

pub fn is_authority_for_vault_depositor(
vault_depositor: &AccountLoader<VaultDepositor>,
signer: &Signer,
Expand All @@ -16,6 +23,13 @@ pub fn is_manager_for_vault(
Ok(vault.load()?.manager.eq(signer.key))
}

pub fn is_delegate_for_vault(
vault: &AccountLoader<Vault>,
signer: &Signer,
) -> anchor_lang::Result<bool> {
Ok(vault.load()?.delegate.eq(signer.key))
}

pub fn is_user_for_vault(
vault_depositor: &AccountLoader<Vault>,
user_key: &Pubkey,
Expand Down
140 changes: 140 additions & 0 deletions programs/drift_vaults/src/instructions/force_withdraw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
use crate::constraints::*;
use crate::cpi::{TokenTransferCPI, WithdrawCPI};
use crate::{declare_vault_seeds, AccountMapProvider};
use crate::{Vault, VaultDepositor};
use anchor_lang::prelude::*;
use anchor_spl::token::{self, Transfer};
use anchor_spl::token::{Token, TokenAccount};
use drift::cpi::accounts::Withdraw as DriftWithdraw;
use drift::instructions::optional_accounts::AccountMaps;
use drift::program::Drift;
use drift::state::user::User;

pub fn force_withdraw<'info>(ctx: Context<'_, '_, '_, 'info, ForceWithdraw<'info>>) -> Result<()> {
let clock = &Clock::get()?;
let mut vault = ctx.accounts.vault.load_mut()?;
let mut vault_depositor = ctx.accounts.vault_depositor.load_mut()?;

let user = ctx.accounts.drift_user.load()?;
let spot_market_index = vault.spot_market_index;

let AccountMaps {
perp_market_map,
spot_market_map,
mut oracle_map,
} = ctx.load_maps(clock.slot, Some(spot_market_index))?;

let vault_equity =
vault.calculate_equity(&user, &perp_market_map, &spot_market_map, &mut oracle_map)?;

let (withdraw_amount, _) =
vault_depositor.withdraw(vault_equity, &mut vault, clock.unix_timestamp)?;

msg!("force_withdraw_amount: {}", withdraw_amount);

drop(vault);
drop(user);

ctx.drift_withdraw(withdraw_amount)?;

ctx.token_transfer(withdraw_amount)?;

Ok(())
}

#[derive(Accounts)]
pub struct ForceWithdraw<'info> {
#[account(
mut,
constraint = is_manager_for_vault(&vault, &manager)? || is_delegate_for_vault(&vault, &manager)?
)]
pub vault: AccountLoader<'info, Vault>,
pub manager: Signer<'info>,
#[account(
mut,
constraint = is_vault_for_vault_depositor(&vault_depositor, &vault)?,
)]
pub vault_depositor: AccountLoader<'info, VaultDepositor>,
#[account(
mut,
seeds = [b"vault_token_account".as_ref(), vault.key().as_ref()],
bump,
)]
pub vault_token_account: Box<Account<'info, TokenAccount>>,
#[account(
mut,
constraint = is_user_stats_for_vault(&vault, &drift_user_stats)?
)]
/// CHECK: checked in drift cpi
pub drift_user_stats: AccountInfo<'info>,
#[account(
mut,
constraint = is_user_for_vault(&vault, &drift_user.key())?
)]
/// CHECK: checked in drift cpi
pub drift_user: AccountLoader<'info, User>,
/// CHECK: checked in drift cpi
pub drift_state: AccountInfo<'info>,
#[account(
mut,
token::mint = vault_token_account.mint
)]
pub drift_spot_market_vault: Box<Account<'info, TokenAccount>>,
/// CHECK: checked in drift cpi
pub drift_signer: AccountInfo<'info>,
#[account(
mut,
token::authority = vault_depositor.load()?.authority,
token::mint = vault_token_account.mint
)]
pub user_token_account: Box<Account<'info, TokenAccount>>,
pub drift_program: Program<'info, Drift>,
pub token_program: Program<'info, Token>,
}

impl<'info> WithdrawCPI for Context<'_, '_, '_, 'info, ForceWithdraw<'info>> {
fn drift_withdraw(&self, amount: u64) -> Result<()> {
declare_vault_seeds!(self.accounts.vault, seeds);
let spot_market_index = self.accounts.vault.load()?.spot_market_index;

let cpi_accounts = DriftWithdraw {
state: self.accounts.drift_state.to_account_info().clone(),
user: self.accounts.drift_user.to_account_info().clone(),
user_stats: self.accounts.drift_user_stats.to_account_info().clone(),
authority: self.accounts.vault.to_account_info().clone(),
spot_market_vault: self
.accounts
.drift_spot_market_vault
.to_account_info()
.clone(),
drift_signer: self.accounts.drift_signer.to_account_info().clone(),
user_token_account: self.accounts.vault_token_account.to_account_info().clone(),
token_program: self.accounts.token_program.to_account_info().clone(),
};

let drift_program = self.accounts.drift_program.to_account_info().clone();
let cpi_context = CpiContext::new_with_signer(drift_program, cpi_accounts, seeds)
.with_remaining_accounts(self.remaining_accounts.into());
drift::cpi::withdraw(cpi_context, spot_market_index, amount, false)?;

Ok(())
}
}

impl<'info> TokenTransferCPI for Context<'_, '_, '_, 'info, ForceWithdraw<'info>> {
fn token_transfer(&self, amount: u64) -> Result<()> {
declare_vault_seeds!(self.accounts.vault, seeds);

let cpi_accounts = Transfer {
from: self.accounts.vault_token_account.to_account_info().clone(),
to: self.accounts.user_token_account.to_account_info().clone(),
authority: self.accounts.vault.to_account_info().clone(),
};
let token_program = self.accounts.token_program.to_account_info().clone();
let cpi_context = CpiContext::new_with_signer(token_program, cpi_accounts, seeds);

token::transfer(cpi_context, amount)?;

Ok(())
}
}
2 changes: 2 additions & 0 deletions programs/drift_vaults/src/instructions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub use cancel_withdraw_request::*;
pub use deposit::*;
pub use force_withdraw::*;
pub use initialize_vault::*;
pub use initialize_vault_depositor::*;
pub use liquidate::*;
Expand All @@ -15,6 +16,7 @@ pub use withdraw::*;
mod cancel_withdraw_request;
pub mod constraints;
mod deposit;
mod force_withdraw;
mod initialize_vault;
mod initialize_vault_depositor;
mod liquidate;
Expand Down
6 changes: 6 additions & 0 deletions programs/drift_vaults/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ pub mod drift_vaults {
instructions::withdraw(ctx)
}

pub fn force_withdraw<'info>(
ctx: Context<'_, '_, '_, 'info, ForceWithdraw<'info>>,
) -> Result<()> {
instructions::force_withdraw(ctx)
}

pub fn liquidate<'info>(ctx: Context<'_, '_, '_, 'info, Liquidate<'info>>) -> Result<()> {
instructions::liquidate(ctx)
}
Expand Down
142 changes: 142 additions & 0 deletions ts/sdk/src/types/drift_vaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,77 @@ export type DriftVaults = {
];
args: [];
},
{
name: 'forceWithdraw';
accounts: [
{
name: 'vault';
isMut: true;
isSigner: false;
},
{
name: 'manager';
isMut: false;
isSigner: true;
},
{
name: 'vaultDepositor';
isMut: true;
isSigner: false;
},
{
name: 'authority';
isMut: false;
isSigner: true;
},
{
name: 'vaultTokenAccount';
isMut: true;
isSigner: false;
},
{
name: 'driftUserStats';
isMut: true;
isSigner: false;
},
{
name: 'driftUser';
isMut: true;
isSigner: false;
},
{
name: 'driftState';
isMut: false;
isSigner: false;
},
{
name: 'driftSpotMarketVault';
isMut: true;
isSigner: false;
},
{
name: 'driftSigner';
isMut: false;
isSigner: false;
},
{
name: 'userTokenAccount';
isMut: true;
isSigner: false;
},
{
name: 'driftProgram';
isMut: false;
isSigner: false;
},
{
name: 'tokenProgram';
isMut: false;
isSigner: false;
}
];
args: [];
},
{
name: 'liquidate';
accounts: [
Expand Down Expand Up @@ -1612,6 +1683,77 @@ export const IDL: DriftVaults = {
],
args: [],
},
{
name: 'forceWithdraw',
accounts: [
{
name: 'vault',
isMut: true,
isSigner: false,
},
{
name: 'manager',
isMut: false,
isSigner: true,
},
{
name: 'vaultDepositor',
isMut: true,
isSigner: false,
},
{
name: 'authority',
isMut: false,
isSigner: true,
},
{
name: 'vaultTokenAccount',
isMut: true,
isSigner: false,
},
{
name: 'driftUserStats',
isMut: true,
isSigner: false,
},
{
name: 'driftUser',
isMut: true,
isSigner: false,
},
{
name: 'driftState',
isMut: false,
isSigner: false,
},
{
name: 'driftSpotMarketVault',
isMut: true,
isSigner: false,
},
{
name: 'driftSigner',
isMut: false,
isSigner: false,
},
{
name: 'userTokenAccount',
isMut: true,
isSigner: false,
},
{
name: 'driftProgram',
isMut: false,
isSigner: false,
},
{
name: 'tokenProgram',
isMut: false,
isSigner: false,
},
],
args: [],
},
{
name: 'liquidate',
accounts: [
Expand Down

0 comments on commit 49935e5

Please sign in to comment.