diff --git a/programs/drift/src/error.rs b/programs/drift/src/error.rs index 33947c5df8..3c644b074a 100644 --- a/programs/drift/src/error.rs +++ b/programs/drift/src/error.rs @@ -642,7 +642,7 @@ pub enum ErrorCode { #[msg("Invalid Amm Constituent Mapping argument")] InvalidAmmConstituentMappingArgument, #[msg("Invalid update constituent update target weights argument")] - InvalidUpdateConstituentTargetWeightsArgument, + InvalidUpdateConstituentTargetBaseArgument, #[msg("Constituent not found")] ConstituentNotFound, #[msg("Constituent could not load")] diff --git a/programs/drift/src/instructions/admin.rs b/programs/drift/src/instructions/admin.rs index 4b02054afa..31b4ac87e9 100644 --- a/programs/drift/src/instructions/admin.rs +++ b/programs/drift/src/instructions/admin.rs @@ -3,8 +3,8 @@ use std::mem::size_of; use crate::msg; use crate::state::lp_pool::{ - AmmConstituentDatum, AmmConstituentMapping, Constituent, ConstituentTargetWeights, LPPool, - WeightDatum, AMM_MAP_PDA_SEED, CONSTITUENT_PDA_SEED, CONSTITUENT_TARGET_WEIGHT_PDA_SEED, + AmmConstituentDatum, AmmConstituentMapping, Constituent, ConstituentTargetBase, LPPool, + TargetsDatum, AMM_MAP_PDA_SEED, CONSTITUENT_PDA_SEED, CONSTITUENT_TARGET_WEIGHT_PDA_SEED, CONSTITUENT_VAULT_PDA_SEED, }; use anchor_lang::prelude::*; @@ -4517,12 +4517,12 @@ pub fn handle_initialize_lp_pool( .resize_with(0 as usize, AmmConstituentDatum::default); amm_constituent_mapping.validate()?; - let constituent_target_weights = &mut ctx.accounts.constituent_target_weights; - constituent_target_weights.bump = ctx.bumps.constituent_target_weights; - constituent_target_weights - .weights - .resize_with(0 as usize, WeightDatum::default); - constituent_target_weights.validate()?; + let constituent_target_base = &mut ctx.accounts.constituent_target_base; + constituent_target_base.bump = ctx.bumps.constituent_target_base; + constituent_target_base + .targets + .resize_with(0 as usize, TargetsDatum::default); + constituent_target_base.validate()?; Ok(()) } @@ -4734,13 +4734,13 @@ pub fn handle_initialize_constituent<'info>( let mut constituent = ctx.accounts.constituent.load_init()?; let mut lp_pool = ctx.accounts.lp_pool.load_mut()?; - let constituent_target_weights = &mut ctx.accounts.constituent_target_weights; - let current_len = constituent_target_weights.weights.len(); + let constituent_target_base = &mut ctx.accounts.constituent_target_base; + let current_len = constituent_target_base.targets.len(); - constituent_target_weights - .weights - .resize_with((current_len + 1) as usize, WeightDatum::default); - constituent_target_weights.validate()?; + constituent_target_base + .targets + .resize_with((current_len + 1) as usize, TargetsDatum::default); + constituent_target_base.validate()?; msg!( "initializing constituent {} with spot market index {}", @@ -4759,7 +4759,7 @@ pub fn handle_initialize_constituent<'info>( constituent.mint = ctx.accounts.spot_market_mint.key(); constituent.bump = ctx.bumps.constituent; constituent.lp_pool = lp_pool.pubkey; - constituent.constituent_index = (constituent_target_weights.weights.len() - 1) as u16; + constituent.constituent_index = (constituent_target_base.targets.len() - 1) as u16; lp_pool.constituents += 1; Ok(()) @@ -4890,7 +4890,7 @@ pub fn handle_add_amm_constituent_data<'info>( init_amm_constituent_mapping_data: Vec, ) -> Result<()> { let amm_mapping = &mut ctx.accounts.amm_constituent_mapping; - let constituent_target_weights = &ctx.accounts.constituent_target_weights; + let constituent_target_base = &ctx.accounts.constituent_target_base; let state = &ctx.accounts.state; let mut current_len = amm_mapping.weights.len(); @@ -4904,7 +4904,7 @@ pub fn handle_add_amm_constituent_data<'info>( )?; validate!( - (init_datum.constituent_index as usize) < constituent_target_weights.weights.len(), + (init_datum.constituent_index as usize) < constituent_target_base.targets.len(), ErrorCode::InvalidAmmConstituentMappingArgument, "constituent_index too large compared to number of constituents in target weights" )?; @@ -5796,10 +5796,10 @@ pub struct InitializeLpPool<'info> { init, seeds = [CONSTITUENT_TARGET_WEIGHT_PDA_SEED.as_ref(), lp_pool.key().as_ref()], bump, - space = ConstituentTargetWeights::space(0 as usize), + space = ConstituentTargetBase::space(0 as usize), payer = admin, )] - pub constituent_target_weights: Box>, + pub constituent_target_base: Box>, #[account( has_one = admin @@ -5838,12 +5838,12 @@ pub struct InitializeConstituent<'info> { #[account( mut, seeds = [CONSTITUENT_TARGET_WEIGHT_PDA_SEED.as_ref(), lp_pool.key().as_ref()], - bump = constituent_target_weights.bump, - realloc = ConstituentTargetWeights::space(constituent_target_weights.weights.len() + 1 as usize), + bump = constituent_target_base.bump, + realloc = ConstituentTargetBase::space(constituent_target_base.targets.len() + 1 as usize), realloc::payer = admin, realloc::zero = false, )] - pub constituent_target_weights: Box>, + pub constituent_target_base: Box>, #[account( init, @@ -5923,11 +5923,11 @@ pub struct AddAmmConstituentMappingData<'info> { mut, seeds = [CONSTITUENT_TARGET_WEIGHT_PDA_SEED.as_ref(), lp_pool.key().as_ref()], bump, - realloc = ConstituentTargetWeights::space(constituent_target_weights.weights.len() + 1 as usize), + realloc = ConstituentTargetBase::space(constituent_target_base.targets.len() + 1 as usize), realloc::payer = admin, realloc::zero = false, )] - pub constituent_target_weights: Box>, + pub constituent_target_base: Box>, pub state: Box>, pub system_program: Program<'info, System>, } diff --git a/programs/drift/src/instructions/lp_pool.rs b/programs/drift/src/instructions/lp_pool.rs index 725344d2e3..34dbbcb03a 100644 --- a/programs/drift/src/instructions/lp_pool.rs +++ b/programs/drift/src/instructions/lp_pool.rs @@ -17,7 +17,7 @@ use crate::{ events::{LPMintRedeemRecord, LPSwapRecord}, lp_pool::{ AmmConstituentDatum, AmmConstituentMappingFixed, Constituent, - ConstituentTargetWeightsFixed, LPPool, WeightDatum, WeightValidationFlags, + ConstituentTargetBaseFixed, LPPool, TargetsDatum, WeightValidationFlags, }, oracle::OraclePriceData, perp_market::{AmmCacheFixed, CacheInfo, AMM_POSITIONS_CACHE}, @@ -41,8 +41,8 @@ use crate::state::lp_pool::{ LP_POOL_TOKEN_VAULT_PDA_SEED, }; -pub fn handle_update_constituent_target_weights<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, UpdateConstituentTargetWeights<'info>>, +pub fn handle_update_constituent_target_base<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, UpdateConstituentTargetBase<'info>>, lp_pool_name: [u8; 32], constituent_indexes: Vec, ) -> Result<()> { @@ -68,7 +68,7 @@ pub fn handle_update_constituent_target_weights<'c: 'info, 'info>( )?; let state = &ctx.accounts.state; - let constituent_target_weights_key = &ctx.accounts.constituent_target_weights.key(); + let constituent_target_base_key = &ctx.accounts.constituent_target_base.key(); let amm_mapping_key = &ctx.accounts.amm_constituent_mapping.key(); // Validate lp pool pda @@ -87,13 +87,13 @@ pub fn handle_update_constituent_target_weights<'c: 'info, 'info>( "Lp pool PDA does not match expected PDA" )?; - let mut constituent_target_weights: AccountZeroCopyMut< + let mut constituent_target_base: AccountZeroCopyMut< '_, - WeightDatum, - ConstituentTargetWeightsFixed, - > = ctx.accounts.constituent_target_weights.load_zc_mut()?; + TargetsDatum, + ConstituentTargetBaseFixed, + > = ctx.accounts.constituent_target_base.load_zc_mut()?; - let bump = constituent_target_weights.fixed.bump; + let bump = constituent_target_base.fixed.bump; let expected_pda = &Pubkey::create_program_address( &[ CONSTITUENT_TARGET_WEIGHT_PDA_SEED.as_ref(), @@ -104,13 +104,13 @@ pub fn handle_update_constituent_target_weights<'c: 'info, 'info>( ) .map_err(|_| ErrorCode::InvalidPDA)?; validate!( - expected_pda.eq(constituent_target_weights_key), + expected_pda.eq(constituent_target_base_key), ErrorCode::InvalidPDA, "Constituent target weights PDA does not match expected PDA" )?; - let num_constituents = constituent_target_weights.len(); - for datum in constituent_target_weights.iter() { + let num_constituents = constituent_target_base.len(); + for datum in constituent_target_base.iter() { msg!("weight datum: {:?}", datum); } @@ -120,7 +120,7 @@ pub fn handle_update_constituent_target_weights<'c: 'info, 'info>( validate!( !exists_invalid_constituent_index, - ErrorCode::InvalidUpdateConstituentTargetWeightsArgument, + ErrorCode::InvalidUpdateConstituentTargetBaseArgument, "Constituent index larger than number of constituent target weights" )?; @@ -171,7 +171,7 @@ pub fn handle_update_constituent_target_weights<'c: 'info, 'info>( if !is_oracle_valid_for_action( oracle_validity, - Some(DriftAction::UpdateLpConstituentTargetWeights), + Some(DriftAction::UpdateLpConstituentTargetBase), )? { msg!("Oracle data for perp market {} and constituent index {} is invalid. Skipping update", datum.perp_market_index, datum.constituent_index); @@ -187,14 +187,11 @@ pub fn handle_update_constituent_target_weights<'c: 'info, 'info>( return Ok(()); } - constituent_target_weights.update_target_weights( + constituent_target_base.update_target_base( &amm_constituent_mapping, amm_inventories.as_slice(), constituent_indexes.as_slice(), - &oracle_prices.as_slice(), - lp_pool.last_aum, slot, - WeightValidationFlags::NONE, )?; Ok(()) @@ -324,23 +321,23 @@ pub fn handle_lp_pool_swap<'c: 'info, 'info>( let mut in_constituent = ctx.accounts.in_constituent.load_mut()?; let mut out_constituent = ctx.accounts.out_constituent.load_mut()?; - let constituent_target_weights_key = &ctx.accounts.constituent_target_weights.key(); - let constituent_target_weights: AccountZeroCopy< + let constituent_target_base_key = &ctx.accounts.constituent_target_base.key(); + let constituent_target_base: AccountZeroCopy< '_, - WeightDatum, - ConstituentTargetWeightsFixed, - > = ctx.accounts.constituent_target_weights.load_zc()?; + TargetsDatum, + ConstituentTargetBaseFixed, + > = ctx.accounts.constituent_target_base.load_zc()?; let expected_pda = &Pubkey::create_program_address( &[ CONSTITUENT_TARGET_WEIGHT_PDA_SEED.as_ref(), lp_pool.pubkey.as_ref(), - constituent_target_weights.fixed.bump.to_le_bytes().as_ref(), + constituent_target_base.fixed.bump.to_le_bytes().as_ref(), ], &crate::ID, ) .map_err(|_| ErrorCode::InvalidPDA)?; validate!( - expected_pda.eq(constituent_target_weights_key), + expected_pda.eq(constituent_target_base_key), ErrorCode::InvalidPDA, "Constituent target weights PDA does not match expected PDA" )?; @@ -401,10 +398,18 @@ pub fn handle_lp_pool_swap<'c: 'info, 'info>( update_spot_market_cumulative_interest(&mut in_spot_market, Some(&in_oracle), now)?; update_spot_market_cumulative_interest(&mut out_spot_market, Some(&out_oracle), now)?; - let in_target_weight = - constituent_target_weights.get_target_weight(in_constituent.constituent_index)?; - let out_target_weight = - constituent_target_weights.get_target_weight(out_constituent.constituent_index)?; + let in_target_weight = constituent_target_base.get_target_weight( + in_constituent.constituent_index, + &in_spot_market, + in_oracle.price, + lp_pool.last_aum, + )?; + let out_target_weight = constituent_target_base.get_target_weight( + out_constituent.constituent_index, + &out_spot_market, + out_oracle.price, + lp_pool.last_aum, + )?; let (in_amount, out_amount, in_fee, out_fee) = lp_pool.get_swap_amount( &in_oracle, @@ -514,7 +519,7 @@ pub fn handle_lp_pool_add_liquidity<'c: 'info, 'info>( let mut in_constituent = ctx.accounts.in_constituent.load_mut()?; - let constituent_target_weights = ctx.accounts.constituent_target_weights.load_zc()?; + let constituent_target_base = ctx.accounts.constituent_target_base.load_zc()?; let AccountMaps { perp_market_map: _, @@ -553,8 +558,12 @@ pub fn handle_lp_pool_add_liquidity<'c: 'info, 'info>( update_spot_market_cumulative_interest(&mut in_spot_market, Some(&in_oracle), now)?; - let in_target_weight = - constituent_target_weights.get_target_weight(in_constituent.constituent_index)?; + let in_target_weight = constituent_target_base.get_target_weight( + in_constituent.constituent_index, + &in_spot_market, + in_oracle.price, + lp_pool.last_aum, // TODO: add in_amount * in_oracle to est post add_liquidity aum + )?; let dlp_total_supply = ctx.accounts.lp_mint.supply; @@ -682,7 +691,7 @@ pub fn handle_lp_pool_remove_liquidity<'c: 'info, 'info>( let mut out_constituent = ctx.accounts.out_constituent.load_mut()?; - let constituent_target_weights = ctx.accounts.constituent_target_weights.load_zc()?; + let constituent_target_base = ctx.accounts.constituent_target_base.load_zc()?; let AccountMaps { perp_market_map: _, @@ -723,8 +732,12 @@ pub fn handle_lp_pool_remove_liquidity<'c: 'info, 'info>( update_spot_market_cumulative_interest(&mut out_spot_market, Some(&out_oracle), now)?; - let out_target_weight = - constituent_target_weights.get_target_weight(out_constituent.constituent_index)?; + let out_target_weight = constituent_target_base.get_target_weight( + out_constituent.constituent_index, + &out_spot_market, + out_oracle.price, + lp_pool.last_aum, // TODO: remove out_amount * out_oracle to est post remove_liquidity aum + )?; let dlp_total_supply = ctx.accounts.lp_mint.supply; @@ -843,14 +856,14 @@ pub fn handle_lp_pool_remove_liquidity<'c: 'info, 'info>( #[instruction( lp_pool_name: [u8; 32], )] -pub struct UpdateConstituentTargetWeights<'info> { +pub struct UpdateConstituentTargetBase<'info> { pub state: Box>, #[account(mut)] pub keeper: Signer<'info>, /// CHECK: checked in AmmConstituentMappingZeroCopy checks pub amm_constituent_mapping: AccountInfo<'info>, - /// CHECK: checked in ConstituentTargetWeightsZeroCopy checks - pub constituent_target_weights: AccountInfo<'info>, + /// CHECK: checked in ConstituentTargetBaseZeroCopy checks + pub constituent_target_base: AccountInfo<'info>, /// CHECK: checked in AmmCacheZeroCopy checks pub amm_cache: AccountInfo<'info>, #[account( @@ -892,8 +905,8 @@ pub struct LPPoolSwap<'info> { seeds = [CONSTITUENT_TARGET_WEIGHT_PDA_SEED.as_ref(), lp_pool.key().as_ref()], bump, )] - /// CHECK: checked in ConstituentTargetWeightsZeroCopy checks - pub constituent_target_weights: AccountInfo<'info>, + /// CHECK: checked in ConstituentTargetBaseZeroCopy checks + pub constituent_target_base: AccountInfo<'info>, #[account(mut)] pub constituent_in_token_account: Box>, @@ -991,8 +1004,8 @@ pub struct LPPoolAddLiquidity<'info> { seeds = [CONSTITUENT_TARGET_WEIGHT_PDA_SEED.as_ref(), lp_pool.key().as_ref()], bump, )] - /// CHECK: checked in ConstituentTargetWeightsZeroCopy checks - pub constituent_target_weights: AccountInfo<'info>, + /// CHECK: checked in ConstituentTargetBaseZeroCopy checks + pub constituent_target_base: AccountInfo<'info>, #[account( mut, @@ -1052,8 +1065,8 @@ pub struct LPPoolRemoveLiquidity<'info> { seeds = [CONSTITUENT_TARGET_WEIGHT_PDA_SEED.as_ref(), lp_pool.key().as_ref()], bump, )] - /// CHECK: checked in ConstituentTargetWeightsZeroCopy checks - pub constituent_target_weights: AccountInfo<'info>, + /// CHECK: checked in ConstituentTargetBaseZeroCopy checks + pub constituent_target_base: AccountInfo<'info>, #[account( mut, diff --git a/programs/drift/src/lib.rs b/programs/drift/src/lib.rs index 6785ceeb35..0fe28bae99 100644 --- a/programs/drift/src/lib.rs +++ b/programs/drift/src/lib.rs @@ -1790,12 +1790,12 @@ pub mod drift { handle_remove_amm_constituent_mapping_data(ctx, perp_market_index, constituent_index) } - pub fn update_lp_constituent_target_weights<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, UpdateConstituentTargetWeights<'info>>, + pub fn update_lp_constituent_target_base<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, UpdateConstituentTargetBase<'info>>, lp_pool_name: [u8; 32], constituent_indexes: Vec, ) -> Result<()> { - handle_update_constituent_target_weights(ctx, lp_pool_name, constituent_indexes) + handle_update_constituent_target_base(ctx, lp_pool_name, constituent_indexes) } pub fn update_lp_pool_aum<'c: 'info, 'info>( diff --git a/programs/drift/src/math/oracle.rs b/programs/drift/src/math/oracle.rs index 09d18d817c..fcd5eae35b 100644 --- a/programs/drift/src/math/oracle.rs +++ b/programs/drift/src/math/oracle.rs @@ -69,7 +69,7 @@ pub enum DriftAction { UpdateTwap, UpdateAMMCurve, OracleOrderPrice, - UpdateLpConstituentTargetWeights, + UpdateLpConstituentTargetBase, UpdateLpPoolAum, LpPoolSwap, } @@ -131,7 +131,7 @@ pub fn is_oracle_valid_for_action( ), DriftAction::UpdateTwap => !matches!(oracle_validity, OracleValidity::NonPositive), DriftAction::UpdateAMMCurve => !matches!(oracle_validity, OracleValidity::NonPositive), - DriftAction::UpdateLpConstituentTargetWeights | DriftAction::UpdateLpPoolAum => { + DriftAction::UpdateLpConstituentTargetBase | DriftAction::UpdateLpPoolAum => { !matches!(oracle_validity, OracleValidity::NonPositive) } DriftAction::LpPoolSwap => !matches!( diff --git a/programs/drift/src/state/lp_pool.rs b/programs/drift/src/state/lp_pool.rs index b43cc04619..e817090c9e 100644 --- a/programs/drift/src/state/lp_pool.rs +++ b/programs/drift/src/state/lp_pool.rs @@ -10,7 +10,8 @@ use anchor_spl::token::Mint; use borsh::{BorshDeserialize, BorshSerialize}; use super::oracle::OraclePriceData; -use super::spot_market::SpotMarket; +use super::oracle_map::OracleMap; +use super::spot_market::{self, SpotMarket}; use super::zero_copy::{AccountZeroCopy, AccountZeroCopyMut, HasLen}; use crate::state::spot_market::{SpotBalance, SpotBalanceType}; use crate::state::traits::Size; @@ -18,7 +19,7 @@ use crate::{impl_zero_copy_loader, validate}; pub const AMM_MAP_PDA_SEED: &str = "AMM_MAP"; pub const CONSTITUENT_PDA_SEED: &str = "CONSTITUENT"; -pub const CONSTITUENT_TARGET_WEIGHT_PDA_SEED: &str = "CONSTITUENT_TARGET_WEIGHTS"; +pub const CONSTITUENT_TARGET_WEIGHT_PDA_SEED: &str = "constituent_target_base"; pub const CONSTITUENT_VAULT_PDA_SEED: &str = "CONSTITUENT_VAULT"; pub const LP_POOL_TOKEN_VAULT_PDA_SEED: &str = "LP_POOL_TOKEN_VAULT"; @@ -623,17 +624,28 @@ pub struct WeightDatum { pub weight: i64, } + +#[zero_copy] +#[derive(Debug, Default, BorshDeserialize, BorshSerialize)] +#[repr(C)] +pub struct TargetsDatum { + pub last_slot: u64, + pub target_base: i64, + // pub correlation_scalar: i32, + // pub cost_to_trade: i32, +} + #[zero_copy] #[derive(Debug, Default)] #[repr(C)] -pub struct ConstituentTargetWeightsFixed { +pub struct ConstituentTargetBaseFixed { pub bump: u8, _pad: [u8; 3], /// total elements in the flattened `data` vec pub len: u32, } -impl HasLen for ConstituentTargetWeightsFixed { +impl HasLen for ConstituentTargetBaseFixed { fn len(&self) -> u32 { self.len } @@ -642,21 +654,21 @@ impl HasLen for ConstituentTargetWeightsFixed { #[account] #[derive(Debug)] #[repr(C)] -pub struct ConstituentTargetWeights { +pub struct ConstituentTargetBase { pub bump: u8, _padding: [u8; 3], // PERCENTAGE_PRECISION. The weights of the target weight matrix. Updated async - pub weights: Vec, + pub targets: Vec, } -impl ConstituentTargetWeights { +impl ConstituentTargetBase { pub fn space(num_constituents: usize) -> usize { 8 + 8 + 4 + num_constituents * 16 } pub fn validate(&self) -> DriftResult<()> { validate!( - self.weights.len() <= 128, + self.targets.len() <= 128, ErrorCode::DefaultError, "Number of constituents len must be between 1 and 128" )?; @@ -665,18 +677,18 @@ impl ConstituentTargetWeights { } impl_zero_copy_loader!( - ConstituentTargetWeights, + ConstituentTargetBase, crate::id, - ConstituentTargetWeightsFixed, - WeightDatum + ConstituentTargetBaseFixed, + TargetsDatum ); -impl Default for ConstituentTargetWeights { +impl Default for ConstituentTargetBase { fn default() -> Self { - ConstituentTargetWeights { + ConstituentTargetBase { bump: 0, _padding: [0; 3], - weights: Vec::with_capacity(0), + targets: Vec::with_capacity(0), } } } @@ -689,92 +701,118 @@ pub enum WeightValidationFlags { NoOverweight = 0b0000_0100, } -impl<'a> AccountZeroCopy<'a, WeightDatum, ConstituentTargetWeightsFixed> { - pub fn get_target_weight(&self, constituent_index: u16) -> DriftResult { +impl<'a> AccountZeroCopy<'a, TargetsDatum, ConstituentTargetBaseFixed> { + pub fn get_target_weight( + &self, + constituent_index: u16, + spot_market: &SpotMarket, + price: i64, + aum: u128, + ) -> DriftResult { validate!( constituent_index < self.len() as u16, ErrorCode::InvalidConstituent, - "Invalid constituent_index = {}, ConstituentTargetWeights len = {}", + "Invalid constituent_index = {}, ConstituentTargetBase len = {}", constituent_index, self.len() )?; + // TODO: validate spot market let datum = self.get(constituent_index as u32); - Ok(datum.weight) + let target_weight = calculate_target_weight( + datum.target_base, + &spot_market, + price, + aum, + WeightValidationFlags::NONE, + )?; + Ok(target_weight) } } -/// Update target weights based on amm_inventory and mapping -impl<'a> AccountZeroCopyMut<'a, WeightDatum, ConstituentTargetWeightsFixed> { - pub fn update_target_weights( +pub fn calculate_target_weight( + target_base: i64, + spot_market: &SpotMarket, + price: i64, + lp_pool_aum: u128, + validation_flags: WeightValidationFlags, +) -> DriftResult { + //.clamp(-PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_I128) as i64; + + // assumes PRICE_PRECISION = PERCENTAGE_PRECISION + let token_precision = 10_i128.pow(spot_market.decimals as u32); + + let value_usd = target_base + .cast::()? + .safe_mul(price.cast::()?)?; + + let target_weight = value_usd + .cast::()? + .safe_mul(PERCENTAGE_PRECISION_I64.cast::()?)? + .safe_div(lp_pool_aum.cast::()?.safe_mul(token_precision)?)? + .cast::()?; + + // if (validation_flags as u8 & (WeightValidationFlags::NoNegativeWeights as u8) != 0) + // && target_weight < 0 + // { + // return Err(ErrorCode::DefaultError); + // } + // if (validation_flags as u8 & (WeightValidationFlags::NoOverweight as u8) != 0) + // && target_weight > PERCENTAGE_PRECISION_I64 as i128 + // { + // return Err(ErrorCode::DefaultError); + // } + + // if (validation_flags as u8) & WeightValidationFlags::EnforceTotalWeight100 as u8 != 0 { + // let deviation = (total_weight - PERCENTAGE_PRECISION_I128).abs(); + // let tolerance = 100; + // if deviation > tolerance { + // return Err(ErrorCode::DefaultError); + // } + // } + + Ok(target_weight) +} + +/// Update target base based on amm_inventory and mapping +impl<'a> AccountZeroCopyMut<'a, TargetsDatum, ConstituentTargetBaseFixed> { + pub fn update_target_base( &mut self, mapping: &AccountZeroCopy<'a, AmmConstituentDatum, AmmConstituentMappingFixed>, // (perp market index, inventory, price) amm_inventory: &[(u16, i64)], constituents_indexes: &[u16], - prices: &[i64], - aum: u128, slot: u64, - validation_flags: WeightValidationFlags, - ) -> DriftResult { - let mut total_weight: i128 = 0; - let aum_i128 = aum.cast::()?; + ) -> DriftResult> { + let mut results = Vec::with_capacity(constituents_indexes.len()); + for (i, constituent_index) in constituents_indexes.iter().enumerate() { let mut target_amount = 0i128; - + for (perp_market_index, inventory) in amm_inventory.iter() { let idx = mapping .iter() .position(|d| &d.perp_market_index == perp_market_index) .expect("missing mapping for this market index"); let weight = mapping.get(idx as u32).weight; // PERCENTAGE_PRECISION - + target_amount += (*inventory as i128) .saturating_mul(weight as i128) .saturating_div(PERCENTAGE_PRECISION_I64 as i128); } - - let price = prices[i] as i128; - - // assumes PRICE_PRECISION = PERCENTAGE_PRECISION - let target_weight: i128 = if aum > 0 { - target_amount.saturating_mul(price).saturating_div(aum_i128) - } else { - 0 - }; - - if (validation_flags as u8 & (WeightValidationFlags::NoNegativeWeights as u8) != 0) - && target_weight < 0 - { - return Err(ErrorCode::DefaultError); - } - if (validation_flags as u8 & (WeightValidationFlags::NoOverweight as u8) != 0) - && target_weight > PERCENTAGE_PRECISION_I64 as i128 - { - return Err(ErrorCode::DefaultError); - } - + let cell = self.get_mut(i as u32); msg!( - "updating constituent index {} target weight to {}", + "updating constituent index {} target amount to {}", constituent_index, - target_weight + target_amount ); - cell.weight = - target_weight.clamp(-PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_I128) as i64; + cell.target_base = target_amount as i64; cell.last_slot = slot; - - total_weight = total_weight.saturating_add(target_weight); - } - - if (validation_flags as u8) & WeightValidationFlags::EnforceTotalWeight100 as u8 != 0 { - let deviation = (total_weight - PERCENTAGE_PRECISION_I128).abs(); - let tolerance = 100; - if deviation > tolerance { - return Err(ErrorCode::DefaultError); - } + + results.push(target_amount); } - - Ok(total_weight) + + Ok(results) } } diff --git a/programs/drift/src/state/lp_pool/tests.rs b/programs/drift/src/state/lp_pool/tests.rs index d14cdf999a..fb42ab2229 100644 --- a/programs/drift/src/state/lp_pool/tests.rs +++ b/programs/drift/src/state/lp_pool/tests.rs @@ -53,33 +53,25 @@ mod tests { let aum = 1_000_000; let now_ts = 1000; - let target_fixed = RefCell::new(ConstituentTargetWeightsFixed { + let target_fixed = RefCell::new(ConstituentTargetBaseFixed { len: 1, - ..ConstituentTargetWeightsFixed::default() + ..ConstituentTargetBaseFixed::default() }); let target_data = RefCell::new([0u8; 16]); let mut target_zc_mut = - AccountZeroCopyMut::<'_, WeightDatum, ConstituentTargetWeightsFixed> { + AccountZeroCopyMut::<'_, TargetsDatum, ConstituentTargetBaseFixed> { fixed: target_fixed.borrow_mut(), data: target_data.borrow_mut(), - _marker: PhantomData::, + _marker: PhantomData::, }; let totalw = target_zc_mut - .update_target_weights( - &mapping_zc, - &amm_inventory, - &constituent_indexes, - &prices, - aum, - now_ts, - WeightValidationFlags::NONE, - ) + .update_target_base(&mapping_zc, &amm_inventory, &constituent_indexes, now_ts) .unwrap(); - assert_eq!(totalw, 0); + assert!(totalw.iter().all(|&x| x == 0)); assert_eq!(target_zc_mut.len(), 1); - assert_eq!(target_zc_mut.get(0).weight, 0); + assert_eq!(target_zc_mut.get(0).target_base, 0); assert_eq!(target_zc_mut.get(0).last_slot, now_ts); } @@ -117,34 +109,26 @@ mod tests { let aum = 1_000_000; let now_ts = 1234; - let target_fixed = RefCell::new(ConstituentTargetWeightsFixed { + let target_fixed = RefCell::new(ConstituentTargetBaseFixed { len: 1, - ..ConstituentTargetWeightsFixed::default() + ..ConstituentTargetBaseFixed::default() }); let target_data = RefCell::new([0u8; 16]); let mut target_zc_mut = - AccountZeroCopyMut::<'_, WeightDatum, ConstituentTargetWeightsFixed> { + AccountZeroCopyMut::<'_, TargetsDatum, ConstituentTargetBaseFixed> { fixed: target_fixed.borrow_mut(), data: target_data.borrow_mut(), - _marker: PhantomData::, + _marker: PhantomData::, }; let totalw = target_zc_mut - .update_target_weights( - &mapping_zc, - &amm_inventory, - &constituent_indexes, - &prices, - aum, - now_ts, - WeightValidationFlags::NONE, - ) + .update_target_base(&mapping_zc, &amm_inventory, &constituent_indexes, now_ts) .unwrap(); - assert_eq!(totalw, 1000000); + assert_eq!(totalw, [1000000]); assert_eq!(target_zc_mut.len(), 1); - assert_eq!(target_zc_mut.get(0).weight, PERCENTAGE_PRECISION_I64); + assert_eq!(target_zc_mut.get(0).target_base, PERCENTAGE_PRECISION_I64); assert_eq!(target_zc_mut.get(0).last_slot, now_ts); } @@ -193,34 +177,26 @@ mod tests { let aum = 1_000_000; let now_ts = 999; - let target_fixed = RefCell::new(ConstituentTargetWeightsFixed { + let target_fixed = RefCell::new(ConstituentTargetBaseFixed { len: amm_mapping_data.len() as u32, - ..ConstituentTargetWeightsFixed::default() + ..ConstituentTargetBaseFixed::default() }); let target_data = RefCell::new([0u8; 32]); let mut target_zc_mut = - AccountZeroCopyMut::<'_, WeightDatum, ConstituentTargetWeightsFixed> { + AccountZeroCopyMut::<'_, TargetsDatum, ConstituentTargetBaseFixed> { fixed: target_fixed.borrow_mut(), data: target_data.borrow_mut(), - _marker: PhantomData::, + _marker: PhantomData::, }; target_zc_mut - .update_target_weights( - &mapping_zc, - &amm_inventory, - &constituent_indexes, - &prices, - aum, - now_ts, - WeightValidationFlags::NONE, - ) + .update_target_base(&mapping_zc, &amm_inventory, &constituent_indexes, now_ts) .unwrap(); assert_eq!(target_zc_mut.len(), 2); for i in 0..target_zc_mut.len() { - assert_eq!(target_zc_mut.get(i).weight, PERCENTAGE_PRECISION_I64 / 2); + assert_eq!(target_zc_mut.get(i).target_base, PERCENTAGE_PRECISION_I64 / 2); assert_eq!(target_zc_mut.get(i).last_slot, now_ts); } } @@ -259,32 +235,24 @@ mod tests { let aum = 0; let now_ts = 111; - let target_fixed = RefCell::new(ConstituentTargetWeightsFixed { + let target_fixed = RefCell::new(ConstituentTargetBaseFixed { len: 1, - ..ConstituentTargetWeightsFixed::default() + ..ConstituentTargetBaseFixed::default() }); let target_data = RefCell::new([0u8; 16]); let mut target_zc_mut = - AccountZeroCopyMut::<'_, WeightDatum, ConstituentTargetWeightsFixed> { + AccountZeroCopyMut::<'_, TargetsDatum, ConstituentTargetBaseFixed> { fixed: target_fixed.borrow_mut(), data: target_data.borrow_mut(), - _marker: PhantomData::, + _marker: PhantomData::, }; target_zc_mut - .update_target_weights( - &mapping_zc, - &amm_inventory, - &constituent_indexes, - &prices, - aum, - now_ts, - WeightValidationFlags::NONE, - ) + .update_target_base(&mapping_zc, &amm_inventory, &constituent_indexes, now_ts) .unwrap(); assert_eq!(target_zc_mut.len(), 1); - assert_eq!(target_zc_mut.get(0).weight, 0); // no target + assert_eq!(target_zc_mut.get(0).target_base, 1_000_000); // despite no aum, desire to reach target assert_eq!(target_zc_mut.get(0).last_slot, now_ts); } @@ -322,32 +290,24 @@ mod tests { let aum = 1; let now_ts = 222; - let target_fixed = RefCell::new(ConstituentTargetWeightsFixed { + let target_fixed = RefCell::new(ConstituentTargetBaseFixed { len: 1, - ..ConstituentTargetWeightsFixed::default() + ..ConstituentTargetBaseFixed::default() }); let target_data = RefCell::new([0u8; 16]); let mut target_zc_mut = - AccountZeroCopyMut::<'_, WeightDatum, ConstituentTargetWeightsFixed> { + AccountZeroCopyMut::<'_, TargetsDatum, ConstituentTargetBaseFixed> { fixed: target_fixed.borrow_mut(), data: target_data.borrow_mut(), - _marker: PhantomData::, + _marker: PhantomData::, }; target_zc_mut - .update_target_weights( - &mapping_zc, - &amm_inventory, - &constituent_indexes, - &prices, - aum, - now_ts, - WeightValidationFlags::NONE, - ) + .update_target_base(&mapping_zc, &amm_inventory, &constituent_indexes, now_ts) .unwrap(); assert_eq!(target_zc_mut.len(), 1); - assert!(target_zc_mut.get(0).weight <= PERCENTAGE_PRECISION_I64); + assert!(target_zc_mut.get(0).target_base < i64::MAX); // rounding sat div assert_eq!(target_zc_mut.get(0).last_slot, now_ts); }