Skip to content

Commit

Permalink
wip, getting change balance of read only account error, see test
Browse files Browse the repository at this point in the history
  • Loading branch information
wphan committed Sep 12, 2023
1 parent c1ecf3c commit 47e62e4
Show file tree
Hide file tree
Showing 13 changed files with 432 additions and 17 deletions.
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

0 comments on commit 47e62e4

Please sign in to comment.