Skip to content

Commit 0ca25a0

Browse files
authored
Moose review (#1948)
* better cap fees * add more constraints for user token accounts * use oracle map for updating aum * cargo tests pass and adding oracle map usage to derivative constituent in aum target as well
1 parent 290b7e9 commit 0ca25a0

File tree

5 files changed

+481
-119
lines changed

5 files changed

+481
-119
lines changed

programs/drift/src/instructions/lp_admin.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1079,7 +1079,6 @@ pub struct InitializeLpPool<'info> {
10791079
spot_market_index: u16,
10801080
)]
10811081
pub struct InitializeConstituent<'info> {
1082-
#[account()]
10831082
pub state: Box<Account<'info, State>>,
10841083
#[account(
10851084
mut,

programs/drift/src/instructions/lp_pool.rs

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};
33

44
use crate::ids::lp_pool_swap_wallet;
55
use crate::math::constants::PRICE_PRECISION_I64;
6-
use crate::math::oracle::OracleValidity;
76
use crate::state::events::{DepositDirection, LPBorrowLendDepositRecord};
87
use crate::state::paused_operations::ConstituentLpOperation;
98
use crate::validation::whitelist::validate_whitelist_token;
@@ -32,7 +31,6 @@ use crate::{
3231
update_constituent_target_base_for_derivatives, AmmConstituentDatum,
3332
AmmConstituentMappingFixed, Constituent, ConstituentCorrelationsFixed,
3433
ConstituentTargetBaseFixed, LPPool, TargetsDatum, LP_POOL_SWAP_AUM_UPDATE_DELAY,
35-
MAX_ORACLE_STALENESS_FOR_TARGET_CALC, MAX_STALENESS_FOR_TARGET_CALC,
3634
},
3735
oracle_map::OracleMap,
3836
perp_market_map::MarketSet,
@@ -45,7 +43,6 @@ use crate::{
4543
},
4644
validate,
4745
};
48-
use std::convert::TryFrom;
4946
use std::iter::Peekable;
5047
use std::slice::Iter;
5148

@@ -164,7 +161,7 @@ pub fn handle_update_lp_pool_aum<'c: 'info, 'info>(
164161
let AccountMaps {
165162
perp_market_map: _,
166163
spot_market_map,
167-
oracle_map: _,
164+
mut oracle_map,
168165
} = load_maps(
169166
remaining_accounts,
170167
&MarketSet::new(),
@@ -202,6 +199,7 @@ pub fn handle_update_lp_pool_aum<'c: 'info, 'info>(
202199
slot,
203200
&constituent_map,
204201
&spot_market_map,
202+
&mut oracle_map,
205203
&constituent_target_base,
206204
&amm_cache,
207205
)?;
@@ -227,6 +225,7 @@ pub fn handle_update_lp_pool_aum<'c: 'info, 'info>(
227225
&derivative_groups,
228226
&constituent_map,
229227
&spot_market_map,
228+
&mut oracle_map,
230229
&mut constituent_target_base,
231230
)?;
232231

@@ -1316,8 +1315,6 @@ pub fn handle_view_lp_pool_remove_liquidity_fees<'c: 'info, 'info>(
13161315
)?;
13171316
let out_oracle = out_oracle.clone();
13181317

1319-
// TODO: check self.aum validity
1320-
13211318
if !is_oracle_valid_for_action(out_oracle_validity, Some(DriftAction::LpPoolSwap))? {
13221319
msg!(
13231320
"Out oracle data for spot market {} is invalid for lp pool swap.",
@@ -1330,7 +1327,7 @@ pub fn handle_view_lp_pool_remove_liquidity_fees<'c: 'info, 'info>(
13301327
out_constituent.constituent_index,
13311328
&out_spot_market,
13321329
out_oracle.price,
1333-
lp_pool.last_aum, // TODO: remove out_amount * out_oracle to est post remove_liquidity aum
1330+
lp_pool.last_aum,
13341331
)?;
13351332

13361333
let dlp_total_supply = ctx.accounts.lp_mint.supply;
@@ -1410,7 +1407,6 @@ pub fn handle_deposit_to_program_vault<'c: 'info, 'info>(
14101407
let deposit_plus_token_amount_before = amount.safe_add(spot_market_vault.amount)?;
14111408

14121409
let oracle_data = oracle_map.get_price_data(&oracle_id)?;
1413-
let oracle_data_slot = clock.slot - oracle_data.delay.max(0i64).cast::<u64>()?;
14141410

14151411
controller::spot_balance::update_spot_market_cumulative_interest(
14161412
&mut spot_market,
@@ -1425,10 +1421,6 @@ pub fn handle_deposit_to_program_vault<'c: 'info, 'info>(
14251421
.cast::<i64>()?
14261422
.safe_sub(constituent.last_spot_balance_token_amount)?;
14271423

1428-
if constituent.last_oracle_slot < oracle_data_slot {
1429-
constituent.last_oracle_price = oracle_data.price;
1430-
constituent.last_oracle_slot = oracle_data_slot;
1431-
}
14321424
constituent.sync_token_balance(ctx.accounts.constituent_token_account.amount);
14331425
let balance_before = constituent.get_full_token_amount(&spot_market)?;
14341426

@@ -1559,11 +1551,6 @@ pub fn handle_withdraw_from_program_vault<'c: 'info, 'info>(
15591551
.cast::<i64>()?
15601552
.safe_sub(constituent.last_spot_balance_token_amount)?;
15611553

1562-
if constituent.last_oracle_slot < oracle_data_slot {
1563-
constituent.last_oracle_price = oracle_data.price;
1564-
constituent.last_oracle_slot = oracle_data_slot;
1565-
}
1566-
15671554
let mint = &Some(*ctx.accounts.mint.clone());
15681555
transfer_from_program_vault(
15691556
amount,
@@ -1865,12 +1852,12 @@ pub struct LPPoolSwap<'info> {
18651852

18661853
#[account(
18671854
mut,
1868-
constraint = user_in_token_account.mint.eq(&constituent_in_token_account.mint)
1855+
constraint = user_in_token_account.mint.eq(&constituent_in_token_account.mint) && user_in_token_account.owner == authority.key()
18691856
)]
18701857
pub user_in_token_account: Box<InterfaceAccount<'info, TokenAccount>>,
18711858
#[account(
18721859
mut,
1873-
constraint = user_out_token_account.mint.eq(&constituent_out_token_account.mint)
1860+
constraint = user_out_token_account.mint.eq(&constituent_out_token_account.mint) && user_out_token_account.owner == authority.key()
18741861
)]
18751862
pub user_out_token_account: Box<InterfaceAccount<'info, TokenAccount>>,
18761863

@@ -1900,7 +1887,6 @@ pub struct LPPoolSwap<'info> {
19001887

19011888
pub authority: Signer<'info>,
19021889

1903-
// TODO: in/out token program
19041890
pub token_program: Interface<'info, TokenInterface>,
19051891
}
19061892

@@ -1974,7 +1960,7 @@ pub struct LPPoolAddLiquidity<'info> {
19741960

19751961
#[account(
19761962
mut,
1977-
constraint = user_in_token_account.mint.eq(&constituent_in_token_account.mint)
1963+
constraint = user_in_token_account.mint.eq(&constituent_in_token_account.mint) && user_in_token_account.owner == authority.key()
19781964
)]
19791965
pub user_in_token_account: Box<InterfaceAccount<'info, TokenAccount>>,
19801966

@@ -2058,7 +2044,7 @@ pub struct LPPoolRemoveLiquidity<'info> {
20582044

20592045
#[account(
20602046
mut,
2061-
constraint = user_out_token_account.mint.eq(&constituent_out_token_account.mint)
2047+
constraint = user_out_token_account.mint.eq(&constituent_out_token_account.mint) && user_out_token_account.owner == authority.key()
20622048
)]
20632049
pub user_out_token_account: Box<InterfaceAccount<'info, TokenAccount>>,
20642050
#[account(

programs/drift/src/state/lp_pool.rs

Lines changed: 90 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
use std::collections::BTreeMap;
2+
use std::f32::consts::E;
23

34
use crate::error::{DriftResult, ErrorCode};
45
use crate::math::casting::Cast;
56
use crate::math::constants::{
67
BASE_PRECISION_I128, PERCENTAGE_PRECISION, PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_I64,
78
PERCENTAGE_PRECISION_U64, PRICE_PRECISION, QUOTE_PRECISION_I128, QUOTE_PRECISION_U64,
89
};
10+
use crate::math::oracle::{is_oracle_valid_for_action, DriftAction};
911
use crate::math::safe_math::SafeMath;
1012
use crate::math::safe_unwrap::SafeUnwrap;
1113
use crate::math::spot_balance::{get_signed_token_amount, get_token_amount};
1214
use crate::state::amm_cache::{AmmCacheFixed, CacheInfo};
1315
use crate::state::constituent_map::ConstituentMap;
16+
use crate::state::oracle_map::OracleMap;
1417
use crate::state::paused_operations::ConstituentLpOperation;
1518
use crate::state::spot_market_map::SpotMarketMap;
19+
use crate::state::user::MarketType;
1620
use anchor_lang::prelude::*;
1721
use borsh::{BorshDeserialize, BorshSerialize};
1822
use enumflags2::BitFlags;
@@ -32,9 +36,8 @@ pub const CONSTITUENT_CORRELATIONS_PDA_SEED: &str = "constituent_correlations";
3236
pub const CONSTITUENT_VAULT_PDA_SEED: &str = "CONSTITUENT_VAULT";
3337
pub const LP_POOL_TOKEN_VAULT_PDA_SEED: &str = "LP_POOL_TOKEN_VAULT";
3438

35-
pub const BASE_SWAP_FEE: i128 = 300; // 0.75% in PERCENTAGE_PRECISION
36-
pub const MAX_SWAP_FEE: i128 = 75_000; // 0.75% in PERCENTAGE_PRECISION
37-
pub const MIN_SWAP_FEE: i128 = 200; // 0.75% in PERCENTAGE_PRECISION
39+
pub const BASE_SWAP_FEE: i128 = 300; // 0.3% in PERCENTAGE_PRECISION
40+
pub const MAX_SWAP_FEE: i128 = 37_500; // 37.5% in PERCENTAGE_PRECISION
3841

3942
pub const MIN_AUM_EXECUTION_FEE: u128 = 10_000_000_000_000;
4043

@@ -225,6 +228,9 @@ impl LPPool {
225228
out_target_oracle_slot_delay,
226229
)?;
227230

231+
in_fee = in_fee.min(MAX_SWAP_FEE);
232+
out_fee = out_fee.min(MAX_SWAP_FEE);
233+
228234
let in_fee_amount = in_amount
229235
.cast::<i128>()?
230236
.safe_mul(in_fee)?
@@ -279,6 +285,7 @@ impl LPPool {
279285
in_target_position_slot_delay,
280286
in_target_oracle_slot_delay,
281287
)?;
288+
in_fee_pct = in_fee_pct.min(MAX_SWAP_FEE * 2);
282289

283290
let in_fee_amount = in_amount
284291
.cast::<i128>()?
@@ -383,6 +390,8 @@ impl LPPool {
383390
out_target_oracle_slot_delay,
384391
)?;
385392
out_fee_pct = in_fee_pct.safe_add(out_fee_pct)?;
393+
out_fee_pct = out_fee_pct.min(MAX_SWAP_FEE * 2);
394+
386395
let out_fee_amount = out_amount
387396
.cast::<i128>()?
388397
.safe_mul(out_fee_pct)?
@@ -629,10 +638,7 @@ impl LPPool {
629638
.safe_add(out_quadratic_inventory_fee)?
630639
.safe_add(BASE_SWAP_FEE.safe_div(2)?)?;
631640

632-
Ok((
633-
total_in_fee.min(MAX_SWAP_FEE.safe_div(2)?),
634-
total_out_fee.min(MAX_SWAP_FEE.safe_div(2)?),
635-
))
641+
Ok((total_in_fee, total_out_fee))
636642
}
637643

638644
pub fn get_target_uncertainty_fees(
@@ -686,6 +692,7 @@ impl LPPool {
686692
slot: u64,
687693
constituent_map: &ConstituentMap,
688694
spot_market_map: &SpotMarketMap,
695+
oracle_map: &mut OracleMap,
689696
constituent_target_base: &AccountZeroCopyMut<'_, TargetsDatum, ConstituentTargetBaseFixed>,
690697
amm_cache: &AccountZeroCopyMut<'_, CacheInfo, AmmCacheFixed>,
691698
) -> DriftResult<(u128, i128, BTreeMap<u16, Vec<u16>>)> {
@@ -723,10 +730,28 @@ impl LPPool {
723730
}
724731

725732
let spot_market = spot_market_map.get_ref(&constituent.spot_market_index)?;
733+
let oracle_and_validity = oracle_map.get_price_data_and_validity(
734+
MarketType::Spot,
735+
constituent.spot_market_index,
736+
&spot_market.oracle_id(),
737+
spot_market.historical_oracle_data.last_oracle_price_twap,
738+
spot_market.get_max_confidence_interval_multiplier()?,
739+
0,
740+
)?;
741+
if !is_oracle_valid_for_action(
742+
oracle_and_validity.1,
743+
Some(DriftAction::UpdateLpPoolAum),
744+
)? {
745+
msg!(
746+
"Constituent {} oracle is not valid for action",
747+
constituent.constituent_index
748+
);
749+
return Err(ErrorCode::InvalidOracle.into());
750+
}
726751

727752
let constituent_aum = constituent
728753
.get_full_token_amount(&spot_market)?
729-
.safe_mul(constituent.last_oracle_price as i128)?
754+
.safe_mul(oracle_and_validity.0.price as i128)?
730755
.safe_div(10_i128.pow(spot_market.decimals))?;
731756
msg!(
732757
"constituent: {}, balance: {}, aum: {}, deriv index: {}, bl token balance {}, bl balance type {}, vault balance: {}",
@@ -747,7 +772,7 @@ impl LPPool {
747772
.get(constituent.constituent_index as u32)
748773
.target_base
749774
.cast::<i128>()?
750-
.safe_mul(constituent.last_oracle_price.cast::<i128>()?)?
775+
.safe_mul(oracle_and_validity.0.price.cast::<i128>()?)?
751776
.safe_div(10_i128.pow(constituent.decimals as u32))?
752777
.cast::<i64>()?;
753778
crypto_delta = crypto_delta.safe_add(constituent_target_notional.cast()?)?;
@@ -1579,8 +1604,8 @@ impl ConstituentCorrelations {
15791604
"ConstituentCorrelation correlations must be between 0 and PERCENTAGE_PRECISION"
15801605
)?;
15811606

1582-
self.correlations[(i as usize * num_constituents + j as usize)] = corr;
1583-
self.correlations[(j as usize * num_constituents + i as usize)] = corr;
1607+
self.correlations[i as usize * num_constituents + j as usize] = corr;
1608+
self.correlations[j as usize * num_constituents + i as usize] = corr;
15841609

15851610
self.validate()?;
15861611

@@ -1662,34 +1687,81 @@ pub fn update_constituent_target_base_for_derivatives(
16621687
derivative_groups: &BTreeMap<u16, Vec<u16>>,
16631688
constituent_map: &ConstituentMap,
16641689
spot_market_map: &SpotMarketMap,
1690+
oracle_map: &mut OracleMap,
16651691
constituent_target_base: &mut AccountZeroCopyMut<'_, TargetsDatum, ConstituentTargetBaseFixed>,
16661692
) -> DriftResult<()> {
16671693
for (parent_index, constituent_indexes) in derivative_groups.iter() {
16681694
let parent_constituent = constituent_map.get_ref(parent_index)?;
1695+
1696+
let parent_spot_market = spot_market_map.get_ref(&parent_constituent.spot_market_index)?;
1697+
let parent_oracle_price_and_validity = oracle_map.get_price_data_and_validity(
1698+
MarketType::Spot,
1699+
parent_spot_market.market_index,
1700+
&parent_spot_market.oracle_id(),
1701+
parent_spot_market
1702+
.historical_oracle_data
1703+
.last_oracle_price_twap,
1704+
parent_spot_market.get_max_confidence_interval_multiplier()?,
1705+
0,
1706+
)?;
1707+
if !is_oracle_valid_for_action(
1708+
parent_oracle_price_and_validity.1,
1709+
Some(DriftAction::UpdateLpPoolAum),
1710+
)? {
1711+
msg!(
1712+
"Parent constituent {} oracle is invalid",
1713+
parent_constituent.constituent_index
1714+
);
1715+
return Err(ErrorCode::InvalidOracle);
1716+
}
1717+
let parent_constituent_price = parent_oracle_price_and_validity.0.price;
1718+
16691719
let parent_target_base = constituent_target_base
16701720
.get(*parent_index as u32)
16711721
.target_base;
16721722
let target_parent_weight = calculate_target_weight(
16731723
parent_target_base,
16741724
&*spot_market_map.get_ref(&parent_constituent.spot_market_index)?,
1675-
parent_constituent.last_oracle_price,
1725+
parent_oracle_price_and_validity.0.price,
16761726
aum,
16771727
)?;
16781728
let mut derivative_weights_sum: u64 = 0;
16791729
for constituent_index in constituent_indexes {
16801730
let constituent = constituent_map.get_ref(constituent_index)?;
1681-
if constituent.last_oracle_price
1682-
< parent_constituent
1683-
.last_oracle_price
1731+
let constituent_spot_market =
1732+
spot_market_map.get_ref(&constituent.spot_market_index)?;
1733+
let constituent_oracle_price_and_validity = oracle_map.get_price_data_and_validity(
1734+
MarketType::Spot,
1735+
constituent.spot_market_index,
1736+
&constituent_spot_market.oracle_id(),
1737+
constituent_spot_market
1738+
.historical_oracle_data
1739+
.last_oracle_price_twap,
1740+
constituent_spot_market.get_max_confidence_interval_multiplier()?,
1741+
0,
1742+
)?;
1743+
if !is_oracle_valid_for_action(
1744+
constituent_oracle_price_and_validity.1,
1745+
Some(DriftAction::UpdateLpPoolAum),
1746+
)? {
1747+
msg!(
1748+
"Constituent {} oracle is invalid",
1749+
constituent.constituent_index
1750+
);
1751+
return Err(ErrorCode::InvalidOracle);
1752+
}
1753+
1754+
if constituent_oracle_price_and_validity.0.price
1755+
< parent_constituent_price
16841756
.safe_mul(constituent.constituent_derivative_depeg_threshold as i64)?
16851757
.safe_div(PERCENTAGE_PRECISION_I64)?
16861758
{
16871759
msg!(
16881760
"Constituent {} last oracle price {} is too low compared to parent constituent {} last oracle price {}. Assuming depegging and setting target base to 0.",
16891761
constituent.constituent_index,
1690-
constituent.last_oracle_price,
1762+
constituent_oracle_price_and_validity.0.price,
16911763
parent_constituent.constituent_index,
1692-
parent_constituent.last_oracle_price
1764+
parent_constituent_price
16931765
);
16941766
constituent_target_base
16951767
.get_mut(*constituent_index as u32)
@@ -1714,7 +1786,7 @@ pub fn update_constituent_target_base_for_derivatives(
17141786
.safe_mul(target_weight)?
17151787
.safe_div(PERCENTAGE_PRECISION_I128)?
17161788
.safe_mul(10_i128.pow(constituent.decimals as u32))?
1717-
.safe_div(constituent.last_oracle_price as i128)?;
1789+
.safe_div(constituent_oracle_price_and_validity.0.price as i128)?;
17181790

17191791
msg!(
17201792
"constituent: {}, target base: {}",

0 commit comments

Comments
 (0)