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

Update VaultDepositor high watermark on net profit #65

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions Cargo.lock

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

5 changes: 4 additions & 1 deletion programs/drift_vaults/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,7 @@ bytemuck = { version = "1.4.0" }
static_assertions = "1.1.0"
drift-macros = { git = "https://github.com/drift-labs/drift-macros.git", rev = "c57d87" }
ahash = "=0.8.6"
serde = "=1.0.209"
serde = "=1.0.209"

[dev-dependencies]
base64 = "0.13.0"
3 changes: 3 additions & 0 deletions programs/drift_vaults/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ mod error;
mod instructions;
pub mod macros;
pub mod state;
#[cfg(test)]
mod test_utils;
#[cfg(test)]
mod tests;
mod token_cpi;

Expand Down
4 changes: 3 additions & 1 deletion programs/drift_vaults/src/state/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,11 @@ pub trait VaultDepositorBase {
let profit_share_amount =
manager_profit_share_amount.safe_add(protocol_profit_share_amount)?;

let net_profit = profit_u128.safe_sub(profit_share_amount)?;

self.set_cumulative_profit_share_amount(
self.get_cumulative_profit_share_amount()
.safe_add(profit_u128.cast()?)?,
.safe_add(net_profit.cast()?)?,
);

self.set_profit_share_fee_paid(
Expand Down
8 changes: 4 additions & 4 deletions programs/drift_vaults/src/state/vault_depositor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1131,8 +1131,8 @@ mod vault_v1_tests {
// total shares outside of user is now 100M + 7.5M = 107.5M
assert_eq!(vault.total_shares, 107_500_000);
assert_eq!(withdraw_amount, equity_minus_fee);
// $100 worth of profit that has been realized (this is not total fees paid)
assert_eq!(vd.cumulative_profit_share_amount, 100_000_000);
// $85 = 100 - 10% - 5%, worth of profit that has been realized (this is not total fees paid)
assert_eq!(vd.cumulative_profit_share_amount, 85_000_000);
println!("vault shares: {}", vd.checked_vault_shares(&vault).unwrap());
println!("shares base: {}", vd.vault_shares_base);
println!("user shares: {}", vault.user_shares);
Expand Down Expand Up @@ -1245,8 +1245,8 @@ mod vault_v1_tests {
// total shares outside of user is now 100M + 5M = 105M
assert_eq!(vault.total_shares, 105_000_000);
assert_eq!(withdraw_amount, equity_minus_fee);
// $100 worth of profit that has been realized (this is not total fees paid)
assert_eq!(vd.cumulative_profit_share_amount, 100_000_000);
// $90 = $100 - 10% worth of profit that has been realized (this is not total fees paid)
assert_eq!(vd.cumulative_profit_share_amount, 90_000_000);
println!("vault shares: {}", vd.checked_vault_shares(&vault).unwrap());
println!("shares base: {}", vd.vault_shares_base);
println!("user shares: {}", vault.user_shares);
Expand Down
27 changes: 27 additions & 0 deletions programs/drift_vaults/src/test_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use anchor_lang::prelude::{AccountInfo, Pubkey};

pub fn create_account_info<'a>(
key: &'a Pubkey,
is_writable: bool,
lamports: &'a mut u64,
bytes: &'a mut [u8],
owner: &'a Pubkey,
) -> AccountInfo<'a> {
AccountInfo::new(key, false, is_writable, lamports, bytes, owner, false, 0)
}

#[macro_export]
macro_rules! create_account_info {
($account:expr, $owner:expr, $name: ident) => {
let key = Pubkey::default();
let mut lamports = 0;
let mut data = get_account_bytes(&mut $account);
let owner = $type::owner();
let $name = create_account_info(&key, true, &mut lamports, &mut data[..], $owner);
};
($account:expr, $pubkey:expr, $owner:expr, $name: ident) => {
let mut lamports = 0;
let mut data = get_account_bytes(&mut $account);
let $name = create_account_info($pubkey, true, &mut lamports, &mut data[..], $owner);
};
}
168 changes: 163 additions & 5 deletions programs/drift_vaults/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
#[cfg(test)]
mod vault_fcn {
use std::str::FromStr;

use crate::state::traits::VaultDepositorBase;
use crate::test_utils::create_account_info;
use crate::withdraw_request::WithdrawRequest;
use crate::{Vault, VaultDepositor, WithdrawUnit};
use anchor_lang::prelude::Pubkey;
use drift::math::constants::{ONE_YEAR, QUOTE_PRECISION_U64};
use anchor_lang::prelude::{AccountLoader, Pubkey};
use drift::math::constants::{
ONE_YEAR, QUOTE_PRECISION, QUOTE_PRECISION_I64, QUOTE_PRECISION_U64,
};
use drift::math::insurance::if_shares_to_vault_amount as depositor_shares_to_vault_amount;

#[test]
Expand Down Expand Up @@ -498,7 +503,7 @@ mod vault_fcn {
assert_eq!(cnt, 4); // 4 days
assert_eq!(
vd.cumulative_profit_share_amount,
(1000 * QUOTE_PRECISION_U64) as i64
(850 * QUOTE_PRECISION_U64) as i64 // 1000 - 15% profit share
);
assert_eq!(vd.net_deposits, (2000 * QUOTE_PRECISION_U64) as i64);

Expand Down Expand Up @@ -747,6 +752,159 @@ mod vault_fcn {
);
assert!(!finishing_liquidation);
}

#[test]
fn test_apply_profit_share_on_net_hwm() {
let mut now = 123456789;
let mut vault = Vault::default();
let mut vp = None;
vault.management_fee = 0;
vault.profit_share = 100_000; // 10%
vault.last_fee_update_ts = now;

let mut vault_equity: u64 = 0;

let deposit_amount = 2000 * QUOTE_PRECISION_U64;
let vd =
&mut VaultDepositor::new(Pubkey::default(), Pubkey::default(), Pubkey::default(), now);
vd.deposit(deposit_amount, vault_equity, &mut vault, &mut vp, now)
.unwrap(); // new user deposits $2000
vault_equity += deposit_amount;

let depositor_amount_before = depositor_shares_to_vault_amount(
vd.checked_vault_shares(&vault).unwrap(),
vault.total_shares,
vault_equity,
)
.unwrap();
assert_eq!(depositor_amount_before, vault_equity);

// vault up 10% (user +$200 gross, +$180 net)
now += 60 * 60 * 24; // 1 day later
vault_equity = 2200 * QUOTE_PRECISION_U64;

vd.apply_profit_share(vault_equity, &mut vault, &mut vp)
.unwrap();
vault.apply_fee(&mut vp, vault_equity, now).unwrap();

let depositor_amount_in_profit = depositor_shares_to_vault_amount(
vd.checked_vault_shares(&vault).unwrap(),
vault.total_shares,
vault_equity,
)
.unwrap();
assert_eq!(depositor_amount_in_profit, 2180 * QUOTE_PRECISION_U64);
assert_eq!(vd.cumulative_profit_share_amount, 180 * QUOTE_PRECISION_I64);
assert_eq!(vd.profit_share_fee_paid, 20 * QUOTE_PRECISION_U64);
assert_eq!(vault.total_shares, 2000 * QUOTE_PRECISION);
assert_eq!(vd.checked_vault_shares(&vault).unwrap(), 1981_818_182);

// vault drawdown 10%
now += 60 * 60 * 24; // 1 day later
vault_equity = 1980 * QUOTE_PRECISION_U64;

vd.apply_profit_share(vault_equity, &mut vault, &mut vp)
.unwrap();
vault.apply_fee(&mut vp, vault_equity, now).unwrap();

let depositor_amount_in_drawdown = depositor_shares_to_vault_amount(
vd.checked_vault_shares(&vault).unwrap(),
vault.total_shares,
vault_equity,
)
.unwrap();
assert_eq!(depositor_amount_in_drawdown, 1962 * QUOTE_PRECISION_U64);
assert_eq!(vd.cumulative_profit_share_amount, 180 * QUOTE_PRECISION_I64);
assert_eq!(vd.profit_share_fee_paid, 20 * QUOTE_PRECISION_U64);
assert_eq!(vault.total_shares, 2000 * QUOTE_PRECISION);
assert_eq!(vd.checked_vault_shares(&vault).unwrap(), 1981_818_182);

// in profit again (above net hwm (2180), below gross hwm (2200))
// vd equity = 2210*1982/2000 = 2190
now += 60 * 60 * 24; // 1 day later
vault_equity = 2210 * QUOTE_PRECISION_U64;

vd.apply_profit_share(vault_equity, &mut vault, &mut vp)
.unwrap();
vault.apply_fee(&mut vp, vault_equity, now).unwrap();

let depositor_amount_in_profit = depositor_shares_to_vault_amount(
vd.checked_vault_shares(&vault).unwrap(),
vault.total_shares,
vault_equity,
)
.unwrap();
assert_eq!(depositor_amount_in_profit, 2188_918_182);
assert_eq!(vd.cumulative_profit_share_amount, 188_918_182);
assert_eq!(vd.profit_share_fee_paid, 20_990_909);
assert_eq!(vault.total_shares, 2000 * QUOTE_PRECISION);
assert_eq!(vd.checked_vault_shares(&vault).unwrap(), 1980_921_432);
}

#[test]
fn apply_profit_share_on_net_hwm_example() {
let vault_str = String::from("0wjoKwKYdXdTdXBlcmNoYXJnZXIgVmF1bHQgICAgICAgICAgICAgIObOTURhcaZ/hexxlaSKNnYv57PHIZx9B8zN8k75zR8X5YskhtQuJRuc1ZuimumplgthmeBFASQW793js7pxldJCFqweTOnzcxGL+XKJx2Lif7339IdAC/KyKj8JEFZwkobFx9kGlk72vtua5uHjyHSzAViuG0/APV227Zmg8aZjmuxoym5eTWKAUNtoEdxsbA9c5IdRusnrLgc4WGs7FVYLef8oRnsVQX7JdnHagGAmJsU0t+iRQjIn8UdUnf8jwgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQjcnBxIEAAAAAAAAAAAAAMeNOt6kBAAAAAAAAAAAAAC5KoFnAAAAAAAAAAAAAAAAgDoJAAAAAADHSjWPHAAAAADgV+tIGwAAAAAAAAAAAADTz/tkAAAAAHycB+TTAgAAAEibJrr///8aotBlLSwAAJ4FyYFZKQAAAAAAAAAAAAAAuGTZRQAAAAAAAAAAAAAAEQ28vgkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ//V2YAAAAAAAAAAOCTBAAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=");
let mut vault_decoded_bytes = base64::decode(vault_str).unwrap();
let vault_bytes = vault_decoded_bytes.as_mut_slice();
let mut lamports = 0;
let key = Pubkey::default();
let owner = Pubkey::from_str("vAuLTsyrvSfZRuRB3XgvkPwNGgYSs9YRYymVebLKoxR").unwrap();
let vault_account_info =
create_account_info(&key, true, &mut lamports, vault_bytes, &owner);
let vault_loader: AccountLoader<Vault> =
AccountLoader::try_from(&vault_account_info).unwrap();

let vd_str = String::from("V222aldgP9Pmzk1EYXGmf4XscZWkijZ2L+ezxyGcfQfMzfJO+c0fF3+94atStThJMBa2OKTfUXJGJXPLv0FNp+KPmd6vhnmEDTcLJRUaWdAPxV7zBY0GiaKVHFu9h+8uxW0VlL9rvWTAzbMLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHJ+VZgAAAAAAAAAAAAAAAABJfw8AAAAAAEl/DwAAAAAAAAAAAAAAAKkKggIAAAAAypzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=");
let mut vd_decoded_bytes = base64::decode(vd_str).unwrap();
let vd_bytes = vd_decoded_bytes.as_mut_slice();
let mut lamports = 0;
let key = Pubkey::default();
let owner = Pubkey::from_str("vAuLTsyrvSfZRuRB3XgvkPwNGgYSs9YRYymVebLKoxR").unwrap();
let vd_account_info = create_account_info(&key, true, &mut lamports, vd_bytes, &owner);
let vd_loader: AccountLoader<VaultDepositor> =
AccountLoader::try_from(&vd_account_info).unwrap();

let mut vault = vault_loader.load_mut().unwrap();
let mut vd = vd_loader.load_mut().unwrap();
let mut vp = None;

let vault_equity = 8_045_367_880_000;
let vd_amount = depositor_shares_to_vault_amount(
vd.checked_vault_shares(&vault).unwrap(),
vault.total_shares,
vault_equity,
)
.unwrap();

let vd_hwm =
(vd.total_deposits - vd.total_withdraws) as i64 + vd.cumulative_profit_share_amount;
let unrealized_profit = vd_amount as i64 - vd_hwm;

assert_eq!(vd_amount, 309_346_825);
assert_eq!(vd_hwm, 302_076_841);
assert_eq!(unrealized_profit, 7_269_984);

let (manager_profit_share, protocol_profit_share) = vd
.apply_profit_share(vault_equity, &mut vault, &mut vp)
.unwrap();

assert_eq!(manager_profit_share, 2_180_995);
assert_eq!(protocol_profit_share, 0);

let vd_amount = depositor_shares_to_vault_amount(
vd.checked_vault_shares(&vault).unwrap(),
vault.total_shares,
vault_equity,
)
.unwrap();
let vd_hwm =
(vd.total_deposits - vd.total_withdraws) as i64 + vd.cumulative_profit_share_amount;
let unrealized_profit = vd_amount as i64 - vd_hwm;

assert_eq!(vd_amount, 307_165_832);
assert_eq!(vd_hwm, 307_165_830);
assert_eq!(unrealized_profit, 2);
}
}

#[cfg(test)]
Expand Down Expand Up @@ -1511,7 +1669,7 @@ mod vault_v1_fcn {
assert_eq!(cnt, 4); // 4 days
assert_eq!(
vd.cumulative_profit_share_amount,
(1000 * QUOTE_PRECISION_U64) as i64
(850 * QUOTE_PRECISION_U64) as i64 // $850 = $1000 - 15%
);
assert_eq!(vd.net_deposits, (2000 * QUOTE_PRECISION_U64) as i64);

Expand Down Expand Up @@ -1619,7 +1777,7 @@ mod vault_v1_fcn {
assert_eq!(cnt, 4); // 4 days
assert_eq!(
vd.cumulative_profit_share_amount,
(1000 * QUOTE_PRECISION_U64) as i64
(850 * QUOTE_PRECISION_U64) as i64 // $850 = $1000 - 15%
);
assert_eq!(vd.net_deposits, (2000 * QUOTE_PRECISION_U64) as i64);

Expand Down
Loading