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

Allow vault deletion #42

Closed
wants to merge 1 commit into from
Closed
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
4 changes: 4 additions & 0 deletions programs/drift_vaults/src/cpi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ pub trait InitializeUserCPI {
fn drift_initialize_user_stats(&self, name: [u8; 32], bump: u8) -> Result<()>;
}

pub trait DeleteUserCPI {
fn delete_user(&self, name: [u8; 32], bump: u8) -> Result<()>;
}

pub trait DepositCPI {
fn drift_deposit(&self, amount: u64) -> Result<()>;
}
Expand Down
2 changes: 2 additions & 0 deletions programs/drift_vaults/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ pub enum ErrorCode {
InvalidVaultDeposit,
#[msg("OngoingLiquidation")]
OngoingLiquidation,
#[msg("VaultCantBeDeleted")]
VaultCantBeDeleted,
}

impl From<DriftErrorCode> for ErrorCode {
Expand Down
7 changes: 7 additions & 0 deletions programs/drift_vaults/src/instructions/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,10 @@ pub fn is_user_stats_for_vault(
) -> anchor_lang::Result<bool> {
Ok(vault_depositor.load()?.user_stats.eq(user_stats.key))
}

pub fn is_spot_market_index(
vault: &AccountLoader<Vault>,
spot_market_index: u16,
) -> anchor_lang::Result<bool> {
Ok(vault.load()?.spot_market_index == spot_market_index)
}
96 changes: 96 additions & 0 deletions programs/drift_vaults/src/instructions/delete_vault.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use crate::constraints::{
is_manager_for_vault, is_spot_market_index, is_user_for_vault, is_user_stats_for_vault,
};

use crate::cpi::DeleteUserCPI;
use crate::declare_vault_seeds;
use crate::{error::ErrorCode, validate, Vault};
use anchor_lang::prelude::*;
use anchor_spl::token::{Mint, TokenAccount};
use drift::cpi::accounts::DeleteUser as DriftDeleteUser;
use drift::program::Drift;
use drift::state::spot_market::SpotMarket;
use drift::state::user::User;

pub fn delete_vault<'info>(ctx: Context<'_, '_, '_, 'info, DeleteVault<'info>>) -> Result<()> {
let vault = ctx.accounts.vault.load()?;

validate!(
vault.total_shares == 0,
ErrorCode::VaultCantBeDeleted,
"cannot delete vault with outstanding shares"
);

ctx.delete_user(vault.name, vault.bump)?;

Ok(())
}

#[derive(Accounts)]
pub struct DeleteVault<'info> {
#[account(
mut,
constraint = is_manager_for_vault(&vault, &manager)?,
close = payer,
)]
pub vault: AccountLoader<'info, Vault>,
#[account(
mut,
close = payer,
)]
pub token_account: Box<Account<'info, TokenAccount>>,
#[account(
mut,
constraint = is_spot_market_index(&vault, drift_spot_market.load()?.market_index)?,
)]
pub drift_spot_market: AccountLoader<'info, SpotMarket>,
#[account(
mut,
constraint = drift_spot_market.load()?.mint.eq(&drift_spot_market_mint.key())
)]
pub drift_spot_market_mint: Box<Account<'info, Mint>>,
/// CHECK: checked in drift cpi
#[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())?,
// close = manager
)]
/// CHECK: checked in drift cpi
pub drift_user: AccountLoader<'info, User>,
/// CHECK: checked in drift cpi
#[account(mut)]
pub drift_state: AccountInfo<'info>,
pub drift_program: Program<'info, Drift>,
#[account(mut)]
pub manager: Signer<'info>,
#[account(mut)]
pub payer: Signer<'info>,
pub rent: Sysvar<'info, Rent>,
}

impl<'info> DeleteUserCPI for Context<'_, '_, '_, 'info, DeleteVault<'info>> {
fn delete_user(&self, name: [u8; 32], bump: u8) -> Result<()> {
let signature_seeds = Vault::get_vault_signer_seeds(&name, &bump);
let signers = &[&signature_seeds[..]];

let cpi_program = self.accounts.drift_program.to_account_info().clone();
let cpi_accounts = DriftDeleteUser {
state: self.accounts.drift_state.clone(),
user: self.accounts.drift_user.to_account_info().clone(),
user_stats: self.accounts.drift_user_stats.clone(),
authority: self.accounts.vault.to_account_info().clone(),
};
let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signers);
// .with_remaining_accounts(self.remaining_accounts.into());

drift::cpi::delete_user(cpi_ctx)?;

Ok(())
}
}
2 changes: 2 additions & 0 deletions programs/drift_vaults/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub use cancel_withdraw_request::*;
pub use deposit::*;
pub use force_withdraw::*;
pub use initialize_vault::*;
pub use delete_vault::*;
pub use initialize_vault_depositor::*;
pub use liquidate::*;
pub use manager_cancel_withdraw_request::*;
Expand All @@ -23,6 +24,7 @@ pub mod constraints;
mod deposit;
mod force_withdraw;
mod initialize_vault;
mod delete_vault;
mod initialize_vault_depositor;
mod liquidate;
mod manager_cancel_withdraw_request;
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 @@ -23,6 +23,12 @@ pub mod drift_vaults {
instructions::initialize_vault(ctx, params)
}

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

pub fn update_delegate<'info>(
ctx: Context<'_, '_, '_, 'info, UpdateDelegate<'info>>,
delegate: Pubkey,
Expand Down
44 changes: 44 additions & 0 deletions tests/driftVaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ describe('driftVaults', () => {
profitShare: ZERO,
hurdleRate: ZERO,
permissioned: false,
minDepositAmount: ZERO,
});

await adminClient.fetchAccounts();
Expand Down Expand Up @@ -217,4 +218,47 @@ describe('driftVaults', () => {

await printTxLogs(provider.connection, txSig);
});

it('Delete Vault', async () => {
const newVaultName = "another vault";
await vaultClient.initializeVault({
name: encodeName(newVaultName),
spotMarketIndex: 0,
redeemPeriod: ZERO,
maxTokens: ZERO,
managementFee: ZERO,
profitShare: ZERO,
hurdleRate: ZERO,
permissioned: false,
minDepositAmount: ZERO,
});
const vaultAddress = getVaultAddressSync(program.programId, encodeName(newVaultName));
console.log(`New vault address: ${vaultAddress.toBase58()}`);

await adminClient.fetchAccounts();
assert(adminClient.getStateAccount().numberOfAuthorities.eq(new BN(2)));
assert(adminClient.getStateAccount().numberOfSubAccounts.eq(new BN(2)));

console.log("deleting vault");
await vaultClient.deleteVault(vaultAddress);

await adminClient.fetchAccounts();
assert(adminClient.getStateAccount().numberOfAuthorities.eq(new BN(1)));
assert(adminClient.getStateAccount().numberOfSubAccounts.eq(new BN(1)));

// await vaultClient.initializeVault({
// name: encodeName(newVaultName),
// spotMarketIndex: 0,
// redeemPeriod: ZERO,
// maxTokens: ZERO,
// managementFee: ZERO,
// profitShare: ZERO,
// hurdleRate: ZERO,
// permissioned: false,
// minDepositAmount: ZERO,
// });
// await adminClient.fetchAccounts();
// assert(adminClient.getStateAccount().numberOfAuthorities.eq(new BN(2)));
// assert(adminClient.getStateAccount().numberOfSubAccounts.eq(new BN(2)));
});
});
2 changes: 1 addition & 1 deletion ts/sdk/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ program
program
.command("init")
.description("Initialize a new vault")
.option("-n, --name <vaultName>", "Name of the vault to create", "my new vault")
.addOption(new Option("--delegate <delegate>", "Address of the delegate to trade the vault, default is vault manager").makeOptionMandatory(false))
.action((opts) => initVault(program, opts));
program
.command("view-vault")
Expand Down
45 changes: 29 additions & 16 deletions ts/sdk/cli/commands/initVault.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,40 @@ export const initVault = async (program: Command, cmdOpts: OptionValues) => {
}
const spotPrecision = TEN.pow(new BN(spotMarket.decimals));

let newVaultName = cmdOpts.name;
if (!newVaultName) {
newVaultName = "my new vault";
}
const vaultNameBytes = encodeName(newVaultName!);
console.log(`Initializing a new vault named '${newVaultName}'`);

const initTx = await driftVault.initializeVault({
name: vaultNameBytes,
// throw new Error("[initVault] You're gonna want to find this message and complete the code");

// WARNING: fill in the below
// const initTx = await driftVault.initializeVault({
// name: encodeName("my new vault"),
// spotMarketIndex: 0,
// redeemPeriod: new BN(3 * 60 * 60), // 3 hours
// maxTokens: new BN(1000).mul(spotPrecision), // 1000 USDC cap
// managementFee: PERCENTAGE_PRECISION.div(new BN(50)), // 2%
// profitShare: PERCENTAGE_PRECISION.div(new BN(5)), // 20%
// hurdleRate: 0,
// permissioned: false,
// minDepositAmount: new BN(10).mul(spotPrecision), // 10 USDC minimum deposit
// });
const vaultParams = {
name: encodeName("Supercharger Vault"),
spotMarketIndex: 0,
redeemPeriod: new BN(3 * 60 * 60), // 3 hours
maxTokens: new BN(1000).mul(spotPrecision), // 1000 USDC cap
managementFee: PERCENTAGE_PRECISION.div(new BN(50)), // 2%
profitShare: PERCENTAGE_PRECISION.div(new BN(5)), // 20%
redeemPeriod: new BN(30 * 24 * 60 * 60), // 30 days
maxTokens: new BN(100_000).mul(spotPrecision),
managementFee: new BN(0), // 0%
profitShare: PERCENTAGE_PRECISION.mul(new BN(3)).div(new BN(10)), // 30%
hurdleRate: 0,
permissioned: false,
minDepositAmount: new BN(10).mul(spotPrecision), // 10 USDC minimum deposit
});
permissioned: true,
minDepositAmount: new BN(1).mul(spotPrecision), // 1 USDC minimum deposit
}
console.log(`Initializing vault based on params':\n${JSON.stringify(vaultParams, null, 2)}'`);

// throw new Error("check it");

const initTx = await driftVault.initializeVault(vaultParams);
console.log(`Initialized vault, tx: ${initTx}`);

const vaultAddress = getVaultAddressSync(VAULT_PROGRAM_ID, vaultNameBytes);
const vaultAddress = getVaultAddressSync(VAULT_PROGRAM_ID, vaultParams.name);
console.log(`New vault address: ${vaultAddress}`);

let delegate = cmdOpts.delegate;
Expand Down
4 changes: 4 additions & 0 deletions ts/sdk/cli/commands/managerUpdateVault.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export const managerUpdateVault = async (program: Command, cmdOpts: OptionValues
driftVault
} = await getCommandContext(program, true);

// throw new Error("[updateVaultDepositor]: You're gonna want to find this message and complete the code");

// WARNING: fill in the below

const newParams = {
redeemPeriod: new BN(30 * 60 * 60 * 24), // 30 days
maxTokens: null,
Expand Down
66 changes: 66 additions & 0 deletions ts/sdk/src/idl/drift_vaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,67 @@
}
]
},
{
"name": "deleteVault",
"accounts": [
{
"name": "vault",
"isMut": true,
"isSigner": false
},
{
"name": "tokenAccount",
"isMut": true,
"isSigner": false
},
{
"name": "driftSpotMarket",
"isMut": true,
"isSigner": false
},
{
"name": "driftSpotMarketMint",
"isMut": true,
"isSigner": false
},
{
"name": "driftUserStats",
"isMut": true,
"isSigner": false
},
{
"name": "driftUser",
"isMut": true,
"isSigner": false
},
{
"name": "driftState",
"isMut": true,
"isSigner": false
},
{
"name": "driftProgram",
"isMut": false,
"isSigner": false
},
{
"name": "manager",
"isMut": true,
"isSigner": true
},
{
"name": "payer",
"isMut": true,
"isSigner": true
},
{
"name": "rent",
"isMut": false,
"isSigner": false
}
],
"args": []
},
{
"name": "updateDelegate",
"accounts": [
Expand Down Expand Up @@ -1539,6 +1600,11 @@
"code": 6021,
"name": "OngoingLiquidation",
"msg": "OngoingLiquidation"
},
{
"code": 6022,
"name": "VaultCantBeDeleted",
"msg": "VaultCantBeDeleted"
}
],
"metadata": {
Expand Down
Loading