Skip to content

Commit 4690a2e

Browse files
authored
Dlp refactor amm cache (#2010)
* cargo tests pass * refactor amm cache to only include collateralized markets * fix anchor tests * update idl with delete func * add hot wallet to delete amm cahce
1 parent 544338b commit 4690a2e

File tree

20 files changed

+260
-205
lines changed

20 files changed

+260
-205
lines changed

programs/drift/src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,8 @@ pub enum ErrorCode {
692692
Unauthorized,
693693
#[msg("Invalid Lp Pool Id for Operation")]
694694
InvalidLpPoolId,
695+
#[msg("MarketIndexNotFoundAmmCache")]
696+
MarketIndexNotFoundAmmCache,
695697
}
696698

697699
#[macro_export]

programs/drift/src/instructions/admin.rs

Lines changed: 31 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,17 +1107,6 @@ pub fn handle_initialize_perp_market(
11071107

11081108
safe_increment!(state.number_of_markets, 1);
11091109

1110-
let amm_cache = &mut ctx.accounts.amm_cache;
1111-
let current_len = amm_cache.cache.len();
1112-
amm_cache
1113-
.cache
1114-
.resize_with(current_len + 1, CacheInfo::default);
1115-
let current_market_info = amm_cache.cache.get_mut(current_len).unwrap();
1116-
current_market_info.slot = clock_slot;
1117-
current_market_info.oracle = perp_market.amm.oracle;
1118-
current_market_info.oracle_source = u8::from(perp_market.amm.oracle_source);
1119-
amm_cache.validate(state)?;
1120-
11211110
controller::amm::update_concentration_coef(perp_market, concentration_coef_scale)?;
11221111
crate::dlog!(oracle_price);
11231112

@@ -1140,28 +1129,32 @@ pub fn handle_initialize_amm_cache(ctx: Context<InitializeAmmCache>) -> Result<(
11401129
Ok(())
11411130
}
11421131

1143-
pub fn handle_resize_amm_cache(ctx: Context<ResizeAmmCache>) -> Result<()> {
1132+
pub fn handle_add_market_to_amm_cache(ctx: Context<AddMarketToAmmCache>) -> Result<()> {
11441133
let amm_cache = &mut ctx.accounts.amm_cache;
1145-
let state = &ctx.accounts.state;
1134+
let perp_market = ctx.accounts.perp_market.load()?;
1135+
1136+
for cache_info in amm_cache.cache.iter() {
1137+
validate!(
1138+
cache_info.market_index != perp_market.market_index,
1139+
ErrorCode::DefaultError,
1140+
"Market index {} already in amm cache",
1141+
perp_market.market_index
1142+
)?;
1143+
}
1144+
11461145
let current_size = amm_cache.cache.len();
1147-
let new_size = (state.number_of_markets as usize).min(current_size + 20_usize);
1146+
let new_size = current_size.saturating_add(1);
11481147

11491148
msg!(
11501149
"resizing amm cache from {} entries to {}",
11511150
current_size,
11521151
new_size
11531152
);
11541153

1155-
let growth = new_size.saturating_sub(current_size);
1156-
validate!(
1157-
growth <= 20,
1158-
ErrorCode::DefaultError,
1159-
"cannot grow amm_cache by more than 20 entries in a single resize (requested +{})",
1160-
growth
1161-
)?;
1162-
1163-
amm_cache.cache.resize_with(new_size, CacheInfo::default);
1164-
amm_cache.validate(state)?;
1154+
amm_cache.cache.resize_with(new_size, || CacheInfo {
1155+
market_index: perp_market.market_index,
1156+
..CacheInfo::default()
1157+
});
11651158

11661159
Ok(())
11671160
}
@@ -3819,7 +3812,14 @@ pub fn handle_update_perp_market_oracle(
38193812
perp_market.amm.oracle = oracle;
38203813
perp_market.amm.oracle_source = oracle_source;
38213814

3822-
amm_cache.update_perp_market_fields(perp_market)?;
3815+
if amm_cache
3816+
.cache
3817+
.iter()
3818+
.find(|cache_info| cache_info.market_index == perp_market.market_index)
3819+
.is_some()
3820+
{
3821+
amm_cache.update_perp_market_fields(perp_market)?;
3822+
}
38233823

38243824
Ok(())
38253825
}
@@ -5511,15 +5511,6 @@ pub struct InitializePerpMarket<'info> {
55115511
payer = admin
55125512
)]
55135513
pub perp_market: AccountLoader<'info, PerpMarket>,
5514-
#[account(
5515-
mut,
5516-
seeds = [AMM_POSITIONS_CACHE.as_ref()],
5517-
bump = amm_cache.bump,
5518-
realloc = AmmCache::space(amm_cache.cache.len() + 1_usize),
5519-
realloc::payer = admin,
5520-
realloc::zero = false,
5521-
)]
5522-
pub amm_cache: Box<Account<'info, AmmCache>>,
55235514
/// CHECK: checked in `initialize_perp_market`
55245515
pub oracle: AccountInfo<'info>,
55255516
pub rent: Sysvar<'info, Rent>,
@@ -5547,7 +5538,7 @@ pub struct InitializeAmmCache<'info> {
55475538
}
55485539

55495540
#[derive(Accounts)]
5550-
pub struct ResizeAmmCache<'info> {
5541+
pub struct AddMarketToAmmCache<'info> {
55515542
#[account(
55525543
mut,
55535544
constraint = admin.key() == admin_hot_wallet::id() || admin.key() == state.admin
@@ -5558,22 +5549,23 @@ pub struct ResizeAmmCache<'info> {
55585549
mut,
55595550
seeds = [AMM_POSITIONS_CACHE.as_ref()],
55605551
bump,
5561-
realloc = AmmCache::space(amm_cache.cache.len() + (state.number_of_markets as usize - amm_cache.cache.len()).min(20_usize)),
5552+
realloc = AmmCache::space(amm_cache.cache.len() + 1),
55625553
realloc::payer = admin,
55635554
realloc::zero = false,
55645555
)]
55655556
pub amm_cache: Box<Account<'info, AmmCache>>,
5557+
pub perp_market: AccountLoader<'info, PerpMarket>,
55665558
pub rent: Sysvar<'info, Rent>,
55675559
pub system_program: Program<'info, System>,
55685560
}
55695561

55705562
#[derive(Accounts)]
55715563
pub struct DeleteAmmCache<'info> {
5572-
#[account(mut)]
5573-
pub admin: Signer<'info>,
55745564
#[account(
5575-
has_one = admin
5565+
mut,
5566+
constraint = admin.key() == admin_hot_wallet::id() || admin.key() == state.admin
55765567
)]
5568+
pub admin: Signer<'info>,
55775569
pub state: Box<Account<'info, State>>,
55785570
#[account(
55795571
mut,

programs/drift/src/instructions/keeper.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3378,7 +3378,7 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>(
33783378
continue;
33793379
}
33803380

3381-
let cached_info = amm_cache.get_mut(perp_market.market_index as u32);
3381+
let cached_info = amm_cache.get_for_market_index_mut(perp_market.market_index)?;
33823382

33833383
// Early validation checks
33843384
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>(
35943594
if perp_market.lp_status == 0 {
35953595
continue;
35963596
}
3597-
let cached_info = amm_cache.get_mut(perp_market.market_index as u32);
3597+
let cached_info = amm_cache.get_for_market_index_mut(perp_market.market_index)?;
35983598

35993599
validate!(
36003600
perp_market.oracle_id() == cached_info.oracle_id()?,

programs/drift/src/instructions/lp_admin.rs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -922,20 +922,6 @@ pub fn handle_update_initial_amm_cache_info<'c: 'info, 'info>(
922922
Ok(())
923923
}
924924

925-
pub fn handle_reset_amm_cache(ctx: Context<ResetAmmCache>) -> Result<()> {
926-
let state = &ctx.accounts.state;
927-
let amm_cache = &mut ctx.accounts.amm_cache;
928-
929-
amm_cache.cache.clear();
930-
amm_cache
931-
.cache
932-
.resize_with(state.number_of_markets as usize, CacheInfo::default);
933-
amm_cache.validate(state)?;
934-
935-
msg!("AMM cache reset. markets: {}", state.number_of_markets);
936-
Ok(())
937-
}
938-
939925
#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)]
940926
pub struct OverrideAmmCacheParams {
941927
pub quote_owed_from_lp_pool: Option<i64>,

programs/drift/src/instructions/lp_pool.rs

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ use crate::{
4343
},
4444
validate,
4545
};
46+
use std::collections::BTreeMap;
4647
use std::iter::Peekable;
4748
use std::slice::Iter;
4849

@@ -96,31 +97,33 @@ pub fn handle_update_constituent_target_base<'c: 'info, 'info>(
9697
let constituent_map =
9798
ConstituentMap::load(&ConstituentSet::new(), &lp_pool_key, remaining_accounts)?;
9899

99-
let mut amm_inventories: Vec<AmmInventoryAndPricesAndSlots> =
100-
Vec::with_capacity(amm_cache.len() as usize);
100+
let mut amm_inventories: BTreeMap<u16, AmmInventoryAndPricesAndSlots> = BTreeMap::new();
101101
for (_, cache_info) in amm_cache.iter().enumerate() {
102102
if cache_info.lp_status_for_perp_market == 0 {
103103
continue;
104104
}
105105

106-
amm_inventories.push(AmmInventoryAndPricesAndSlots {
107-
inventory: {
108-
let scaled_position = cache_info
109-
.position
110-
.safe_mul(cache_info.amm_position_scalar as i64)?
111-
.safe_div(100)?;
112-
113-
scaled_position.clamp(
114-
-cache_info.amm_inventory_limit,
115-
cache_info.amm_inventory_limit,
116-
)
106+
amm_inventories.insert(
107+
cache_info.market_index,
108+
AmmInventoryAndPricesAndSlots {
109+
inventory: {
110+
let scaled_position = cache_info
111+
.position
112+
.safe_mul(cache_info.amm_position_scalar as i64)?
113+
.safe_div(100)?;
114+
115+
scaled_position.clamp(
116+
-cache_info.amm_inventory_limit,
117+
cache_info.amm_inventory_limit,
118+
)
119+
},
120+
price: cache_info.oracle_price,
121+
last_oracle_slot: cache_info.oracle_slot,
122+
last_position_slot: cache_info.slot,
117123
},
118-
price: cache_info.oracle_price,
119-
last_oracle_slot: cache_info.oracle_slot,
120-
last_position_slot: cache_info.slot,
121-
});
124+
);
122125
}
123-
msg!("amm inventories: {:?}", amm_inventories);
126+
msg!("amm inventories:{:?}", amm_inventories);
124127

125128
if amm_inventories.is_empty() {
126129
msg!("No valid inventories found for constituent target weights update");
@@ -140,7 +143,7 @@ pub fn handle_update_constituent_target_base<'c: 'info, 'info>(
140143

141144
constituent_target_base.update_target_base(
142145
&amm_constituent_mapping,
143-
amm_inventories.as_slice(),
146+
&amm_inventories,
144147
constituent_indexes_and_decimals_and_prices.as_mut_slice(),
145148
slot,
146149
)?;
@@ -1010,7 +1013,7 @@ pub fn handle_lp_pool_remove_liquidity<'c: 'info, 'info>(
10101013
if cache_info.last_fee_pool_token_amount != 0 && cache_info.last_settle_slot != slot {
10111014
msg!(
10121015
"Market {} has not been settled in current slot. Last slot: {}",
1013-
i,
1016+
cache_info.market_index,
10141017
cache_info.last_settle_slot
10151018
);
10161019
return Err(ErrorCode::AMMCacheStale.into());

programs/drift/src/lib.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,10 +1020,16 @@ pub mod drift {
10201020
handle_initialize_amm_cache(ctx)
10211021
}
10221022

1023-
pub fn resize_amm_cache<'c: 'info, 'info>(
1024-
ctx: Context<'_, '_, 'c, 'info, ResizeAmmCache<'info>>,
1023+
pub fn add_market_to_amm_cache<'c: 'info, 'info>(
1024+
ctx: Context<'_, '_, 'c, 'info, AddMarketToAmmCache<'info>>,
10251025
) -> Result<()> {
1026-
handle_resize_amm_cache(ctx)
1026+
handle_add_market_to_amm_cache(ctx)
1027+
}
1028+
1029+
pub fn delete_amm_cache<'c: 'info, 'info>(
1030+
ctx: Context<'_, '_, 'c, 'info, DeleteAmmCache<'info>>,
1031+
) -> Result<()> {
1032+
handle_delete_amm_cache(ctx)
10271033
}
10281034

10291035
pub fn update_initial_amm_cache_info<'c: 'info, 'info>(
@@ -2086,12 +2092,6 @@ pub mod drift {
20862092
handle_override_amm_cache_info(ctx, market_index, override_params)
20872093
}
20882094

2089-
pub fn reset_amm_cache<'c: 'info, 'info>(
2090-
ctx: Context<'_, '_, 'c, 'info, ResetAmmCache<'info>>,
2091-
) -> Result<()> {
2092-
handle_reset_amm_cache(ctx)
2093-
}
2094-
20952095
pub fn lp_pool_swap<'c: 'info, 'info>(
20962096
ctx: Context<'_, '_, 'c, 'info, LPPoolSwap<'info>>,
20972097
in_market_index: u16,

programs/drift/src/state/amm_cache.rs

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@ use crate::state::oracle::MMOraclePriceData;
1010
use crate::state::oracle_map::OracleIdentifier;
1111
use crate::state::perp_market::PerpMarket;
1212
use crate::state::spot_market::{SpotBalance, SpotMarket};
13-
use crate::state::state::State;
1413
use crate::state::traits::Size;
1514
use crate::state::zero_copy::HasLen;
1615
use crate::state::zero_copy::{AccountZeroCopy, AccountZeroCopyMut};
17-
use crate::validate;
1816
use crate::OracleSource;
1917
use crate::{impl_zero_copy_loader, OracleGuardRails};
2018

@@ -53,11 +51,12 @@ pub struct CacheInfo {
5351
pub amm_inventory_limit: i64,
5452
pub oracle_price: i64,
5553
pub oracle_slot: u64,
54+
pub market_index: u16,
5655
pub oracle_source: u8,
5756
pub oracle_validity: u8,
5857
pub lp_status_for_perp_market: u8,
5958
pub amm_position_scalar: u8,
60-
pub _padding: [u8; 36],
59+
pub _padding: [u8; 34],
6160
}
6261

6362
impl Size for CacheInfo {
@@ -86,7 +85,8 @@ impl Default for CacheInfo {
8685
quote_owed_from_lp_pool: 0i64,
8786
lp_status_for_perp_market: 0u8,
8887
amm_position_scalar: 0u8,
89-
_padding: [0u8; 36],
88+
market_index: 0u16,
89+
_padding: [0u8; 34],
9090
}
9191
}
9292
}
@@ -183,15 +183,6 @@ impl AmmCache {
183183
8 + 8 + 4 + num_markets * CacheInfo::SIZE
184184
}
185185

186-
pub fn validate(&self, state: &State) -> DriftResult<()> {
187-
validate!(
188-
self.cache.len() <= state.number_of_markets as usize,
189-
ErrorCode::DefaultError,
190-
"Number of amm positions is no larger than number of markets"
191-
)?;
192-
Ok(())
193-
}
194-
195186
pub fn update_perp_market_fields(&mut self, perp_market: &PerpMarket) -> DriftResult<()> {
196187
let cache_info = self.cache.get_mut(perp_market.market_index as usize);
197188
if let Some(cache_info) = cache_info {
@@ -238,6 +229,15 @@ impl AmmCache {
238229
impl_zero_copy_loader!(AmmCache, crate::id, AmmCacheFixed, CacheInfo);
239230

240231
impl<'a> AccountZeroCopy<'a, CacheInfo, AmmCacheFixed> {
232+
pub fn get_for_market_index(&self, market_index: u16) -> DriftResult<&CacheInfo> {
233+
for cache_info in self.iter() {
234+
if cache_info.market_index == market_index {
235+
return Ok(cache_info);
236+
}
237+
}
238+
Err(ErrorCode::MarketIndexNotFoundAmmCache.into())
239+
}
240+
241241
pub fn check_settle_staleness(&self, slot: u64, threshold_slot_diff: u64) -> DriftResult<()> {
242242
for (i, cache_info) in self.iter().enumerate() {
243243
if cache_info.slot == 0 {
@@ -284,6 +284,27 @@ impl<'a> AccountZeroCopy<'a, CacheInfo, AmmCacheFixed> {
284284
}
285285

286286
impl<'a> AccountZeroCopyMut<'a, CacheInfo, AmmCacheFixed> {
287+
pub fn get_for_market_index_mut(&mut self, market_index: u16) -> DriftResult<&mut CacheInfo> {
288+
let pos = {
289+
let mut found: Option<u32> = None;
290+
for i in 0..self.len() {
291+
let cache_info = self.get(i);
292+
if cache_info.market_index == market_index {
293+
found = Some(i);
294+
break;
295+
}
296+
}
297+
found
298+
};
299+
300+
if let Some(i) = pos {
301+
Ok(self.get_mut(i))
302+
} else {
303+
msg!("Market index not found in amm cache: {}", market_index);
304+
Err(ErrorCode::MarketIndexNotFoundAmmCache.into())
305+
}
306+
}
307+
287308
pub fn update_amount_owed_from_lp_pool(
288309
&mut self,
289310
perp_market: &PerpMarket,

0 commit comments

Comments
 (0)