diff --git a/programs/drift/src/error.rs b/programs/drift/src/error.rs index ab38f477b..4c5bbcfa9 100644 --- a/programs/drift/src/error.rs +++ b/programs/drift/src/error.rs @@ -692,6 +692,8 @@ pub enum ErrorCode { Unauthorized, #[msg("Invalid Lp Pool Id for Operation")] InvalidLpPoolId, + #[msg("MarketIndexNotFoundAmmCache")] + MarketIndexNotFoundAmmCache, } #[macro_export] diff --git a/programs/drift/src/instructions/admin.rs b/programs/drift/src/instructions/admin.rs index 456886ea2..35d665c0f 100644 --- a/programs/drift/src/instructions/admin.rs +++ b/programs/drift/src/instructions/admin.rs @@ -1107,17 +1107,6 @@ pub fn handle_initialize_perp_market( safe_increment!(state.number_of_markets, 1); - let amm_cache = &mut ctx.accounts.amm_cache; - let current_len = amm_cache.cache.len(); - amm_cache - .cache - .resize_with(current_len + 1, CacheInfo::default); - let current_market_info = amm_cache.cache.get_mut(current_len).unwrap(); - current_market_info.slot = clock_slot; - current_market_info.oracle = perp_market.amm.oracle; - current_market_info.oracle_source = u8::from(perp_market.amm.oracle_source); - amm_cache.validate(state)?; - controller::amm::update_concentration_coef(perp_market, concentration_coef_scale)?; crate::dlog!(oracle_price); @@ -1140,11 +1129,21 @@ pub fn handle_initialize_amm_cache(ctx: Context) -> Result<( Ok(()) } -pub fn handle_resize_amm_cache(ctx: Context) -> Result<()> { +pub fn handle_add_market_to_amm_cache(ctx: Context) -> Result<()> { let amm_cache = &mut ctx.accounts.amm_cache; - let state = &ctx.accounts.state; + let perp_market = ctx.accounts.perp_market.load()?; + + for cache_info in amm_cache.cache.iter() { + validate!( + cache_info.market_index != perp_market.market_index, + ErrorCode::DefaultError, + "Market index {} already in amm cache", + perp_market.market_index + )?; + } + let current_size = amm_cache.cache.len(); - let new_size = (state.number_of_markets as usize).min(current_size + 20_usize); + let new_size = current_size.saturating_add(1); msg!( "resizing amm cache from {} entries to {}", @@ -1152,16 +1151,10 @@ pub fn handle_resize_amm_cache(ctx: Context) -> Result<()> { new_size ); - let growth = new_size.saturating_sub(current_size); - validate!( - growth <= 20, - ErrorCode::DefaultError, - "cannot grow amm_cache by more than 20 entries in a single resize (requested +{})", - growth - )?; - - amm_cache.cache.resize_with(new_size, CacheInfo::default); - amm_cache.validate(state)?; + amm_cache.cache.resize_with(new_size, || CacheInfo { + market_index: perp_market.market_index, + ..CacheInfo::default() + }); Ok(()) } @@ -3819,7 +3812,14 @@ pub fn handle_update_perp_market_oracle( perp_market.amm.oracle = oracle; perp_market.amm.oracle_source = oracle_source; - amm_cache.update_perp_market_fields(perp_market)?; + if amm_cache + .cache + .iter() + .find(|cache_info| cache_info.market_index == perp_market.market_index) + .is_some() + { + amm_cache.update_perp_market_fields(perp_market)?; + } Ok(()) } @@ -5511,15 +5511,6 @@ pub struct InitializePerpMarket<'info> { payer = admin )] pub perp_market: AccountLoader<'info, PerpMarket>, - #[account( - mut, - seeds = [AMM_POSITIONS_CACHE.as_ref()], - bump = amm_cache.bump, - realloc = AmmCache::space(amm_cache.cache.len() + 1_usize), - realloc::payer = admin, - realloc::zero = false, - )] - pub amm_cache: Box>, /// CHECK: checked in `initialize_perp_market` pub oracle: AccountInfo<'info>, pub rent: Sysvar<'info, Rent>, @@ -5547,7 +5538,7 @@ pub struct InitializeAmmCache<'info> { } #[derive(Accounts)] -pub struct ResizeAmmCache<'info> { +pub struct AddMarketToAmmCache<'info> { #[account( mut, constraint = admin.key() == admin_hot_wallet::id() || admin.key() == state.admin @@ -5558,22 +5549,23 @@ pub struct ResizeAmmCache<'info> { mut, seeds = [AMM_POSITIONS_CACHE.as_ref()], bump, - realloc = AmmCache::space(amm_cache.cache.len() + (state.number_of_markets as usize - amm_cache.cache.len()).min(20_usize)), + realloc = AmmCache::space(amm_cache.cache.len() + 1), realloc::payer = admin, realloc::zero = false, )] pub amm_cache: Box>, + pub perp_market: AccountLoader<'info, PerpMarket>, pub rent: Sysvar<'info, Rent>, pub system_program: Program<'info, System>, } #[derive(Accounts)] pub struct DeleteAmmCache<'info> { - #[account(mut)] - pub admin: Signer<'info>, #[account( - has_one = admin + mut, + constraint = admin.key() == admin_hot_wallet::id() || admin.key() == state.admin )] + pub admin: Signer<'info>, pub state: Box>, #[account( mut, diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index c0c465395..d4308a531 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -3378,7 +3378,7 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>( continue; } - let cached_info = amm_cache.get_mut(perp_market.market_index as u32); + let cached_info = amm_cache.get_for_market_index_mut(perp_market.market_index)?; // Early validation checks if slot.saturating_sub(cached_info.oracle_slot) > SETTLE_AMM_ORACLE_MAX_DELAY { @@ -3594,7 +3594,7 @@ pub fn handle_update_amm_cache<'c: 'info, 'info>( if perp_market.lp_status == 0 { continue; } - let cached_info = amm_cache.get_mut(perp_market.market_index as u32); + let cached_info = amm_cache.get_for_market_index_mut(perp_market.market_index)?; validate!( perp_market.oracle_id() == cached_info.oracle_id()?, diff --git a/programs/drift/src/instructions/lp_admin.rs b/programs/drift/src/instructions/lp_admin.rs index 28e6026a4..21630eae2 100644 --- a/programs/drift/src/instructions/lp_admin.rs +++ b/programs/drift/src/instructions/lp_admin.rs @@ -922,20 +922,6 @@ pub fn handle_update_initial_amm_cache_info<'c: 'info, 'info>( Ok(()) } -pub fn handle_reset_amm_cache(ctx: Context) -> Result<()> { - let state = &ctx.accounts.state; - let amm_cache = &mut ctx.accounts.amm_cache; - - amm_cache.cache.clear(); - amm_cache - .cache - .resize_with(state.number_of_markets as usize, CacheInfo::default); - amm_cache.validate(state)?; - - msg!("AMM cache reset. markets: {}", state.number_of_markets); - Ok(()) -} - #[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)] pub struct OverrideAmmCacheParams { pub quote_owed_from_lp_pool: Option, diff --git a/programs/drift/src/instructions/lp_pool.rs b/programs/drift/src/instructions/lp_pool.rs index cfb485675..3108af7ca 100644 --- a/programs/drift/src/instructions/lp_pool.rs +++ b/programs/drift/src/instructions/lp_pool.rs @@ -43,6 +43,7 @@ use crate::{ }, validate, }; +use std::collections::BTreeMap; use std::iter::Peekable; use std::slice::Iter; @@ -96,31 +97,33 @@ pub fn handle_update_constituent_target_base<'c: 'info, 'info>( let constituent_map = ConstituentMap::load(&ConstituentSet::new(), &lp_pool_key, remaining_accounts)?; - let mut amm_inventories: Vec = - Vec::with_capacity(amm_cache.len() as usize); + let mut amm_inventories: BTreeMap = BTreeMap::new(); for (_, cache_info) in amm_cache.iter().enumerate() { if cache_info.lp_status_for_perp_market == 0 { continue; } - amm_inventories.push(AmmInventoryAndPricesAndSlots { - inventory: { - let scaled_position = cache_info - .position - .safe_mul(cache_info.amm_position_scalar as i64)? - .safe_div(100)?; - - scaled_position.clamp( - -cache_info.amm_inventory_limit, - cache_info.amm_inventory_limit, - ) + amm_inventories.insert( + cache_info.market_index, + AmmInventoryAndPricesAndSlots { + inventory: { + let scaled_position = cache_info + .position + .safe_mul(cache_info.amm_position_scalar as i64)? + .safe_div(100)?; + + scaled_position.clamp( + -cache_info.amm_inventory_limit, + cache_info.amm_inventory_limit, + ) + }, + price: cache_info.oracle_price, + last_oracle_slot: cache_info.oracle_slot, + last_position_slot: cache_info.slot, }, - price: cache_info.oracle_price, - last_oracle_slot: cache_info.oracle_slot, - last_position_slot: cache_info.slot, - }); + ); } - msg!("amm inventories: {:?}", amm_inventories); + msg!("amm inventories:{:?}", amm_inventories); if amm_inventories.is_empty() { msg!("No valid inventories found for constituent target weights update"); @@ -140,7 +143,7 @@ pub fn handle_update_constituent_target_base<'c: 'info, 'info>( constituent_target_base.update_target_base( &amm_constituent_mapping, - amm_inventories.as_slice(), + &amm_inventories, constituent_indexes_and_decimals_and_prices.as_mut_slice(), slot, )?; @@ -1010,7 +1013,7 @@ pub fn handle_lp_pool_remove_liquidity<'c: 'info, 'info>( if cache_info.last_fee_pool_token_amount != 0 && cache_info.last_settle_slot != slot { msg!( "Market {} has not been settled in current slot. Last slot: {}", - i, + cache_info.market_index, cache_info.last_settle_slot ); return Err(ErrorCode::AMMCacheStale.into()); diff --git a/programs/drift/src/lib.rs b/programs/drift/src/lib.rs index 3d13094eb..70fe980b7 100644 --- a/programs/drift/src/lib.rs +++ b/programs/drift/src/lib.rs @@ -1020,10 +1020,16 @@ pub mod drift { handle_initialize_amm_cache(ctx) } - pub fn resize_amm_cache<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, ResizeAmmCache<'info>>, + pub fn add_market_to_amm_cache<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, AddMarketToAmmCache<'info>>, ) -> Result<()> { - handle_resize_amm_cache(ctx) + handle_add_market_to_amm_cache(ctx) + } + + pub fn delete_amm_cache<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, DeleteAmmCache<'info>>, + ) -> Result<()> { + handle_delete_amm_cache(ctx) } pub fn update_initial_amm_cache_info<'c: 'info, 'info>( @@ -2086,12 +2092,6 @@ pub mod drift { handle_override_amm_cache_info(ctx, market_index, override_params) } - pub fn reset_amm_cache<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, ResetAmmCache<'info>>, - ) -> Result<()> { - handle_reset_amm_cache(ctx) - } - pub fn lp_pool_swap<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, LPPoolSwap<'info>>, in_market_index: u16, diff --git a/programs/drift/src/state/amm_cache.rs b/programs/drift/src/state/amm_cache.rs index 59982ce15..1e485b228 100644 --- a/programs/drift/src/state/amm_cache.rs +++ b/programs/drift/src/state/amm_cache.rs @@ -10,11 +10,9 @@ use crate::state::oracle::MMOraclePriceData; use crate::state::oracle_map::OracleIdentifier; use crate::state::perp_market::PerpMarket; use crate::state::spot_market::{SpotBalance, SpotMarket}; -use crate::state::state::State; use crate::state::traits::Size; use crate::state::zero_copy::HasLen; use crate::state::zero_copy::{AccountZeroCopy, AccountZeroCopyMut}; -use crate::validate; use crate::OracleSource; use crate::{impl_zero_copy_loader, OracleGuardRails}; @@ -53,11 +51,12 @@ pub struct CacheInfo { pub amm_inventory_limit: i64, pub oracle_price: i64, pub oracle_slot: u64, + pub market_index: u16, pub oracle_source: u8, pub oracle_validity: u8, pub lp_status_for_perp_market: u8, pub amm_position_scalar: u8, - pub _padding: [u8; 36], + pub _padding: [u8; 34], } impl Size for CacheInfo { @@ -86,7 +85,8 @@ impl Default for CacheInfo { quote_owed_from_lp_pool: 0i64, lp_status_for_perp_market: 0u8, amm_position_scalar: 0u8, - _padding: [0u8; 36], + market_index: 0u16, + _padding: [0u8; 34], } } } @@ -183,15 +183,6 @@ impl AmmCache { 8 + 8 + 4 + num_markets * CacheInfo::SIZE } - pub fn validate(&self, state: &State) -> DriftResult<()> { - validate!( - self.cache.len() <= state.number_of_markets as usize, - ErrorCode::DefaultError, - "Number of amm positions is no larger than number of markets" - )?; - Ok(()) - } - pub fn update_perp_market_fields(&mut self, perp_market: &PerpMarket) -> DriftResult<()> { let cache_info = self.cache.get_mut(perp_market.market_index as usize); if let Some(cache_info) = cache_info { @@ -238,6 +229,15 @@ impl AmmCache { impl_zero_copy_loader!(AmmCache, crate::id, AmmCacheFixed, CacheInfo); impl<'a> AccountZeroCopy<'a, CacheInfo, AmmCacheFixed> { + pub fn get_for_market_index(&self, market_index: u16) -> DriftResult<&CacheInfo> { + for cache_info in self.iter() { + if cache_info.market_index == market_index { + return Ok(cache_info); + } + } + Err(ErrorCode::MarketIndexNotFoundAmmCache.into()) + } + pub fn check_settle_staleness(&self, slot: u64, threshold_slot_diff: u64) -> DriftResult<()> { for (i, cache_info) in self.iter().enumerate() { if cache_info.slot == 0 { @@ -284,6 +284,27 @@ impl<'a> AccountZeroCopy<'a, CacheInfo, AmmCacheFixed> { } impl<'a> AccountZeroCopyMut<'a, CacheInfo, AmmCacheFixed> { + pub fn get_for_market_index_mut(&mut self, market_index: u16) -> DriftResult<&mut CacheInfo> { + let pos = { + let mut found: Option = None; + for i in 0..self.len() { + let cache_info = self.get(i); + if cache_info.market_index == market_index { + found = Some(i); + break; + } + } + found + }; + + if let Some(i) = pos { + Ok(self.get_mut(i)) + } else { + msg!("Market index not found in amm cache: {}", market_index); + Err(ErrorCode::MarketIndexNotFoundAmmCache.into()) + } + } + pub fn update_amount_owed_from_lp_pool( &mut self, perp_market: &PerpMarket, diff --git a/programs/drift/src/state/lp_pool.rs b/programs/drift/src/state/lp_pool.rs index 106945fa5..f975ce9e8 100644 --- a/programs/drift/src/state/lp_pool.rs +++ b/programs/drift/src/state/lp_pool.rs @@ -1378,27 +1378,33 @@ impl<'a> AccountZeroCopyMut<'a, TargetsDatum, ConstituentTargetBaseFixed> { pub fn update_target_base( &mut self, mapping: &AccountZeroCopy<'a, AmmConstituentDatum, AmmConstituentMappingFixed>, - amm_inventory_and_prices: &[AmmInventoryAndPricesAndSlots], + amm_inventory_and_prices: &std::collections::BTreeMap, constituents_indexes_and_decimals_and_prices: &mut [ConstituentIndexAndDecimalAndPrice], slot: u64, ) -> DriftResult<()> { // Sorts by constituent index constituents_indexes_and_decimals_and_prices.sort_by_key(|c| c.constituent_index); - // Precompute notional by perp market index - let mut notionals_and_slots: Vec<(i128, u64, u64)> = - Vec::with_capacity(amm_inventory_and_prices.len()); - for &AmmInventoryAndPricesAndSlots { - inventory, - price, - last_oracle_slot, - last_position_slot, - } in amm_inventory_and_prices.iter() + let mut notionals_and_slots: std::collections::BTreeMap = + std::collections::BTreeMap::new(); + for ( + &market_index, + &AmmInventoryAndPricesAndSlots { + inventory, + price, + last_oracle_slot, + last_position_slot, + .. + }, + ) in amm_inventory_and_prices.iter() { let notional = (inventory as i128) .safe_mul(price as i128)? .safe_div(BASE_PRECISION_I128)?; - notionals_and_slots.push((notional, last_oracle_slot, last_position_slot)); + notionals_and_slots.insert( + market_index, + (notional, last_oracle_slot, last_position_slot), + ); } let mut mapping_index = 0; @@ -1428,7 +1434,7 @@ impl<'a> AccountZeroCopyMut<'a, TargetsDatum, ConstituentTargetBaseFixed> { break; } if let Some((perp_notional, perp_last_oracle_slot, perp_last_position_slot)) = - notionals_and_slots.get(d.perp_market_index as usize) + notionals_and_slots.get(&d.perp_market_index) { target_notional = target_notional .saturating_add(perp_notional.saturating_mul(d.weight as i128)); diff --git a/programs/drift/src/state/lp_pool/tests.rs b/programs/drift/src/state/lp_pool/tests.rs index ef9153b72..3e76383af 100644 --- a/programs/drift/src/state/lp_pool/tests.rs +++ b/programs/drift/src/state/lp_pool/tests.rs @@ -4,7 +4,7 @@ mod tests { BASE_PRECISION_I64, PERCENTAGE_PRECISION_I64, PRICE_PRECISION_I64, QUOTE_PRECISION, }; use crate::state::lp_pool::*; - use std::{cell::RefCell, marker::PhantomData, vec}; + use std::{cell::RefCell, collections::BTreeMap, marker::PhantomData, vec}; fn amm_const_datum( perp_market_index: u16, @@ -68,32 +68,45 @@ mod tests { } }; - let amm_inventory_and_price: Vec = vec![ + let mut amm_inventory_and_price: BTreeMap = + BTreeMap::new(); + // key: perp market index + amm_inventory_and_price.insert( + 0, AmmInventoryAndPricesAndSlots { inventory: 4 * BASE_PRECISION_I64, price: 100_000 * PRICE_PRECISION_I64, last_oracle_slot: slot, last_position_slot: slot, - }, // $400k BTC + }, + ); // $400k BTC + amm_inventory_and_price.insert( + 1, AmmInventoryAndPricesAndSlots { inventory: 2000 * BASE_PRECISION_I64, price: 200 * PRICE_PRECISION_I64, last_oracle_slot: slot, last_position_slot: slot, - }, // $400k SOL + }, + ); // $400k SOL + amm_inventory_and_price.insert( + 2, AmmInventoryAndPricesAndSlots { inventory: 200 * BASE_PRECISION_I64, price: 1500 * PRICE_PRECISION_I64, last_oracle_slot: slot, last_position_slot: slot, - }, // $300k ETH + }, + ); // $300k ETH + amm_inventory_and_price.insert( + 3, AmmInventoryAndPricesAndSlots { inventory: 16500 * BASE_PRECISION_I64, price: PRICE_PRECISION_I64, last_oracle_slot: slot, last_position_slot: slot, - }, // $16.5k FARTCOIN - ]; + }, + ); // $16.5k FARTCOIN let mut constituents_indexes_and_decimals_and_prices = vec![ ConstituentIndexAndDecimalAndPrice { constituent_index: 0, @@ -145,7 +158,7 @@ mod tests { calculate_target_weight( datum.target_base.cast::().unwrap(), &SpotMarket::default_quote_market(), - amm_inventory_and_price.get(index).unwrap().price, + amm_inventory_and_price.get(&(index as u16)).unwrap().price, aum, ) .unwrap() @@ -189,13 +202,17 @@ mod tests { } }; - let amm_inventory_and_prices: Vec = - vec![AmmInventoryAndPricesAndSlots { + let mut amm_inventory_and_prices: BTreeMap = + BTreeMap::new(); + amm_inventory_and_prices.insert( + 0, + AmmInventoryAndPricesAndSlots { inventory: 1_000_000, price: 1_000_000, last_oracle_slot: slot, last_position_slot: slot, - }]; + }, + ); let mut constituents_indexes_and_decimals_and_prices = vec![ConstituentIndexAndDecimalAndPrice { constituent_index: 1, @@ -259,13 +276,17 @@ mod tests { }; let price = PRICE_PRECISION_I64; - let amm_inventory_and_prices: Vec = - vec![AmmInventoryAndPricesAndSlots { + let mut amm_inventory_and_prices: BTreeMap = + BTreeMap::new(); + amm_inventory_and_prices.insert( + 0, + AmmInventoryAndPricesAndSlots { inventory: BASE_PRECISION_I64, price, last_oracle_slot: slot, last_position_slot: slot, - }]; + }, + ); let mut constituents_indexes_and_decimals_and_prices = vec![ConstituentIndexAndDecimalAndPrice { constituent_index: 1, @@ -362,13 +383,17 @@ mod tests { } }; - let amm_inventory_and_prices: Vec = - vec![AmmInventoryAndPricesAndSlots { + let mut amm_inventory_and_prices: BTreeMap = + BTreeMap::new(); + amm_inventory_and_prices.insert( + 0, + AmmInventoryAndPricesAndSlots { inventory: 1_000_000_000, price: 1_000_000, last_oracle_slot: slot, last_position_slot: slot, - }]; + }, + ); let mut constituents_indexes_and_decimals_and_prices = vec![ ConstituentIndexAndDecimalAndPrice { constituent_index: 1, @@ -453,13 +478,17 @@ mod tests { } }; - let amm_inventory_and_prices: Vec = - vec![AmmInventoryAndPricesAndSlots { + let mut amm_inventory_and_prices: BTreeMap = + BTreeMap::new(); + amm_inventory_and_prices.insert( + 0, + AmmInventoryAndPricesAndSlots { inventory: 1_000_000, price: 142_000_000, last_oracle_slot: slot, last_position_slot: slot, - }]; + }, + ); let mut constituents_indexes_and_decimals_and_prices = vec![ConstituentIndexAndDecimalAndPrice { constituent_index: 1, diff --git a/sdk/src/adminClient.ts b/sdk/src/adminClient.ts index 9698e1371..bb2fb35ae 100644 --- a/sdk/src/adminClient.ts +++ b/sdk/src/adminClient.ts @@ -507,12 +507,6 @@ export class AdminClient extends DriftClient { ): Promise { const currentPerpMarketIndex = this.getStateAccount().numberOfMarkets; - const ammCachePublicKey = getAmmCachePublicKey(this.program.programId); - const ammCacheAccount = await this.connection.getAccountInfo( - ammCachePublicKey - ); - const mustInitializeAmmCache = ammCacheAccount?.data == null; - const initializeMarketIxs = await this.getInitializePerpMarketIx( marketIndex, priceOracle, @@ -540,8 +534,7 @@ export class AdminClient extends DriftClient { curveUpdateIntensity, ammJitIntensity, name, - lpPoolId, - mustInitializeAmmCache + lpPoolId ); const tx = await this.buildTransaction(initializeMarketIxs); @@ -588,8 +581,7 @@ export class AdminClient extends DriftClient { curveUpdateIntensity = 0, ammJitIntensity = 0, name = DEFAULT_MARKET_NAME, - lpPoolId: number = 0, - includeInitAmmCacheIx = false + lpPoolId: number = 0 ): Promise { const perpMarketPublicKey = await getPerpMarketPublicKey( this.program.programId, @@ -598,10 +590,6 @@ export class AdminClient extends DriftClient { const ixs: TransactionInstruction[] = []; - if (includeInitAmmCacheIx) { - ixs.push(await this.getInitializeAmmCacheIx()); - } - const nameBuffer = encodeName(name); const initPerpIx = await this.program.instruction.initializePerpMarket( marketIndex, @@ -638,7 +626,6 @@ export class AdminClient extends DriftClient { : this.wallet.publicKey, oracle: priceOracle, perpMarket: perpMarketPublicKey, - ammCache: getAmmCachePublicKey(this.program.programId), rent: SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, }, @@ -667,17 +654,20 @@ export class AdminClient extends DriftClient { admin: this.useHotWalletAdmin ? this.wallet.publicKey : this.getStateAccount().admin, - ammCache: getAmmCachePublicKey(this.program.programId), rent: SYSVAR_RENT_PUBKEY, + ammCache: getAmmCachePublicKey(this.program.programId), systemProgram: anchor.web3.SystemProgram.programId, }, }); } - public async resizeAmmCache( + public async addMarketToAmmCache( + perpMarketIndex: number, txParams?: TxParams ): Promise { - const initializeAmmCacheIx = await this.getResizeAmmCacheIx(); + const initializeAmmCacheIx = await this.getAddMarketToAmmCacheIx( + perpMarketIndex + ); const tx = await this.buildTransaction(initializeAmmCacheIx, txParams); @@ -686,13 +676,16 @@ export class AdminClient extends DriftClient { return txSig; } - public async getResizeAmmCacheIx(): Promise { - return await this.program.instruction.resizeAmmCache({ + public async getAddMarketToAmmCacheIx( + perpMarketIndex: number + ): Promise { + return await this.program.instruction.addMarketToAmmCache({ accounts: { state: await this.getStatePublicKey(), admin: this.useHotWalletAdmin ? this.wallet.publicKey : this.getStateAccount().admin, + perpMarket: this.getPerpMarketAccount(perpMarketIndex).pubkey, ammCache: getAmmCachePublicKey(this.program.programId), rent: SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index f11d15d6d..f8ce96e37 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -4285,11 +4285,6 @@ "isMut": true, "isSigner": false }, - { - "name": "ammCache", - "isMut": true, - "isSigner": false - }, { "name": "oracle", "isMut": false, @@ -4454,7 +4449,7 @@ "args": [] }, { - "name": "resizeAmmCache", + "name": "addMarketToAmmCache", "accounts": [ { "name": "admin", @@ -4471,6 +4466,11 @@ "isMut": true, "isSigner": false }, + { + "name": "perpMarket", + "isMut": false, + "isSigner": false + }, { "name": "rent", "isMut": false, @@ -4484,6 +4484,27 @@ ], "args": [] }, + { + "name": "deleteAmmCache", + "accounts": [ + { + "name": "admin", + "isMut": true, + "isSigner": true + }, + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "ammCache", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, { "name": "updateInitialAmmCacheInfo", "accounts": [ @@ -8478,32 +8499,6 @@ } ] }, - { - "name": "resetAmmCache", - "accounts": [ - { - "name": "admin", - "isMut": true, - "isSigner": true - }, - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "ammCache", - "isMut": true, - "isSigner": false - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false - } - ], - "args": [] - }, { "name": "lpPoolSwap", "accounts": [ @@ -12267,6 +12262,10 @@ "name": "oracleSlot", "type": "u64" }, + { + "name": "marketIndex", + "type": "u16" + }, { "name": "oracleSource", "type": "u8" @@ -12288,7 +12287,7 @@ "type": { "array": [ "u8", - 36 + 34 ] } } @@ -19777,6 +19776,11 @@ "code": 6343, "name": "InvalidLpPoolId", "msg": "Invalid Lp Pool Id for Operation" + }, + { + "code": 6344, + "name": "MarketIndexNotFoundAmmCache", + "msg": "MarketIndexNotFoundAmmCache" } ] } \ No newline at end of file diff --git a/sdk/src/types.ts b/sdk/src/types.ts index bb1580569..ee5ff3542 100644 --- a/sdk/src/types.ts +++ b/sdk/src/types.ts @@ -1891,6 +1891,7 @@ export type CacheInfo = { oracleValidity: number; lpStatusForPerpMarket: number; ammPositionScalar: number; + marketIndex: number; }; export type AmmCache = { diff --git a/tests/admin.ts b/tests/admin.ts index c397b3f35..533068d29 100644 --- a/tests/admin.ts +++ b/tests/admin.ts @@ -106,6 +106,7 @@ describe('admin', () => { new BN(1000), periodicity ); + await driftClient.initializeAmmCache(); }); it('checks market name', async () => { diff --git a/tests/lpPool.ts b/tests/lpPool.ts index 6cd722977..b82a5623e 100644 --- a/tests/lpPool.ts +++ b/tests/lpPool.ts @@ -191,6 +191,8 @@ describe('LP Pool', () => { solUsdLazer = getPythLazerOraclePublicKey(program.programId, 6); await adminClient.initializePythLazerOracle(6); + await adminClient.initializeAmmCache(); + await adminClient.initializePerpMarket( 0, solUsd, @@ -199,6 +201,7 @@ describe('LP Pool', () => { periodicity, new BN(200 * PEG_PRECISION.toNumber()) ); + await adminClient.addMarketToAmmCache(0); await adminClient.updatePerpMarketLpPoolStatus(0, 1); await adminClient.initializePerpMarket( @@ -209,6 +212,7 @@ describe('LP Pool', () => { periodicity, new BN(200 * PEG_PRECISION.toNumber()) ); + await adminClient.addMarketToAmmCache(1); await adminClient.updatePerpMarketLpPoolStatus(1, 1); await adminClient.initializePerpMarket( @@ -219,6 +223,7 @@ describe('LP Pool', () => { periodicity, new BN(200 * PEG_PRECISION.toNumber()) ); + await adminClient.addMarketToAmmCache(2); await adminClient.updatePerpMarketLpPoolStatus(2, 1); await adminClient.updatePerpAuctionDuration(new BN(0)); @@ -270,7 +275,6 @@ describe('LP Pool', () => { new BN(1_000_000).mul(QUOTE_PRECISION), Keypair.generate() ); - await adminClient.updateFeatureBitFlagsMintRedeemLpPool(true); // Give the vamm some inventory @@ -1732,25 +1736,28 @@ describe('LP Pool', () => { } }); - // it('can delete amm cache and then init and realloc and update', async () => { - // const ammCacheKey = getAmmCachePublicKey(program.programId); - // const ammCacheBefore = (await adminClient.program.account.ammCache.fetch( - // ammCacheKey - // )) as AmmCache; - - // await adminClient.deleteAmmCache(); - // await adminClient.resizeAmmCache(); - // await adminClient.updateInitialAmmCacheInfo([0, 1, 2]); - // await adminClient.updateAmmCache([0, 1, 2]); - - // const ammCacheAfter = (await adminClient.program.account.ammCache.fetch( - // ammCacheKey - // )) as AmmCache; - - // for (let i = 0; i < ammCacheBefore.cache.length; i++) { - // assert( - // ammCacheBefore.cache[i].position.eq(ammCacheAfter.cache[i].position) - // ); - // } - // }); + it('can delete amm cache and then init and realloc and update', async () => { + const ammCacheKey = getAmmCachePublicKey(program.programId); + const ammCacheBefore = (await adminClient.program.account.ammCache.fetch( + ammCacheKey + )) as AmmCache; + + await adminClient.deleteAmmCache(); + await adminClient.initializeAmmCache(); + await adminClient.addMarketToAmmCache(0); + await adminClient.addMarketToAmmCache(1); + await adminClient.addMarketToAmmCache(2); + await adminClient.updateInitialAmmCacheInfo([0, 1, 2]); + await adminClient.updateAmmCache([0, 1, 2]); + + const ammCacheAfter = (await adminClient.program.account.ammCache.fetch( + ammCacheKey + )) as AmmCache; + + for (let i = 0; i < ammCacheBefore.cache.length; i++) { + assert( + ammCacheBefore.cache[i].position.eq(ammCacheAfter.cache[i].position) + ); + } + }); }); diff --git a/tests/lpPoolCUs.ts b/tests/lpPoolCUs.ts index d256c56ed..dea540f49 100644 --- a/tests/lpPoolCUs.ts +++ b/tests/lpPoolCUs.ts @@ -70,7 +70,7 @@ import { dotenv.config(); const NUMBER_OF_CONSTITUENTS = 10; -const NUMBER_OF_PERP_MARKETS = 60; +const NUMBER_OF_PERP_MARKETS = 40; const NUMBER_OF_USERS = Math.ceil(NUMBER_OF_PERP_MARKETS / 8); const PERP_MARKET_INDEXES = Array.from( @@ -255,6 +255,7 @@ describe('LP Pool', () => { new Keypair() ); await adminClient.updateFeatureBitFlagsMintRedeemLpPool(true); + await adminClient.initializeAmmCache(); // check LpPool created const lpPool = (await adminClient.program.account.lpPool.fetch( @@ -414,6 +415,7 @@ describe('LP Pool', () => { new BN(0), new BN(200 * PEG_PRECISION.toNumber()) ); + await adminClient.addMarketToAmmCache(i); await adminClient.updatePerpMarketLpPoolStatus(i, 1); await adminClient.updatePerpMarketLpPoolFeeTransferScalar(i, 100); await sleep(50); diff --git a/tests/lpPoolSwap.ts b/tests/lpPoolSwap.ts index 4361c9a94..866436c13 100644 --- a/tests/lpPoolSwap.ts +++ b/tests/lpPoolSwap.ts @@ -176,6 +176,8 @@ describe('LP Pool', () => { const periodicity = new BN(0); + await adminClient.initializeAmmCache(); + await adminClient.initializePerpMarket( 0, spotMarketOracle, @@ -184,6 +186,7 @@ describe('LP Pool', () => { periodicity, new BN(224 * PEG_PRECISION.toNumber()) ); + await adminClient.addMarketToAmmCache(0); await adminClient.updatePerpMarketLpPoolStatus(0, 1); await adminClient.initializePerpMarket( @@ -194,6 +197,7 @@ describe('LP Pool', () => { periodicity, new BN(224 * PEG_PRECISION.toNumber()) ); + await adminClient.addMarketToAmmCache(1); await adminClient.updatePerpMarketLpPoolStatus(1, 1); const optimalUtilization = SPOT_MARKET_RATE_PRECISION.div( diff --git a/tests/placeAndMakeSignedMsgBankrun.ts b/tests/placeAndMakeSignedMsgBankrun.ts index 597150125..5cf0368d8 100644 --- a/tests/placeAndMakeSignedMsgBankrun.ts +++ b/tests/placeAndMakeSignedMsgBankrun.ts @@ -196,6 +196,7 @@ describe('place and make signedMsg order', () => { periodicity, new BN(224 * PEG_PRECISION.toNumber()) ); + await makerDriftClient.initializeAmmCache(); await makerDriftClient.initializeUserAccountAndDepositCollateral( usdcAmount, diff --git a/tests/prelisting.ts b/tests/prelisting.ts index 92f7a510a..ca784e7b5 100644 --- a/tests/prelisting.ts +++ b/tests/prelisting.ts @@ -130,6 +130,7 @@ describe('prelisting', () => { new BN(32 * PEG_PRECISION.toNumber()), OracleSource.Prelaunch ); + await adminDriftClient.initializeAmmCache(); await adminDriftClient.updatePerpMarketBaseSpread( 0, diff --git a/tests/pythLazerBankrun.ts b/tests/pythLazerBankrun.ts index 7e1721bea..d14ec2f08 100644 --- a/tests/pythLazerBankrun.ts +++ b/tests/pythLazerBankrun.ts @@ -115,6 +115,7 @@ describe('pyth pull oracles', () => { periodicity, new BN(224 * PEG_PRECISION.toNumber()) ); + await driftClient.initializeAmmCache(); await initializeQuoteSpotMarket(driftClient, usdcMint.publicKey); }); diff --git a/tests/switchOracle.ts b/tests/switchOracle.ts index 7cd425e8c..eaefb12f9 100644 --- a/tests/switchOracle.ts +++ b/tests/switchOracle.ts @@ -117,6 +117,7 @@ describe('switch oracles', () => { periodicity, new BN(30 * PEG_PRECISION.toNumber()) ); + await admin.initializeAmmCache(); }); beforeEach(async () => {