Skip to content
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
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 @@ -36,6 +36,7 @@ pub use reset_delegate::*;
pub use reset_fuel_season::*;
pub use reset_vault_fuel_season::*;
pub use tokenize_shares::*;
pub use transfer_vault_depositor_shares::*;
pub use update_cumulative_fuel_amount::*;
pub use update_delegate::*;
pub use update_margin_trading_enabled::*;
Expand Down Expand Up @@ -84,6 +85,7 @@ mod reset_delegate;
mod reset_fuel_season;
mod reset_vault_fuel_season;
mod tokenize_shares;
mod transfer_vault_depositor_shares;
mod update_cumulative_fuel_amount;
mod update_delegate;
mod update_margin_trading_enabled;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
use anchor_lang::prelude::*;
use drift::instructions::optional_accounts::AccountMaps;
use drift::math::safe_math::SafeMath;
use drift::state::user::User;

use crate::constraints::{
is_authority_for_vault_depositor, is_user_for_vault, is_vault_for_vault_depositor,
};
use crate::error::ErrorCode;
use crate::state::traits::VaultDepositorBase;
use crate::{validate, AccountMapProvider};
use crate::{Vault, VaultDepositor, VaultProtocolProvider, WithdrawUnit};

pub fn transfer_vault_depositor_shares<'info>(
ctx: Context<'_, '_, 'info, 'info, TransferVaultDepositorShares<'info>>,
amount: u64,
withdraw_unit: WithdrawUnit,
) -> Result<()> {
let clock = &Clock::get()?;

let mut vault = ctx.accounts.vault.load_mut()?;

validate!(!vault.in_liquidation(), ErrorCode::OngoingLiquidation)?;

validate!(
ctx.accounts.vault_depositor.key() != ctx.accounts.to_vault_depositor.key(),
ErrorCode::InvalidVaultDeposit,
"Cannot transfer shares to the same depositor"
)?;

validate!(
amount > 0,
ErrorCode::InvalidVaultWithdrawSize,
"Transfer amount must be greater than 0"
)?;

let mut vault_depositor = ctx.accounts.vault_depositor.load_mut()?;
let mut to_vault_depositor = ctx.accounts.to_vault_depositor.load_mut()?;

// backwards compatible: if last rem acct does not deserialize into [`VaultProtocol`] then it's a legacy vault.
let mut vp = ctx.vault_protocol();
vault.validate_vault_protocol(&vp)?;
let mut vp = vp.as_mut().map(|vp| vp.load_mut()).transpose()?;

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),
vp.is_some(),
false,
false,
)?;

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

validate!(
!vault_depositor.last_withdraw_request.pending(),
ErrorCode::InvalidVaultDeposit,
"Cannot transfer shares with a pending withdraw request"
)?;

validate!(
!to_vault_depositor.last_withdraw_request.pending(),
ErrorCode::InvalidVaultDeposit,
"Cannot transfer shares to a depositor with a pending withdraw request"
)?;

let spot_market = spot_market_map.get_ref(&spot_market_index)?;
let oracle = oracle_map.get_price_data(&spot_market.oracle_id())?;

let total_shares_before = vault_depositor
.get_vault_shares()
.safe_add(to_vault_depositor.get_vault_shares())?;

vault_depositor.transfer_shares(
&mut *to_vault_depositor,
&mut vault,
&mut vp,
&mut None,
amount,
withdraw_unit,
vault_equity,
clock.unix_timestamp,
oracle.price,
)?;

let total_shares_after = vault_depositor
.get_vault_shares()
.safe_add(to_vault_depositor.get_vault_shares())?;

validate!(
total_shares_after.eq(&total_shares_before),
ErrorCode::InvalidVaultSharesDetected,
"Total vault depositor shares before != after"
)?;

Ok(())
}

#[derive(Accounts)]
pub struct TransferVaultDepositorShares<'info> {
#[account(mut)]
pub vault: AccountLoader<'info, Vault>,
#[account(
mut,
seeds = [b"vault_depositor", vault.key().as_ref(), authority.key().as_ref()],
bump,
constraint = is_authority_for_vault_depositor(&vault_depositor, &authority)?,
)]
pub vault_depositor: AccountLoader<'info, VaultDepositor>,
pub authority: Signer<'info>,
#[account(
mut,
constraint = is_vault_for_vault_depositor(&to_vault_depositor, &vault)?,
)]
pub to_vault_depositor: AccountLoader<'info, VaultDepositor>,
#[account(
constraint = is_user_for_vault(&vault, &drift_user.key())?
)]
pub drift_user: AccountLoader<'info, User>,
}
8 changes: 8 additions & 0 deletions programs/drift_vaults/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,14 @@ pub mod drift_vaults {
instructions::cancel_request_remove_insurance_fund_stake(ctx, market_index)
}

pub fn transfer_vault_depositor_shares<'info>(
ctx: Context<'_, '_, 'info, 'info, TransferVaultDepositorShares<'info>>,
amount: u64,
withdraw_unit: WithdrawUnit,
) -> Result<()> {
instructions::transfer_vault_depositor_shares(ctx, amount, withdraw_unit)
}

pub fn protocol_request_withdraw<'c: 'info, 'info>(
ctx: Context<'_, '_, 'c, 'info, ProtocolRequestWithdraw<'info>>,
withdraw_amount: u64,
Expand Down
4 changes: 2 additions & 2 deletions programs/drift_vaults/src/state/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,8 +307,8 @@ pub trait VaultDepositorBase {
let to_depositor_shares_after = to.checked_vault_shares(vault)?;

validate!(
from_depositor_shares_before.safe_add(to_depositor_shares_before)
== from_depositor_shares_after.safe_add(to_depositor_shares_after),
from_depositor_shares_before.safe_add(to_depositor_shares_before)?
== from_depositor_shares_after.safe_add(to_depositor_shares_after)?,
ErrorCode::InvalidVaultSharesDetected,
"VaultDepositor: total shares mismatch"
)?;
Expand Down
Loading
Loading