From 3c096c81d81a93aa79d63acd298da383c5eb5d98 Mon Sep 17 00:00:00 2001 From: 0xahzam Date: Sat, 29 Nov 2025 13:39:28 +0530 Subject: [PATCH 1/5] add perp price overrides to margin check --- src/margin.rs | 33 +++++++++++++++++++++++++++++---- src/types.rs | 3 +++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/margin.rs b/src/margin.rs index 4897449..3f19729 100644 --- a/src/margin.rs +++ b/src/margin.rs @@ -12,7 +12,7 @@ use drift_program::{ spot_balance::get_strict_token_value, }, state::{ - oracle::StrictOraclePrice, + oracle::{OraclePriceData, StrictOraclePrice}, spot_market::SpotBalanceType, user::{OrderFillSimulation, PerpPosition, SpotPosition, User}, }, @@ -20,7 +20,7 @@ use drift_program::{ // This is a mathematical abstraction of the Drift Protocol margin system // Reuses existing type definitions while removing Solana-specific abstractions -use crate::types::MarketState; +use crate::types::{MarketState, PerpPriceOverrides}; // Core margin calculation result #[repr(C, align(16))] @@ -63,6 +63,22 @@ pub fn calculate_simplified_margin_requirement( market_state: &MarketState, margin_type: MarginRequirementType, margin_buffer: u32, +) -> SimplifiedMarginCalculation { + calculate_simplified_margin_requirement_with_overrides( + user, + market_state, + margin_type, + margin_buffer, + None, + ) +} + +pub fn calculate_simplified_margin_requirement_with_overrides( + user: &User, + market_state: &MarketState, + margin_type: MarginRequirementType, + margin_buffer: u32, + perp_pyth_price_overrides: Option<&PerpPriceOverrides>, ) -> SimplifiedMarginCalculation { let user_high_leverage_mode = user.is_high_leverage_mode(margin_type); let mut total_collateral = 0i128; @@ -186,7 +202,16 @@ pub fn calculate_simplified_margin_requirement( } let perp_market = market_state.get_perp_market(perp_position.market_index); - let oracle_price = market_state.get_perp_oracle_price(perp_position.market_index); + let oracle_price = perp_pyth_price_overrides + .and_then(|map| map.get(&perp_position.market_index)) + .map(|&price| OraclePriceData { + price: price as i64, + confidence: 0, + delay: 0, + has_sufficient_number_of_data_points: true, + sequence_id: None, + }) + .unwrap_or_else(|| *market_state.get_perp_oracle_price(perp_position.market_index)); let strict_quote_price = { let quote_price_data = @@ -213,7 +238,7 @@ pub fn calculate_simplified_margin_requirement( ) = calculate_perp_position_value_and_pnl( perp_position, perp_market, - oracle_price, + &oracle_price, &strict_quote_price, margin_type, user_custom_margin_ratio.max(perp_position_custom_margin_ratio), diff --git a/src/types.rs b/src/types.rs index 5705a8f..d114998 100644 --- a/src/types.rs +++ b/src/types.rs @@ -262,3 +262,6 @@ impl MarketState { self.perp_oracle_prices.insert(market_index, price_data); } } + +/// Market Index -> Price +pub type PerpPriceOverrides = HashMap; From cda1a96b44bf9938a0cd5a10bab1ddf062d4afb4 Mon Sep 17 00:00:00 2001 From: 0xahzam Date: Sat, 29 Nov 2025 13:59:43 +0530 Subject: [PATCH 2/5] add export for margin calc with overrides --- src/exports.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/exports.rs b/src/exports.rs index 29d32ad..038fcd5 100644 --- a/src/exports.rs +++ b/src/exports.rs @@ -37,7 +37,7 @@ use crate::{ types::{ compat::{self}, AccountsList, FfiResult, MMOraclePriceData, MarginCalculation, MarginContextMode, - MarketState, + MarketState, PerpPriceOverrides, }, }; @@ -466,6 +466,25 @@ pub extern "C" fn margin_calculate_simplified_margin_requirement( FfiResult::ROk(result) } +#[no_mangle] +pub extern "C" fn margin_calculate_simplified_margin_requirement_with_overrides( + user: &User, + market_state: &MarketState, + margin_type: MarginRequirementType, + margin_buffer: u32, + perp_pyth_price_overrides: Option<&PerpPriceOverrides>, +) -> FfiResult { + let result = crate::margin::calculate_simplified_margin_requirement_with_overrides( + user, + market_state, + margin_type, + margin_buffer, + perp_pyth_price_overrides, + ); + + FfiResult::ROk(result) +} + #[no_mangle] pub extern "C" fn incremental_margin_calculation_from_user( user: &User, From 674b07d1be4d0340939efdce688822294bf919e4 Mon Sep 17 00:00:00 2001 From: 0xahzam Date: Mon, 1 Dec 2025 15:20:16 +0530 Subject: [PATCH 3/5] refactor --- src/exports.rs | 21 +-------------------- src/margin.rs | 43 +++++++++++++++---------------------------- src/types.rs | 20 +++++++++++++++++--- 3 files changed, 33 insertions(+), 51 deletions(-) diff --git a/src/exports.rs b/src/exports.rs index 038fcd5..29d32ad 100644 --- a/src/exports.rs +++ b/src/exports.rs @@ -37,7 +37,7 @@ use crate::{ types::{ compat::{self}, AccountsList, FfiResult, MMOraclePriceData, MarginCalculation, MarginContextMode, - MarketState, PerpPriceOverrides, + MarketState, }, }; @@ -466,25 +466,6 @@ pub extern "C" fn margin_calculate_simplified_margin_requirement( FfiResult::ROk(result) } -#[no_mangle] -pub extern "C" fn margin_calculate_simplified_margin_requirement_with_overrides( - user: &User, - market_state: &MarketState, - margin_type: MarginRequirementType, - margin_buffer: u32, - perp_pyth_price_overrides: Option<&PerpPriceOverrides>, -) -> FfiResult { - let result = crate::margin::calculate_simplified_margin_requirement_with_overrides( - user, - market_state, - margin_type, - margin_buffer, - perp_pyth_price_overrides, - ); - - FfiResult::ROk(result) -} - #[no_mangle] pub extern "C" fn incremental_margin_calculation_from_user( user: &User, diff --git a/src/margin.rs b/src/margin.rs index 3f19729..6992c4f 100644 --- a/src/margin.rs +++ b/src/margin.rs @@ -12,7 +12,7 @@ use drift_program::{ spot_balance::get_strict_token_value, }, state::{ - oracle::{OraclePriceData, StrictOraclePrice}, + oracle::StrictOraclePrice, spot_market::SpotBalanceType, user::{OrderFillSimulation, PerpPosition, SpotPosition, User}, }, @@ -20,7 +20,7 @@ use drift_program::{ // This is a mathematical abstraction of the Drift Protocol margin system // Reuses existing type definitions while removing Solana-specific abstractions -use crate::types::{MarketState, PerpPriceOverrides}; +use crate::types::MarketState; // Core margin calculation result #[repr(C, align(16))] @@ -63,22 +63,6 @@ pub fn calculate_simplified_margin_requirement( market_state: &MarketState, margin_type: MarginRequirementType, margin_buffer: u32, -) -> SimplifiedMarginCalculation { - calculate_simplified_margin_requirement_with_overrides( - user, - market_state, - margin_type, - margin_buffer, - None, - ) -} - -pub fn calculate_simplified_margin_requirement_with_overrides( - user: &User, - market_state: &MarketState, - margin_type: MarginRequirementType, - margin_buffer: u32, - perp_pyth_price_overrides: Option<&PerpPriceOverrides>, ) -> SimplifiedMarginCalculation { let user_high_leverage_mode = user.is_high_leverage_mode(margin_type); let mut total_collateral = 0i128; @@ -202,16 +186,19 @@ pub fn calculate_simplified_margin_requirement_with_overrides( } let perp_market = market_state.get_perp_market(perp_position.market_index); - let oracle_price = perp_pyth_price_overrides - .and_then(|map| map.get(&perp_position.market_index)) - .map(|&price| OraclePriceData { - price: price as i64, - confidence: 0, - delay: 0, - has_sufficient_number_of_data_points: true, - sequence_id: None, - }) - .unwrap_or_else(|| *market_state.get_perp_oracle_price(perp_position.market_index)); + let oracle_price = match market_state.get_perp_pyth_price(perp_position.market_index) { + Some(pyth_price) => { + let oracle = market_state.get_perp_oracle_price(perp_position.market_index); + let diff_bps = (pyth_price.price.abs_diff(oracle.price) * 10_000) + / oracle.price.unsigned_abs(); + if diff_bps > 5 { + pyth_price + } else { + *oracle + } + } + None => *market_state.get_perp_oracle_price(perp_position.market_index), + }; let strict_quote_price = { let quote_price_data = diff --git a/src/types.rs b/src/types.rs index d114998..760259d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -227,6 +227,7 @@ pub struct MarketState { pub perp_markets: HashMap, pub spot_oracle_prices: HashMap, pub perp_oracle_prices: HashMap, + pub perp_pyth_prices: HashMap, // Override with pyth price } impl MarketState { @@ -246,6 +247,18 @@ impl MarketState { self.perp_oracle_prices.get(&market_index).unwrap() } + pub fn get_perp_pyth_price(&self, market_index: u16) -> Option { + self.perp_pyth_prices + .get(&market_index) + .map(|&price| OraclePriceData { + price, + confidence: 0, + delay: 0, + has_sufficient_number_of_data_points: true, + sequence_id: None, + }) + } + pub fn set_spot_market(&mut self, market: SpotMarket) { self.spot_markets.insert(market.market_index, market); } @@ -261,7 +274,8 @@ impl MarketState { pub fn set_perp_oracle_price(&mut self, market_index: u16, price_data: OraclePriceData) { self.perp_oracle_prices.insert(market_index, price_data); } -} -/// Market Index -> Price -pub type PerpPriceOverrides = HashMap; + pub fn set_perp_pyth_price(&mut self, market_index: u16, price_data: i64) { + self.perp_pyth_prices.insert(market_index, price_data); + } +} From ade3ebbc021993f5fa23b202b2ce13aefa9b81db Mon Sep 17 00:00:00 2001 From: 0xahzam Date: Mon, 1 Dec 2025 18:14:35 +0530 Subject: [PATCH 4/5] add spot pyth price market state --- src/margin.rs | 14 +++++++++++++- src/types.rs | 19 ++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/margin.rs b/src/margin.rs index 6992c4f..f4f1534 100644 --- a/src/margin.rs +++ b/src/margin.rs @@ -85,7 +85,19 @@ pub fn calculate_simplified_margin_requirement( } let spot_market = market_state.get_spot_market(spot_position.market_index); - let oracle_price = market_state.get_spot_oracle_price(spot_position.market_index); + let oracle_price = match market_state.get_spot_pyth_price(spot_position.market_index) { + Some(pyth_price) => { + let oracle = market_state.get_spot_oracle_price(spot_position.market_index); + let diff_bps = (pyth_price.price.abs_diff(oracle.price) * 10_000) + / oracle.price.unsigned_abs(); + if diff_bps > 5 { + pyth_price + } else { + *oracle + } + } + None => *market_state.get_spot_oracle_price(spot_position.market_index), + }; let signed_token_amount = spot_position.get_signed_token_amount(spot_market).unwrap(); diff --git a/src/types.rs b/src/types.rs index 760259d..99d3df6 100644 --- a/src/types.rs +++ b/src/types.rs @@ -227,7 +227,8 @@ pub struct MarketState { pub perp_markets: HashMap, pub spot_oracle_prices: HashMap, pub perp_oracle_prices: HashMap, - pub perp_pyth_prices: HashMap, // Override with pyth price + pub spot_pyth_prices: HashMap, // Override spot with pyth price + pub perp_pyth_prices: HashMap, // Override perp with pyth price } impl MarketState { @@ -247,6 +248,18 @@ impl MarketState { self.perp_oracle_prices.get(&market_index).unwrap() } + pub fn get_spot_pyth_price(&self, market_index: u16) -> Option { + self.spot_pyth_prices + .get(&market_index) + .map(|&price| OraclePriceData { + price, + confidence: 0, + delay: 0, + has_sufficient_number_of_data_points: true, + sequence_id: None, + }) + } + pub fn get_perp_pyth_price(&self, market_index: u16) -> Option { self.perp_pyth_prices .get(&market_index) @@ -275,6 +288,10 @@ impl MarketState { self.perp_oracle_prices.insert(market_index, price_data); } + pub fn set_spot_pyth_price(&mut self, market_index: u16, price_data: i64) { + self.spot_pyth_prices.insert(market_index, price_data); + } + pub fn set_perp_pyth_price(&mut self, market_index: u16, price_data: i64) { self.perp_pyth_prices.insert(market_index, price_data); } From 616d5211dccb8531e399011cb37cc913066849f8 Mon Sep 17 00:00:00 2001 From: 0xahzam Date: Tue, 2 Dec 2025 11:36:16 +0530 Subject: [PATCH 5/5] add threshold param --- src/margin.rs | 4 ++-- src/types.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/margin.rs b/src/margin.rs index f4f1534..cb9519e 100644 --- a/src/margin.rs +++ b/src/margin.rs @@ -90,7 +90,7 @@ pub fn calculate_simplified_margin_requirement( let oracle = market_state.get_spot_oracle_price(spot_position.market_index); let diff_bps = (pyth_price.price.abs_diff(oracle.price) * 10_000) / oracle.price.unsigned_abs(); - if diff_bps > 5 { + if diff_bps > market_state.pyth_oracle_diff_threshold_bps { pyth_price } else { *oracle @@ -203,7 +203,7 @@ pub fn calculate_simplified_margin_requirement( let oracle = market_state.get_perp_oracle_price(perp_position.market_index); let diff_bps = (pyth_price.price.abs_diff(oracle.price) * 10_000) / oracle.price.unsigned_abs(); - if diff_bps > 5 { + if diff_bps > market_state.pyth_oracle_diff_threshold_bps { pyth_price } else { *oracle diff --git a/src/types.rs b/src/types.rs index 99d3df6..b148686 100644 --- a/src/types.rs +++ b/src/types.rs @@ -229,6 +229,7 @@ pub struct MarketState { pub perp_oracle_prices: HashMap, pub spot_pyth_prices: HashMap, // Override spot with pyth price pub perp_pyth_prices: HashMap, // Override perp with pyth price + pub pyth_oracle_diff_threshold_bps: u64, // Min bps diff to prefer pyth price over oracle. Defaults to 0 (always use pyth when set). } impl MarketState {