Skip to content

Commit

Permalink
program: add back switchboard with no dependency (#943)
Browse files Browse the repository at this point in the history
* copy switchboard state

* add back switchboard

* add test watching cus for place orders

* sdk: account for switchboard max confidence

* CHANGELOG
  • Loading branch information
crispheaney committed Mar 8, 2024
1 parent 4c27396 commit 3a97a01
Show file tree
Hide file tree
Showing 16 changed files with 8,960 additions and 56 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Features

- program: add back switchboard without cargo dependency ([#943](https://github.com/drift-labs/protocol-v2/pull/943))
- program: add admin fn to update funding period
- program: add prelaunch oracles ([#910](https://github.com/drift-labs/protocol-v2/pull/910))
- program: make isolated perp contract tier more ergonomic ([#913](https://github.com/drift-labs/protocol-v2/pull/913))
Expand Down
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions programs/drift/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ phoenix-v1 = { git = "https://github.com/drift-labs/phoenix-v1", rev = "bf6b84",
solana-security-txt = "1.1.0"
static_assertions = "1.1.0"
drift-macros = { git = "https://github.com/drift-labs/drift-macros.git", rev = "c57d87" }
switchboard = { path = "../switchboard", features = ["no-entrypoint"] }

[dev-dependencies]
bytes = "1.2.0"
Expand Down
5 changes: 5 additions & 0 deletions programs/drift/src/ids.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ pub mod pyth_program {
declare_id!("gSbePebfvPy7tRqimPoVecS2UsBvYv46ynrzWocc92s");
}

pub mod switchboard_program {
use solana_program::declare_id;
declare_id!("SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f");
}

pub mod bonk_oracle {
use solana_program::declare_id;
#[cfg(feature = "mainnet-beta")]
Expand Down
14 changes: 10 additions & 4 deletions programs/drift/src/instructions/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ use crate::state::fulfillment_params::serum::SerumContext;
use crate::state::fulfillment_params::serum::SerumV3FulfillmentConfig;
use crate::state::insurance_fund_stake::ProtocolIfSharesTransferConfig;
use crate::state::oracle::{
get_oracle_price, get_prelaunch_price, get_pyth_price, HistoricalIndexData,
HistoricalOracleData, OraclePriceData, OracleSource, PrelaunchOracle, PrelaunchOracleParams,
get_oracle_price, get_prelaunch_price, get_pyth_price, get_switchboard_price,
HistoricalIndexData, HistoricalOracleData, OraclePriceData, OracleSource, PrelaunchOracle,
PrelaunchOracleParams,
};
use crate::state::paused_operations::{PerpOperation, SpotOperation};
use crate::state::perp_market::{
Expand Down Expand Up @@ -569,8 +570,13 @@ pub fn handle_initialize_perp_market(
(oracle_price, oracle_delay, QUOTE_PRECISION_I64)
}
OracleSource::Switchboard => {
msg!("Switchboard oracle cant be used for perp market");
return Err(ErrorCode::InvalidOracle.into());
let OraclePriceData {
price: oracle_price,
delay: oracle_delay,
..
} = get_switchboard_price(&ctx.accounts.oracle, clock_slot)?;

(oracle_price, oracle_delay, oracle_price)
}
OracleSource::QuoteAsset => {
msg!("Quote asset oracle cant be used for perp market");
Expand Down
103 changes: 59 additions & 44 deletions programs/drift/src/state/oracle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::error::{DriftResult, ErrorCode};
use crate::math::casting::Cast;
use crate::math::constants::{PRICE_PRECISION, PRICE_PRECISION_I64, PRICE_PRECISION_U64};
use crate::math::safe_math::SafeMath;
use switchboard::{AggregatorAccountData, SwitchboardDecimal};

use crate::error::ErrorCode::{InvalidOracle, UnableToLoadOracle};
use crate::math::safe_unwrap::SafeUnwrap;
Expand Down Expand Up @@ -150,10 +151,7 @@ pub fn get_oracle_price(
OracleSource::Pyth1K => get_pyth_price(price_oracle, clock_slot, 1000),
OracleSource::Pyth1M => get_pyth_price(price_oracle, clock_slot, 1000000),
OracleSource::PythStableCoin => get_pyth_stable_coin_price(price_oracle, clock_slot),
OracleSource::Switchboard => {
msg!("Switchboard oracle not yet supported");
Err(crate::error::ErrorCode::InvalidOracle)
}
OracleSource::Switchboard => get_switchboard_price(price_oracle, clock_slot),
OracleSource::QuoteAsset => Ok(OraclePriceData {
price: PRICE_PRECISION_I64,
confidence: 1,
Expand Down Expand Up @@ -244,46 +242,63 @@ pub fn get_pyth_stable_coin_price(
Ok(oracle_price_data)
}

// pub fn get_switchboard_price(
// _price_oracle: &AccountInfo,
// _clock_slot: u64,
// ) -> DriftResult<OraclePriceData> {
// updating solana/anchor cause this to make compiler complan
// fix when we're using switchboard again
// let aggregator_data = AggregatorAccountData::new(price_oracle)
// .or(Err(crate::error::ErrorCode::UnableToLoadOracle))?;
//
// let price = convert_switchboard_decimal(&aggregator_data.latest_confirmed_round.result)?;
// let confidence =
// convert_switchboard_decimal(&aggregator_data.latest_confirmed_round.std_deviation)?;
//
// // std deviation should always be positive, if we get a negative make it u128::MAX so it's flagged as bad value
// let confidence = if confidence < 0 {
// u128::MAX
// } else {
// let price_10bps = price
// .unsigned_abs()
// .safe_div(1000)
// ?;
// max(confidence.unsigned_abs(), price_10bps)
// };
//
// let delay: i64 = cast_to_i64(clock_slot)?
// .safe_sub(cast(
// aggregator_data.latest_confirmed_round.round_open_slot,
// )?)
// ?;
//
// let has_sufficient_number_of_data_points =
// aggregator_data.latest_confirmed_round.num_success >= aggregator_data.min_oracle_results;
//
// Ok(OraclePriceData {
// price,
// confidence,
// delay,
// has_sufficient_number_of_data_points,
// })
// }
pub fn get_switchboard_price(
price_oracle: &AccountInfo,
clock_slot: u64,
) -> DriftResult<OraclePriceData> {
let aggregator_data_loader: AccountLoader<AggregatorAccountData> =
AccountLoader::try_from(price_oracle).or(Err(ErrorCode::UnableToLoadOracle))?;
let aggregator_data = aggregator_data_loader
.load()
.or(Err(ErrorCode::UnableToLoadOracle))?;

let price = convert_switchboard_decimal(&aggregator_data.latest_confirmed_round.result)?
.cast::<i64>()?;
let confidence =
convert_switchboard_decimal(&aggregator_data.latest_confirmed_round.std_deviation)?
.cast::<i64>()?;

// std deviation should always be positive, if we get a negative make it u128::MAX so it's flagged as bad value
let confidence = if confidence < 0 {
u64::MAX
} else {
let price_10bps = price.unsigned_abs().safe_div(1000)?;
confidence.unsigned_abs().max(price_10bps)
};

let delay = clock_slot.cast::<i64>()?.safe_sub(
aggregator_data
.latest_confirmed_round
.round_open_slot
.cast()?,
)?;

let has_sufficient_number_of_data_points =
aggregator_data.latest_confirmed_round.num_success >= aggregator_data.min_oracle_results;

Ok(OraclePriceData {
price,
confidence,
delay,
has_sufficient_number_of_data_points,
})
}

/// Given a decimal number represented as a mantissa (the digits) plus an
/// original_precision (10.pow(some number of decimals)), scale the
/// mantissa/digits to make sense with a new_precision.
fn convert_switchboard_decimal(switchboard_decimal: &SwitchboardDecimal) -> DriftResult<i128> {
let switchboard_precision = 10_u128.pow(switchboard_decimal.scale);
if switchboard_precision > PRICE_PRECISION {
switchboard_decimal
.mantissa
.safe_div((switchboard_precision / PRICE_PRECISION) as i128)
} else {
switchboard_decimal
.mantissa
.safe_mul((PRICE_PRECISION / switchboard_precision) as i128)
}
}

pub fn get_prelaunch_price(price_oracle: &AccountInfo, slot: u64) -> DriftResult<OraclePriceData> {
let oracle_account_loader: AccountLoader<PrelaunchOracle> =
Expand Down
27 changes: 25 additions & 2 deletions programs/drift/src/state/oracle_map.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::error::ErrorCode::UnableToLoadOracle;
use crate::error::{DriftResult, ErrorCode};
use crate::ids::{bonk_oracle, pepe_oracle, pyth_program, usdc_oracle, usdt_oracle_mainnet};
use crate::ids::{
bonk_oracle, pepe_oracle, pyth_program, switchboard_program, usdc_oracle, usdt_oracle_mainnet,
};
use crate::math::constants::PRICE_PRECISION_I64;
use crate::math::oracle::{oracle_validity, OracleValidity};
use crate::state::oracle::{get_oracle_price, OraclePriceData, OracleSource, PrelaunchOracle};
Expand Down Expand Up @@ -235,6 +237,19 @@ impl<'a> OracleMap<'a> {
},
);

continue;
} else if account_info.owner == &switchboard_program::id() {
let account_info = account_info_iter.next().safe_unwrap()?;
let pubkey = account_info.key();

oracles.insert(
pubkey,
AccountInfoAndOracleSource {
account_info: account_info.clone(),
oracle_source: OracleSource::Switchboard,
},
);

continue;
}

Expand Down Expand Up @@ -304,14 +319,22 @@ impl<'a> OracleMap<'a> {
}

let pubkey = account_info.key();

oracles.insert(
pubkey,
AccountInfoAndOracleSource {
account_info: account_info.clone(),
oracle_source: OracleSource::Prelaunch,
},
);
} else if account_info.owner == &switchboard_program::id() {
let pubkey = account_info.key();
oracles.insert(
pubkey,
AccountInfoAndOracleSource {
account_info: account_info.clone(),
oracle_source: OracleSource::Switchboard,
},
);
} else if account_info.key() != Pubkey::default() {
return Err(ErrorCode::InvalidOracle);
}
Expand Down
6 changes: 4 additions & 2 deletions programs/drift/src/state/perp_market.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ use crate::math::safe_math::SafeMath;
use crate::math::stats;
use crate::state::events::OrderActionExplanation;

use crate::state::oracle::{get_prelaunch_price, HistoricalOracleData, OracleSource};
use crate::state::oracle::{
get_prelaunch_price, get_switchboard_price, HistoricalOracleData, OracleSource,
};
use crate::state::spot_market::{AssetTier, SpotBalance, SpotBalanceType};
use crate::state::traits::{MarketIndexOffset, Size};
use borsh::{BorshDeserialize, BorshSerialize};
Expand Down Expand Up @@ -1278,7 +1280,7 @@ impl AMM {
}
OracleSource::Pyth1K => Ok(Some(self.get_pyth_twap(price_oracle, 1000)?)),
OracleSource::Pyth1M => Ok(Some(self.get_pyth_twap(price_oracle, 1000000)?)),
OracleSource::Switchboard => Ok(None),
OracleSource::Switchboard => Ok(Some(get_switchboard_price(price_oracle, slot)?.price)),
OracleSource::QuoteAsset => {
msg!("Can't get oracle twap for quote asset");
Err(ErrorCode::DefaultError)
Expand Down
22 changes: 22 additions & 0 deletions programs/switchboard/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "switchboard"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]
name = "switchboard"

[features]
no-entrypoint = []
no-idl = []
cpi = ["no-entrypoint"]
default = ["mainnet-beta"]
mainnet-beta=[]

[dependencies]
anchor-lang = "0.27.0"

[dev-dependencies]
base64 = "0.13.0"

Loading

0 comments on commit 3a97a01

Please sign in to comment.