Skip to content

vamm uses mm oracle #1747

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jul 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion programs/drift/src/controller/funding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,11 +188,12 @@ pub fn update_funding_rate(
if valid_funding_update {
let oracle_price_data = oracle_map.get_price_data(&market.oracle_id())?;
let sanitize_clamp_denominator = market.get_sanitize_clamp_denominator()?;
let mm_oracle_price_data = market.get_mm_oracle_price_data(*oracle_price_data, slot)?;

let oracle_price_twap = amm::update_oracle_price_twap(
&mut market.amm,
now,
oracle_price_data,
&mm_oracle_price_data,
Some(reserve_price),
sanitize_clamp_denominator,
)?;
Expand Down
6 changes: 4 additions & 2 deletions programs/drift/src/controller/liquidation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,11 @@ pub fn liquidate_perp(

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

update_amm_and_check_validity(
&mut market,
oracle_price_data,
&mm_oracle_price_data,
state,
now,
slot,
Expand Down Expand Up @@ -846,10 +847,11 @@ pub fn liquidate_perp_with_fill(

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

update_amm_and_check_validity(
&mut market,
oracle_price_data,
&mm_oracle_price_data,
state,
now,
slot,
Expand Down
99 changes: 81 additions & 18 deletions programs/drift/src/controller/position/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::math::constants::{
use crate::math::lp::calculate_settle_lp_metrics;
use crate::math::position::swap_direction_to_close_position;
use crate::math::repeg;
use crate::state::oracle::{OraclePriceData, PrelaunchOracle};
use crate::state::oracle::{MMOraclePriceData, OraclePriceData, PrelaunchOracle};
use crate::state::oracle_map::OracleMap;
use crate::state::perp_market::{AMMLiquiditySplit, PerpMarket, AMM};
use crate::state::perp_market_map::PerpMarketMap;
Expand All @@ -40,6 +40,7 @@ use crate::test_utils::get_hardcoded_pyth_price;
use crate::QUOTE_PRECISION_I64;
use anchor_lang::prelude::{AccountLoader, Clock};
use anchor_lang::Owner;
use solana_program::clock;
use solana_program::pubkey::Pubkey;
use std::str::FromStr;

Expand Down Expand Up @@ -540,6 +541,9 @@ fn amm_pred_market_example() {
delay: 1,
has_sufficient_number_of_data_points: true,
};
let mm_oracle_price_data = perp_market
.get_mm_oracle_price_data(oracle_price_data, clock_slot)
.unwrap();

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

let (optimal_peg, fee_budget, _check_lower_bound) =
repeg::calculate_optimal_peg_and_budget(&perp_market, &oracle_price_data).unwrap();
repeg::calculate_optimal_peg_and_budget(&perp_market, &mm_oracle_price_data).unwrap();

assert_eq!(perp_market.amm.terminal_quote_asset_reserve, 56405211622548);
assert_eq!(perp_market.amm.quote_asset_reserve, 56933567973708);
Expand Down Expand Up @@ -581,7 +585,7 @@ fn amm_pred_market_example() {

let cost = _update_amm(
&mut perp_market,
&oracle_price_data,
&mm_oracle_price_data,
&state,
now,
clock_slot,
Expand Down Expand Up @@ -678,9 +682,12 @@ fn amm_perp_ref_offset() {
delay: 1,
has_sufficient_number_of_data_points: true,
};
let mm_oracle_price_data = perp_market
.get_mm_oracle_price_data(oracle_price_data, clock_slot)
.unwrap();
let cost = _update_amm(
&mut perp_market,
&oracle_price_data,
&mm_oracle_price_data,
&state,
now,
clock_slot,
Expand All @@ -705,6 +712,47 @@ fn amm_perp_ref_offset() {
assert_eq!(perp_market.amm.ask_base_asset_reserve, 4672813088646692);

crate::validation::perp_market::validate_perp_market(&perp_market).unwrap();

// Update MM oracle and reference price offset stays the same and is applied to the MM oracle
perp_market.amm.mm_oracle_price = 7200000;
perp_market.amm.mm_oracle_slot = clock_slot;
let mm_oracle_price_data = perp_market
.get_mm_oracle_price_data(oracle_price_data, clock_slot)
.unwrap();

let _ = _update_amm(
&mut perp_market,
&mm_oracle_price_data,
&state,
now,
clock_slot,
);
let reserve_price_mm_offset = perp_market.amm.reserve_price().unwrap();
let (b2, a2) = perp_market
.amm
.bid_ask_price(reserve_price_mm_offset)
.unwrap();
assert_eq!(perp_market.amm.reference_price_offset, 132);
assert_eq!(reserve_price_mm_offset, 7199999);
assert_eq!(b2, 7197349);
assert_eq!(a2, 7204578);

// Uses the original oracle if the slot is old, ignoring MM oracle
perp_market.amm.mm_oracle_price = 7200000;
perp_market.amm.mm_oracle_slot = clock_slot - 100;
let mm_oracle_price = perp_market
.get_mm_oracle_price_data(oracle_price_data, clock_slot)
.unwrap();

let _ = _update_amm(&mut perp_market, &mm_oracle_price, &state, now, clock_slot);
let reserve_price_mm_offset_3 = perp_market.amm.reserve_price().unwrap();
let (b3, a3) = perp_market
.amm
.bid_ask_price(reserve_price_mm_offset_3)
.unwrap();
assert_eq!(reserve_price_mm_offset_3, r);
assert_eq!(b3, b);
assert_eq!(a3, a);
}

#[test]
Expand Down Expand Up @@ -1131,15 +1179,11 @@ fn amm_split_large_k_with_rebase() {
delay: 14,
has_sufficient_number_of_data_points: true,
};
let mm_oracle_price = perp_market
.get_mm_oracle_price_data(oracle_price_data, clock_slot)
.unwrap();

let cost = _update_amm(
&mut perp_market,
&oracle_price_data,
&state,
now,
clock_slot,
)
.unwrap();
let cost = _update_amm(&mut perp_market, &mm_oracle_price, &state, now, clock_slot).unwrap();
assert_eq!(cost, -3017938);

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

let oracle_price_data = oracle_map.get_price_data(&perp_market.oracle_id()).unwrap();
let mm_oracle_price_data = perp_market
.get_mm_oracle_price_data(*oracle_price_data, slot)
.unwrap();

let state = State::default();

let cost = _update_amm(&mut perp_market, oracle_price_data, &state, now, slot).unwrap();
let cost = _update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap();

assert_eq!(cost, 18803837952);
}
Expand Down Expand Up @@ -2280,10 +2327,13 @@ fn update_amm_near_boundary2() {
println!("perp_market: {:?}", perp_market.amm.last_update_slot);

let oracle_price_data = oracle_map.get_price_data(&perp_market.oracle_id()).unwrap();

let mm_oracle_price_data = perp_market
.get_mm_oracle_price_data(*oracle_price_data, slot)
.unwrap();
let state = State::default();

let cost: i128 = _update_amm(&mut perp_market, oracle_price_data, &state, now, slot).unwrap();
let cost: i128 =
_update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap();
assert!(perp_market.amm.last_oracle_valid);
assert_eq!(cost, 2987010);
}
Expand Down Expand Up @@ -2322,10 +2372,13 @@ fn recenter_amm_1() {
println!("perp_market: {:?}", perp_market.amm.last_update_slot);

let oracle_price_data = oracle_map.get_price_data(&perp_market.oracle_id()).unwrap();
let mm_oracle_price_data = perp_market
.get_mm_oracle_price_data(*oracle_price_data, slot)
.unwrap();

let state = State::default();

let cost = _update_amm(&mut perp_market, oracle_price_data, &state, now, slot).unwrap();
let cost = _update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap();

assert_eq!(cost, 2987010);

Expand Down Expand Up @@ -2422,10 +2475,15 @@ fn recenter_amm_2() {
let oracle_price_data = oracle_map
.get_price_data(&(oracle_price_key, OracleSource::Pyth))
.unwrap();
let mm_oracle_price_data = MMOraclePriceData {
mm_oracle_price: oracle_price_data.price,
mm_oracle_delay: oracle_price_data.delay + 1,
oracle_price_data: *oracle_price_data,
};

let state = State::default();

let cost = _update_amm(&mut perp_market, oracle_price_data, &state, now, slot).unwrap();
let cost = _update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap();

assert_eq!(cost, 0);

Expand Down Expand Up @@ -2551,10 +2609,15 @@ fn test_move_amm() {
let oracle_price_data = oracle_map
.get_price_data(&(oracle_price_key, OracleSource::Pyth))
.unwrap();
let mm_oracle_price_data = MMOraclePriceData {
mm_oracle_price: oracle_price_data.price,
mm_oracle_delay: oracle_price_data.delay + 1,
oracle_price_data: *oracle_price_data,
};

let state = State::default();

let cost = _update_amm(&mut perp_market, oracle_price_data, &state, now, slot).unwrap();
let cost = _update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap();

assert_eq!(cost, 0);

Expand Down
29 changes: 17 additions & 12 deletions programs/drift/src/controller/repeg.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::cmp::min;

use crate::msg;
use crate::state::oracle::MMOraclePriceData;
use anchor_lang::prelude::AccountInfo;
use anchor_lang::prelude::*;

Expand All @@ -24,7 +25,7 @@ use crate::math::repeg;
use crate::math::safe_math::SafeMath;
use crate::math::spot_balance::get_token_amount;

use crate::state::oracle::{OraclePriceData, OracleSource};
use crate::state::oracle::OracleSource;
use crate::state::oracle_map::OracleMap;
use crate::state::perp_market::{MarketStatus, PerpMarket};
use crate::state::perp_market_map::PerpMarketMap;
Expand Down Expand Up @@ -110,8 +111,11 @@ pub fn update_amms(
let updated = true; // todo
for (_key, market_account_loader) in perp_market_map.0.iter_mut() {
let market = &mut load_mut!(market_account_loader)?;
let oracle_price_data = &oracle_map.get_price_data(&market.oracle_id())?;
_update_amm(market, oracle_price_data, state, now, clock_slot)?;
let oracle_price_data = oracle_map.get_price_data(&market.oracle_id())?;
let mm_oracle_price_data =
market.get_mm_oracle_price_data(*oracle_price_data, clock_slot)?;

_update_amm(market, &mm_oracle_price_data, state, now, clock_slot)?;
}

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

let cost_of_update = _update_amm(
market,
oracle_price_data,
&mm_oracle_price_data,
state,
clock.unix_timestamp,
clock.slot,
Expand All @@ -140,7 +145,7 @@ pub fn update_amm(

pub fn _update_amm(
market: &mut PerpMarket,
oracle_price_data: &OraclePriceData,
mm_oracle_price_data: &MMOraclePriceData,
state: &State,
now: i64,
clock_slot: u64,
Expand All @@ -156,7 +161,7 @@ pub fn _update_amm(
MarketType::Perp,
market.market_index,
market.amm.historical_oracle_data.last_oracle_price_twap,
oracle_price_data,
&mm_oracle_price_data.oracle_price_data,
&state.oracle_guard_rails.validity,
market.get_max_confidence_interval_multiplier()?,
&market.amm.oracle_source,
Expand All @@ -172,7 +177,7 @@ pub fn _update_amm(

if curve_update_intensity > 0 {
let (optimal_peg, fee_budget, check_lower_bound) =
repeg::calculate_optimal_peg_and_budget(market, oracle_price_data)?;
repeg::calculate_optimal_peg_and_budget(market, mm_oracle_price_data)?;

let (repegged_market, repegged_cost) = repeg::adjust_amm(
market,
Expand Down Expand Up @@ -208,7 +213,7 @@ pub fn _update_amm(
amm::update_oracle_price_twap(
&mut market.amm,
now,
oracle_price_data,
mm_oracle_price_data,
Some(reserve_price_after),
sanitize_clamp_denominator,
)?;
Expand All @@ -230,13 +235,13 @@ pub fn _update_amm(

pub fn update_amm_and_check_validity(
market: &mut PerpMarket,
oracle_price_data: &OraclePriceData,
mm_oracle_price_data: &MMOraclePriceData,
state: &State,
now: i64,
clock_slot: u64,
action: Option<DriftAction>,
) -> DriftResult {
_update_amm(market, oracle_price_data, state, now, clock_slot)?;
_update_amm(market, mm_oracle_price_data, state, now, clock_slot)?;

// 1 hour EMA
let risk_ema_price = market.amm.historical_oracle_data.last_oracle_price_twap;
Expand All @@ -245,7 +250,7 @@ pub fn update_amm_and_check_validity(
MarketType::Perp,
market.market_index,
risk_ema_price,
oracle_price_data,
&mm_oracle_price_data.oracle_price_data,
&state.oracle_guard_rails.validity,
market.get_max_confidence_interval_multiplier()?,
&market.amm.oracle_source,
Expand All @@ -257,7 +262,7 @@ pub fn update_amm_and_check_validity(
is_oracle_valid_for_action(oracle_validity, action)?,
ErrorCode::InvalidOracle,
"Invalid Oracle ({:?} vs ema={:?}) for perp market index={} and action={:?}",
oracle_price_data,
mm_oracle_price_data.oracle_price_data,
risk_ema_price,
market.market_index,
action
Expand Down
Loading