Clarkeni/oracle confidence (#568)

* Add oracle confidence and oracle type to update funding logs.

* Return price and OracleState struct from oracle_price_and_state.
This commit is contained in:
Nicholas Clarke 2023-05-05 23:44:14 -07:00 committed by GitHub
parent 3600b6592c
commit c0ea9970b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 292 additions and 23 deletions

View File

@ -7881,6 +7881,78 @@
}
]
},
{
"name": "PerpUpdateFundingLogV2",
"fields": [
{
"name": "mangoGroup",
"type": "publicKey",
"index": false
},
{
"name": "marketIndex",
"type": "u16",
"index": false
},
{
"name": "longFunding",
"type": "i128",
"index": false
},
{
"name": "shortFunding",
"type": "i128",
"index": false
},
{
"name": "price",
"type": "i128",
"index": false
},
{
"name": "oracleSlot",
"type": "u64",
"index": false
},
{
"name": "oracleConfidence",
"type": "i128",
"index": false
},
{
"name": "oracleType",
"type": {
"defined": "OracleType"
},
"index": false
},
{
"name": "stablePrice",
"type": "i128",
"index": false
},
{
"name": "feesAccrued",
"type": "i128",
"index": false
},
{
"name": "feesSettled",
"type": "i128",
"index": false
},
{
"name": "openInterest",
"type": "i64",
"index": false
},
{
"name": "instantaneousFundingRate",
"type": "i128",
"index": false
}
]
},
{
"name": "UpdateIndexLog",
"fields": [

View File

@ -30,13 +30,13 @@ pub fn perp_place_order(
asks: ctx.accounts.asks.load_mut()?,
};
let oracle_slot;
(oracle_price, oracle_slot) = perp_market.oracle_price_and_slot(
let oracle_state;
(oracle_price, oracle_state) = perp_market.oracle_price_and_state(
&AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?,
None, // staleness checked in health
)?;
perp_market.update_funding_and_stable_price(&book, oracle_price, oracle_slot, now_ts)?;
perp_market.update_funding_and_stable_price(&book, oracle_price, oracle_state, now_ts)?;
}
let mut account = ctx.accounts.account.load_full_mut()?;

View File

@ -14,12 +14,12 @@ pub fn perp_update_funding(ctx: Context<PerpUpdateFunding>) -> Result<()> {
};
let now_slot = Clock::get()?.slot;
let (oracle_price, oracle_slot) = perp_market.oracle_price_and_slot(
let (oracle_price, oracle_state) = perp_market.oracle_price_and_state(
&AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?,
Some(now_slot),
)?;
perp_market.update_funding_and_stable_price(&book, oracle_price, oracle_slot, now_ts)?;
perp_market.update_funding_and_stable_price(&book, oracle_price, oracle_state, now_ts)?;
Ok(())
}

View File

@ -1,6 +1,6 @@
use crate::{
accounts_ix::FlashLoanType,
state::{PerpMarket, PerpPosition},
state::{OracleType, PerpMarket, PerpPosition},
};
use anchor_lang::prelude::*;
use borsh::BorshSerialize;
@ -152,6 +152,23 @@ pub struct PerpUpdateFundingLog {
pub instantaneous_funding_rate: i128,
}
#[event]
pub struct PerpUpdateFundingLogV2 {
pub mango_group: Pubkey,
pub market_index: u16,
pub long_funding: i128,
pub short_funding: i128,
pub price: i128,
pub oracle_slot: u64,
pub oracle_confidence: i128,
pub oracle_type: OracleType,
pub stable_price: i128,
pub fees_accrued: i128,
pub fees_settled: i128,
pub open_interest: i64,
pub instantaneous_funding_rate: i128,
}
#[event]
pub struct UpdateIndexLog {
pub mango_group: Pubkey,

View File

@ -789,7 +789,7 @@ impl Bank {
staleness_slot: Option<u64>,
) -> Result<I80F48> {
require_keys_eq!(self.oracle, *oracle_acc.key());
let (price, _) = oracle::oracle_price_and_slot(
let (price, _) = oracle::oracle_price_and_state(
oracle_acc,
&self.oracle_config,
self.mint_decimals,

View File

@ -83,7 +83,7 @@ impl OracleConfigParams {
}
}
#[derive(PartialEq)]
#[derive(PartialEq, AnchorSerialize, AnchorDeserialize)]
pub enum OracleType {
Pyth,
Stub,
@ -91,6 +91,12 @@ pub enum OracleType {
SwitchboardV2,
}
pub struct OracleState {
pub last_update_slot: u64,
pub confidence: I80F48,
pub oracle_type: OracleType,
}
#[account(zero_copy(safe_bytemuck_derives))]
pub struct StubOracle {
// ABI: Clients rely on this being at offset 8
@ -135,18 +141,25 @@ pub fn determine_oracle_type(acc_info: &impl KeyedAccountReader) -> Result<Oracl
/// This currently assumes that quote decimals is 6, like for USDC.
///
/// Pass `staleness_slot` = None to skip the staleness check
pub fn oracle_price_and_slot(
pub fn oracle_price_and_state(
acc_info: &impl KeyedAccountReader,
config: &OracleConfig,
base_decimals: u8,
staleness_slot: Option<u64>,
) -> Result<(I80F48, u64)> {
) -> Result<(I80F48, OracleState)> {
let data = &acc_info.data();
let oracle_type = determine_oracle_type(acc_info)?;
let staleness_slot = staleness_slot.unwrap_or(0);
Ok(match oracle_type {
OracleType::Stub => (acc_info.load::<StubOracle>()?.price, 0),
OracleType::Stub => (
acc_info.load::<StubOracle>()?.price,
OracleState {
last_update_slot: 0,
confidence: I80F48::ZERO,
oracle_type: OracleType::Stub,
},
),
OracleType::Pyth => {
let price_account = pyth_sdk_solana::state::load_price_account(data).unwrap();
let price_data = price_account.to_price();
@ -187,7 +200,14 @@ pub fn oracle_price_and_slot(
let decimals = (price_account.expo as i8) + QUOTE_DECIMALS - (base_decimals as i8);
let decimal_adj = power_of_ten(decimals);
(price * decimal_adj, last_slot)
(
price * decimal_adj,
OracleState {
last_update_slot: last_slot,
confidence: I80F48::from_num(price_data.conf),
oracle_type: OracleType::Pyth,
},
)
}
OracleType::SwitchboardV2 => {
fn from_foreign_error(e: impl std::fmt::Display) -> Error {
@ -233,7 +253,14 @@ pub fn oracle_price_and_slot(
let decimals = QUOTE_DECIMALS - (base_decimals as i8);
let decimal_adj = power_of_ten(decimals);
(price * decimal_adj, round_open_slot)
(
price * decimal_adj,
OracleState {
last_update_slot: round_open_slot,
confidence: I80F48::from_num(std_deviation_decimal),
oracle_type: OracleType::SwitchboardV2,
},
)
}
OracleType::SwitchboardV1 => {
let result = FastRoundResultAccountData::deserialize(data).unwrap();
@ -269,7 +296,14 @@ pub fn oracle_price_and_slot(
let decimals = QUOTE_DECIMALS - (base_decimals as i8);
let decimal_adj = power_of_ten(decimals);
(price * decimal_adj, round_open_slot)
(
price * decimal_adj,
OracleState {
last_update_slot: round_open_slot,
confidence: max_response - min_response,
oracle_type: OracleType::SwitchboardV1,
},
)
}
})
}

View File

@ -7,11 +7,11 @@ use static_assertions::const_assert_eq;
use crate::accounts_zerocopy::KeyedAccountReader;
use crate::error::MangoError;
use crate::logs::PerpUpdateFundingLog;
use crate::logs::PerpUpdateFundingLogV2;
use crate::state::orderbook::Side;
use crate::state::{oracle, TokenIndex};
use super::{orderbook, OracleConfig, Orderbook, StablePriceModel, DAY_I80F48};
use super::{orderbook, OracleConfig, OracleState, Orderbook, StablePriceModel, DAY_I80F48};
pub type PerpMarketIndex = u16;
@ -246,7 +246,7 @@ impl PerpMarket {
staleness_slot: Option<u64>,
) -> Result<I80F48> {
require_keys_eq!(self.oracle, *oracle_acc.key());
let (price, _) = oracle::oracle_price_and_slot(
let (price, _) = oracle::oracle_price_and_state(
oracle_acc,
&self.oracle_config,
self.base_decimals,
@ -256,13 +256,13 @@ impl PerpMarket {
Ok(price)
}
pub fn oracle_price_and_slot(
pub fn oracle_price_and_state(
&self,
oracle_acc: &impl KeyedAccountReader,
staleness_slot: Option<u64>,
) -> Result<(I80F48, u64)> {
) -> Result<(I80F48, OracleState)> {
require_keys_eq!(self.oracle, *oracle_acc.key());
oracle::oracle_price_and_slot(
oracle::oracle_price_and_state(
oracle_acc,
&self.oracle_config,
self.base_decimals,
@ -279,7 +279,7 @@ impl PerpMarket {
&mut self,
book: &Orderbook,
oracle_price: I80F48,
oracle_slot: u64,
oracle_state: OracleState,
now_ts: u64,
) -> Result<()> {
if now_ts <= self.funding_last_updated {
@ -331,13 +331,15 @@ impl PerpMarket {
self.stable_price_model
.update(now_ts, oracle_price.to_num());
emit!(PerpUpdateFundingLog {
emit!(PerpUpdateFundingLogV2 {
mango_group: self.group,
market_index: self.perp_market_index,
long_funding: self.long_funding.to_bits(),
short_funding: self.short_funding.to_bits(),
price: oracle_price.to_bits(),
oracle_slot: oracle_slot,
oracle_slot: oracle_state.last_update_slot,
oracle_confidence: oracle_state.confidence.to_bits(),
oracle_type: oracle_state.oracle_type,
stable_price: self.stable_price().to_bits(),
fees_accrued: self.fees_accrued.to_bits(),
fees_settled: self.fees_settled.to_bits(),

View File

@ -7881,6 +7881,78 @@ export type MangoV4 = {
}
]
},
{
"name": "PerpUpdateFundingLogV2",
"fields": [
{
"name": "mangoGroup",
"type": "publicKey",
"index": false
},
{
"name": "marketIndex",
"type": "u16",
"index": false
},
{
"name": "longFunding",
"type": "i128",
"index": false
},
{
"name": "shortFunding",
"type": "i128",
"index": false
},
{
"name": "price",
"type": "i128",
"index": false
},
{
"name": "oracleSlot",
"type": "u64",
"index": false
},
{
"name": "oracleConfidence",
"type": "i128",
"index": false
},
{
"name": "oracleType",
"type": {
"defined": "OracleType"
},
"index": false
},
{
"name": "stablePrice",
"type": "i128",
"index": false
},
{
"name": "feesAccrued",
"type": "i128",
"index": false
},
{
"name": "feesSettled",
"type": "i128",
"index": false
},
{
"name": "openInterest",
"type": "i64",
"index": false
},
{
"name": "instantaneousFundingRate",
"type": "i128",
"index": false
}
]
},
{
"name": "UpdateIndexLog",
"fields": [
@ -16896,6 +16968,78 @@ export const IDL: MangoV4 = {
}
]
},
{
"name": "PerpUpdateFundingLogV2",
"fields": [
{
"name": "mangoGroup",
"type": "publicKey",
"index": false
},
{
"name": "marketIndex",
"type": "u16",
"index": false
},
{
"name": "longFunding",
"type": "i128",
"index": false
},
{
"name": "shortFunding",
"type": "i128",
"index": false
},
{
"name": "price",
"type": "i128",
"index": false
},
{
"name": "oracleSlot",
"type": "u64",
"index": false
},
{
"name": "oracleConfidence",
"type": "i128",
"index": false
},
{
"name": "oracleType",
"type": {
"defined": "OracleType"
},
"index": false
},
{
"name": "stablePrice",
"type": "i128",
"index": false
},
{
"name": "feesAccrued",
"type": "i128",
"index": false
},
{
"name": "feesSettled",
"type": "i128",
"index": false
},
{
"name": "openInterest",
"type": "i64",
"index": false
},
{
"name": "instantaneousFundingRate",
"type": "i128",
"index": false
}
]
},
{
"name": "UpdateIndexLog",
"fields": [