Skip to content

Commit 8b37dfd

Browse files
vamm uses mm oracle (#1747)
* add offset * working tests * refactor to use MM oracle as its own type * remove weird preface * sdk updates * bankrun tests all pass * fix test * changes and fixes * widen confidence if mm oracle too diff * sdk side for confidence adjust
1 parent 86a0acf commit 8b37dfd

31 files changed

+892
-242
lines changed

programs/drift/src/controller/funding.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,12 @@ pub fn update_funding_rate(
188188
if valid_funding_update {
189189
let oracle_price_data = oracle_map.get_price_data(&market.oracle_id())?;
190190
let sanitize_clamp_denominator = market.get_sanitize_clamp_denominator()?;
191+
let mm_oracle_price_data = market.get_mm_oracle_price_data(*oracle_price_data, slot)?;
191192

192193
let oracle_price_twap = amm::update_oracle_price_twap(
193194
&mut market.amm,
194195
now,
195-
oracle_price_data,
196+
&mm_oracle_price_data,
196197
Some(reserve_price),
197198
sanitize_clamp_denominator,
198199
)?;

programs/drift/src/controller/liquidation.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,11 @@ pub fn liquidate_perp(
203203

204204
let mut market = perp_market_map.get_ref_mut(&market_index)?;
205205
let oracle_price_data = oracle_map.get_price_data(&market.oracle_id())?;
206+
let mm_oracle_price_data = market.get_mm_oracle_price_data(*oracle_price_data, slot)?;
206207

207208
update_amm_and_check_validity(
208209
&mut market,
209-
oracle_price_data,
210+
&mm_oracle_price_data,
210211
state,
211212
now,
212213
slot,
@@ -846,10 +847,11 @@ pub fn liquidate_perp_with_fill(
846847

847848
let mut market = perp_market_map.get_ref_mut(&market_index)?;
848849
let oracle_price_data = oracle_map.get_price_data(&market.oracle_id())?;
850+
let mm_oracle_price_data = market.get_mm_oracle_price_data(*oracle_price_data, slot)?;
849851

850852
update_amm_and_check_validity(
851853
&mut market,
852-
oracle_price_data,
854+
&mm_oracle_price_data,
853855
state,
854856
now,
855857
slot,

programs/drift/src/controller/position/tests.rs

Lines changed: 81 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::math::constants::{
1616
use crate::math::lp::calculate_settle_lp_metrics;
1717
use crate::math::position::swap_direction_to_close_position;
1818
use crate::math::repeg;
19-
use crate::state::oracle::{OraclePriceData, PrelaunchOracle};
19+
use crate::state::oracle::{MMOraclePriceData, OraclePriceData, PrelaunchOracle};
2020
use crate::state::oracle_map::OracleMap;
2121
use crate::state::perp_market::{AMMLiquiditySplit, PerpMarket, AMM};
2222
use crate::state::perp_market_map::PerpMarketMap;
@@ -40,6 +40,7 @@ use crate::test_utils::get_hardcoded_pyth_price;
4040
use crate::QUOTE_PRECISION_I64;
4141
use anchor_lang::prelude::{AccountLoader, Clock};
4242
use anchor_lang::Owner;
43+
use solana_program::clock;
4344
use solana_program::pubkey::Pubkey;
4445
use std::str::FromStr;
4546

@@ -540,6 +541,9 @@ fn amm_pred_market_example() {
540541
delay: 1,
541542
has_sufficient_number_of_data_points: true,
542543
};
544+
let mm_oracle_price_data = perp_market
545+
.get_mm_oracle_price_data(oracle_price_data, clock_slot)
546+
.unwrap();
543547

544548
let (max_bids, max_asks) = calculate_market_open_bids_asks(&perp_market.amm).unwrap();
545549
perp_market.amm.curve_update_intensity = 99;
@@ -550,7 +554,7 @@ fn amm_pred_market_example() {
550554
assert_eq!(perp_market.amm.sqrt_k, 56_649_660_613_272);
551555

552556
let (optimal_peg, fee_budget, _check_lower_bound) =
553-
repeg::calculate_optimal_peg_and_budget(&perp_market, &oracle_price_data).unwrap();
557+
repeg::calculate_optimal_peg_and_budget(&perp_market, &mm_oracle_price_data).unwrap();
554558

555559
assert_eq!(perp_market.amm.terminal_quote_asset_reserve, 56405211622548);
556560
assert_eq!(perp_market.amm.quote_asset_reserve, 56933567973708);
@@ -581,7 +585,7 @@ fn amm_pred_market_example() {
581585

582586
let cost = _update_amm(
583587
&mut perp_market,
584-
&oracle_price_data,
588+
&mm_oracle_price_data,
585589
&state,
586590
now,
587591
clock_slot,
@@ -678,9 +682,12 @@ fn amm_perp_ref_offset() {
678682
delay: 1,
679683
has_sufficient_number_of_data_points: true,
680684
};
685+
let mm_oracle_price_data = perp_market
686+
.get_mm_oracle_price_data(oracle_price_data, clock_slot)
687+
.unwrap();
681688
let cost = _update_amm(
682689
&mut perp_market,
683-
&oracle_price_data,
690+
&mm_oracle_price_data,
684691
&state,
685692
now,
686693
clock_slot,
@@ -705,6 +712,47 @@ fn amm_perp_ref_offset() {
705712
assert_eq!(perp_market.amm.ask_base_asset_reserve, 4672813088646692);
706713

707714
crate::validation::perp_market::validate_perp_market(&perp_market).unwrap();
715+
716+
// Update MM oracle and reference price offset stays the same and is applied to the MM oracle
717+
perp_market.amm.mm_oracle_price = 7200000;
718+
perp_market.amm.mm_oracle_slot = clock_slot;
719+
let mm_oracle_price_data = perp_market
720+
.get_mm_oracle_price_data(oracle_price_data, clock_slot)
721+
.unwrap();
722+
723+
let _ = _update_amm(
724+
&mut perp_market,
725+
&mm_oracle_price_data,
726+
&state,
727+
now,
728+
clock_slot,
729+
);
730+
let reserve_price_mm_offset = perp_market.amm.reserve_price().unwrap();
731+
let (b2, a2) = perp_market
732+
.amm
733+
.bid_ask_price(reserve_price_mm_offset)
734+
.unwrap();
735+
assert_eq!(perp_market.amm.reference_price_offset, 132);
736+
assert_eq!(reserve_price_mm_offset, 7199999);
737+
assert_eq!(b2, 7197349);
738+
assert_eq!(a2, 7204578);
739+
740+
// Uses the original oracle if the slot is old, ignoring MM oracle
741+
perp_market.amm.mm_oracle_price = 7200000;
742+
perp_market.amm.mm_oracle_slot = clock_slot - 100;
743+
let mm_oracle_price = perp_market
744+
.get_mm_oracle_price_data(oracle_price_data, clock_slot)
745+
.unwrap();
746+
747+
let _ = _update_amm(&mut perp_market, &mm_oracle_price, &state, now, clock_slot);
748+
let reserve_price_mm_offset_3 = perp_market.amm.reserve_price().unwrap();
749+
let (b3, a3) = perp_market
750+
.amm
751+
.bid_ask_price(reserve_price_mm_offset_3)
752+
.unwrap();
753+
assert_eq!(reserve_price_mm_offset_3, r);
754+
assert_eq!(b3, b);
755+
assert_eq!(a3, a);
708756
}
709757

710758
#[test]
@@ -1131,15 +1179,11 @@ fn amm_split_large_k_with_rebase() {
11311179
delay: 14,
11321180
has_sufficient_number_of_data_points: true,
11331181
};
1182+
let mm_oracle_price = perp_market
1183+
.get_mm_oracle_price_data(oracle_price_data, clock_slot)
1184+
.unwrap();
11341185

1135-
let cost = _update_amm(
1136-
&mut perp_market,
1137-
&oracle_price_data,
1138-
&state,
1139-
now,
1140-
clock_slot,
1141-
)
1142-
.unwrap();
1186+
let cost = _update_amm(&mut perp_market, &mm_oracle_price, &state, now, clock_slot).unwrap();
11431187
assert_eq!(cost, -3017938);
11441188

11451189
assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 12535655660);
@@ -2238,10 +2282,13 @@ fn update_amm_near_boundary() {
22382282
println!("perp_market: {:?}", perp_market.amm.last_update_slot);
22392283

22402284
let oracle_price_data = oracle_map.get_price_data(&perp_market.oracle_id()).unwrap();
2285+
let mm_oracle_price_data = perp_market
2286+
.get_mm_oracle_price_data(*oracle_price_data, slot)
2287+
.unwrap();
22412288

22422289
let state = State::default();
22432290

2244-
let cost = _update_amm(&mut perp_market, oracle_price_data, &state, now, slot).unwrap();
2291+
let cost = _update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap();
22452292

22462293
assert_eq!(cost, 18803837952);
22472294
}
@@ -2280,10 +2327,13 @@ fn update_amm_near_boundary2() {
22802327
println!("perp_market: {:?}", perp_market.amm.last_update_slot);
22812328

22822329
let oracle_price_data = oracle_map.get_price_data(&perp_market.oracle_id()).unwrap();
2283-
2330+
let mm_oracle_price_data = perp_market
2331+
.get_mm_oracle_price_data(*oracle_price_data, slot)
2332+
.unwrap();
22842333
let state = State::default();
22852334

2286-
let cost: i128 = _update_amm(&mut perp_market, oracle_price_data, &state, now, slot).unwrap();
2335+
let cost: i128 =
2336+
_update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap();
22872337
assert!(perp_market.amm.last_oracle_valid);
22882338
assert_eq!(cost, 2987010);
22892339
}
@@ -2322,10 +2372,13 @@ fn recenter_amm_1() {
23222372
println!("perp_market: {:?}", perp_market.amm.last_update_slot);
23232373

23242374
let oracle_price_data = oracle_map.get_price_data(&perp_market.oracle_id()).unwrap();
2375+
let mm_oracle_price_data = perp_market
2376+
.get_mm_oracle_price_data(*oracle_price_data, slot)
2377+
.unwrap();
23252378

23262379
let state = State::default();
23272380

2328-
let cost = _update_amm(&mut perp_market, oracle_price_data, &state, now, slot).unwrap();
2381+
let cost = _update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap();
23292382

23302383
assert_eq!(cost, 2987010);
23312384

@@ -2422,10 +2475,15 @@ fn recenter_amm_2() {
24222475
let oracle_price_data = oracle_map
24232476
.get_price_data(&(oracle_price_key, OracleSource::Pyth))
24242477
.unwrap();
2478+
let mm_oracle_price_data = MMOraclePriceData {
2479+
mm_oracle_price: oracle_price_data.price,
2480+
mm_oracle_delay: oracle_price_data.delay + 1,
2481+
oracle_price_data: *oracle_price_data,
2482+
};
24252483

24262484
let state = State::default();
24272485

2428-
let cost = _update_amm(&mut perp_market, oracle_price_data, &state, now, slot).unwrap();
2486+
let cost = _update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap();
24292487

24302488
assert_eq!(cost, 0);
24312489

@@ -2551,10 +2609,15 @@ fn test_move_amm() {
25512609
let oracle_price_data = oracle_map
25522610
.get_price_data(&(oracle_price_key, OracleSource::Pyth))
25532611
.unwrap();
2612+
let mm_oracle_price_data = MMOraclePriceData {
2613+
mm_oracle_price: oracle_price_data.price,
2614+
mm_oracle_delay: oracle_price_data.delay + 1,
2615+
oracle_price_data: *oracle_price_data,
2616+
};
25542617

25552618
let state = State::default();
25562619

2557-
let cost = _update_amm(&mut perp_market, oracle_price_data, &state, now, slot).unwrap();
2620+
let cost = _update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap();
25582621

25592622
assert_eq!(cost, 0);
25602623

programs/drift/src/controller/repeg.rs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::cmp::min;
22

33
use crate::msg;
4+
use crate::state::oracle::MMOraclePriceData;
45
use anchor_lang::prelude::AccountInfo;
56
use anchor_lang::prelude::*;
67

@@ -24,7 +25,7 @@ use crate::math::repeg;
2425
use crate::math::safe_math::SafeMath;
2526
use crate::math::spot_balance::get_token_amount;
2627

27-
use crate::state::oracle::{OraclePriceData, OracleSource};
28+
use crate::state::oracle::OracleSource;
2829
use crate::state::oracle_map::OracleMap;
2930
use crate::state::perp_market::{MarketStatus, PerpMarket};
3031
use crate::state::perp_market_map::PerpMarketMap;
@@ -110,8 +111,11 @@ pub fn update_amms(
110111
let updated = true; // todo
111112
for (_key, market_account_loader) in perp_market_map.0.iter_mut() {
112113
let market = &mut load_mut!(market_account_loader)?;
113-
let oracle_price_data = &oracle_map.get_price_data(&market.oracle_id())?;
114-
_update_amm(market, oracle_price_data, state, now, clock_slot)?;
114+
let oracle_price_data = oracle_map.get_price_data(&market.oracle_id())?;
115+
let mm_oracle_price_data =
116+
market.get_mm_oracle_price_data(*oracle_price_data, clock_slot)?;
117+
118+
_update_amm(market, &mm_oracle_price_data, state, now, clock_slot)?;
115119
}
116120

117121
Ok(updated)
@@ -126,10 +130,11 @@ pub fn update_amm(
126130
) -> DriftResult<i128> {
127131
let market = &mut perp_market_map.get_ref_mut(&market_index)?;
128132
let oracle_price_data = oracle_map.get_price_data(&market.oracle_id())?;
133+
let mm_oracle_price_data = market.get_mm_oracle_price_data(*oracle_price_data, clock.slot)?;
129134

130135
let cost_of_update = _update_amm(
131136
market,
132-
oracle_price_data,
137+
&mm_oracle_price_data,
133138
state,
134139
clock.unix_timestamp,
135140
clock.slot,
@@ -140,7 +145,7 @@ pub fn update_amm(
140145

141146
pub fn _update_amm(
142147
market: &mut PerpMarket,
143-
oracle_price_data: &OraclePriceData,
148+
mm_oracle_price_data: &MMOraclePriceData,
144149
state: &State,
145150
now: i64,
146151
clock_slot: u64,
@@ -156,7 +161,7 @@ pub fn _update_amm(
156161
MarketType::Perp,
157162
market.market_index,
158163
market.amm.historical_oracle_data.last_oracle_price_twap,
159-
oracle_price_data,
164+
&mm_oracle_price_data.oracle_price_data,
160165
&state.oracle_guard_rails.validity,
161166
market.get_max_confidence_interval_multiplier()?,
162167
&market.amm.oracle_source,
@@ -172,7 +177,7 @@ pub fn _update_amm(
172177

173178
if curve_update_intensity > 0 {
174179
let (optimal_peg, fee_budget, check_lower_bound) =
175-
repeg::calculate_optimal_peg_and_budget(market, oracle_price_data)?;
180+
repeg::calculate_optimal_peg_and_budget(market, mm_oracle_price_data)?;
176181

177182
let (repegged_market, repegged_cost) = repeg::adjust_amm(
178183
market,
@@ -208,7 +213,7 @@ pub fn _update_amm(
208213
amm::update_oracle_price_twap(
209214
&mut market.amm,
210215
now,
211-
oracle_price_data,
216+
mm_oracle_price_data,
212217
Some(reserve_price_after),
213218
sanitize_clamp_denominator,
214219
)?;
@@ -230,13 +235,13 @@ pub fn _update_amm(
230235

231236
pub fn update_amm_and_check_validity(
232237
market: &mut PerpMarket,
233-
oracle_price_data: &OraclePriceData,
238+
mm_oracle_price_data: &MMOraclePriceData,
234239
state: &State,
235240
now: i64,
236241
clock_slot: u64,
237242
action: Option<DriftAction>,
238243
) -> DriftResult {
239-
_update_amm(market, oracle_price_data, state, now, clock_slot)?;
244+
_update_amm(market, mm_oracle_price_data, state, now, clock_slot)?;
240245

241246
// 1 hour EMA
242247
let risk_ema_price = market.amm.historical_oracle_data.last_oracle_price_twap;
@@ -245,7 +250,7 @@ pub fn update_amm_and_check_validity(
245250
MarketType::Perp,
246251
market.market_index,
247252
risk_ema_price,
248-
oracle_price_data,
253+
&mm_oracle_price_data.oracle_price_data,
249254
&state.oracle_guard_rails.validity,
250255
market.get_max_confidence_interval_multiplier()?,
251256
&market.amm.oracle_source,
@@ -257,7 +262,7 @@ pub fn update_amm_and_check_validity(
257262
is_oracle_valid_for_action(oracle_validity, action)?,
258263
ErrorCode::InvalidOracle,
259264
"Invalid Oracle ({:?} vs ema={:?}) for perp market index={} and action={:?}",
260-
oracle_price_data,
265+
mm_oracle_price_data.oracle_price_data,
261266
risk_ema_price,
262267
market.market_index,
263268
action

0 commit comments

Comments
 (0)