Fix oracle price read by using decimals from pyth (#62)

* Fix oracle price read by using decimals from pyth

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>

* fix

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
microwavedcola1 2022-06-02 10:36:04 +02:00 committed by GitHub
parent 2274d5cade
commit a20d04b6ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 58 additions and 25 deletions

View File

@ -71,8 +71,8 @@ pub fn liq_token_with_token(
account_retriever.bank_mut_and_oracle(asset_token_index)?; account_retriever.bank_mut_and_oracle(asset_token_index)?;
let (mut liab_bank, liab_oracle) = let (mut liab_bank, liab_oracle) =
account_retriever.bank_mut_and_oracle(liab_token_index)?; account_retriever.bank_mut_and_oracle(liab_token_index)?;
let asset_price = oracle_price(asset_oracle)?; let asset_price = oracle_price(asset_oracle, asset_bank.mint_decimals)?;
let liab_price = oracle_price(liab_oracle)?; let liab_price = oracle_price(liab_oracle, liab_bank.mint_decimals)?;
let liqee_assets_native = liqee let liqee_assets_native = liqee
.tokens .tokens

View File

@ -2,6 +2,7 @@ use anchor_lang::prelude::*;
use fixed::types::I80F48; use fixed::types::I80F48;
use crate::error::MangoError; use crate::error::MangoError;
use crate::state::*; use crate::state::*;
use crate::util::fill16_from_str; use crate::util::fill16_from_str;
@ -46,6 +47,7 @@ pub fn perp_create_market(
perp_market_index: PerpMarketIndex, perp_market_index: PerpMarketIndex,
name: String, name: String,
base_token_index_opt: Option<TokenIndex>, base_token_index_opt: Option<TokenIndex>,
base_token_decimals: u8,
quote_token_index: TokenIndex, quote_token_index: TokenIndex,
quote_lot_size: i64, quote_lot_size: i64,
base_lot_size: i64, base_lot_size: i64,
@ -79,17 +81,18 @@ pub fn perp_create_market(
taker_fee: I80F48::from_num(taker_fee), taker_fee: I80F48::from_num(taker_fee),
min_funding: I80F48::from_num(min_funding), min_funding: I80F48::from_num(min_funding),
max_funding: I80F48::from_num(max_funding), max_funding: I80F48::from_num(max_funding),
impact_quantity,
long_funding: I80F48::ZERO, long_funding: I80F48::ZERO,
short_funding: I80F48::ZERO, short_funding: I80F48::ZERO,
funding_last_updated: Clock::get()?.unix_timestamp, funding_last_updated: Clock::get()?.unix_timestamp,
impact_quantity,
open_interest: 0, open_interest: 0,
seq_num: 0, seq_num: 0,
fees_accrued: I80F48::ZERO, fees_accrued: I80F48::ZERO,
bump: *ctx.bumps.get("perp_market").ok_or(MangoError::SomeError)?, bump: *ctx.bumps.get("perp_market").ok_or(MangoError::SomeError)?,
reserved: Default::default(), // Why optional - Perp could be based purely on an oracle
perp_market_index,
base_token_index: base_token_index_opt.ok_or(TokenIndex::MAX).unwrap(), base_token_index: base_token_index_opt.ok_or(TokenIndex::MAX).unwrap(),
base_token_decimals,
perp_market_index,
quote_token_index, quote_token_index,
}; };

View File

@ -89,7 +89,10 @@ pub fn perp_place_order(
let mut event_queue = ctx.accounts.event_queue.load_mut()?; let mut event_queue = ctx.accounts.event_queue.load_mut()?;
let oracle_price = oracle_price(&ctx.accounts.oracle.to_account_info())?; let oracle_price = oracle_price(
&ctx.accounts.oracle.to_account_info(),
perp_market.base_token_decimals,
)?;
let now_ts = Clock::get()?.unix_timestamp as u64; let now_ts = Clock::get()?.unix_timestamp as u64;
let time_in_force = if expiry_timestamp != 0 { let time_in_force = if expiry_timestamp != 0 {

View File

@ -27,7 +27,10 @@ pub fn perp_update_funding(ctx: Context<PerpUpdateFunding>) -> Result<()> {
let asks = &ctx.accounts.asks.to_account_info(); let asks = &ctx.accounts.asks.to_account_info();
let book = Book::load_mut(bids, asks, &perp_market)?; let book = Book::load_mut(bids, asks, &perp_market)?;
let oracle_price = oracle_price(&ctx.accounts.oracle.to_account_info())?; let oracle_price = oracle_price(
&ctx.accounts.oracle.to_account_info(),
perp_market.base_token_decimals,
)?;
perp_market.update_funding(&book, oracle_price, now_ts as u64)?; perp_market.update_funding(&book, oracle_price, now_ts as u64)?;

View File

@ -196,6 +196,7 @@ pub mod mango_v4 {
perp_market_index: PerpMarketIndex, perp_market_index: PerpMarketIndex,
name: String, name: String,
base_token_index_opt: Option<TokenIndex>, base_token_index_opt: Option<TokenIndex>,
base_token_decimals: u8,
quote_token_index: TokenIndex, quote_token_index: TokenIndex,
quote_lot_size: i64, quote_lot_size: i64,
base_lot_size: i64, base_lot_size: i64,
@ -215,6 +216,7 @@ pub mod mango_v4 {
perp_market_index, perp_market_index,
name, name,
base_token_index_opt, base_token_index_opt,
base_token_decimals,
quote_token_index, quote_token_index,
quote_lot_size, quote_lot_size,
base_lot_size, base_lot_size,

View File

@ -410,7 +410,7 @@ fn compute_health_detail<'a, 'b: 'a>(
for (i, position) in account.tokens.iter_active().enumerate() { for (i, position) in account.tokens.iter_active().enumerate() {
let (bank, oracle_ai) = let (bank, oracle_ai) =
retriever.bank_and_oracle(&account.group, i, position.token_index)?; retriever.bank_and_oracle(&account.group, i, position.token_index)?;
let oracle_price = oracle_price(oracle_ai)?; let oracle_price = oracle_price(oracle_ai, bank.mint_decimals)?;
// converts the token value to the basis token value for health computations // converts the token value to the basis token value for health computations
// TODO: health basis token == USDC? // TODO: health basis token == USDC?

View File

@ -5,9 +5,12 @@ use anchor_lang::Discriminator;
use fixed::types::I80F48; use fixed::types::I80F48;
use static_assertions::const_assert_eq; use static_assertions::const_assert_eq;
use crate::checked_math as cm;
use crate::error::MangoError; use crate::error::MangoError;
use crate::util::LoadZeroCopy; use crate::util::LoadZeroCopy;
pub const QUOTE_DECIMALS: u32 = 6;
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum OracleType { pub enum OracleType {
Stub, Stub,
@ -35,7 +38,7 @@ pub fn determine_oracle_type(data: &[u8]) -> Result<OracleType> {
Err(MangoError::UnknownOracleType.into()) Err(MangoError::UnknownOracleType.into())
} }
pub fn oracle_price(acc_info: &AccountInfo) -> Result<I80F48> { pub fn oracle_price(acc_info: &AccountInfo, base_token_decimals: u8) -> Result<I80F48> {
let data = &acc_info.try_borrow_data()?; let data = &acc_info.try_borrow_data()?;
let oracle_type = determine_oracle_type(data)?; let oracle_type = determine_oracle_type(data)?;
@ -43,7 +46,14 @@ pub fn oracle_price(acc_info: &AccountInfo) -> Result<I80F48> {
OracleType::Stub => acc_info.load::<StubOracle>()?.price, OracleType::Stub => acc_info.load::<StubOracle>()?.price,
OracleType::Pyth => { OracleType::Pyth => {
let price_struct = pyth_sdk_solana::load_price(data).unwrap(); let price_struct = pyth_sdk_solana::load_price(data).unwrap();
I80F48::from_num(price_struct.price) let price = I80F48::from_num(price_struct.price);
let decimals = (price_struct.expo as u32)
.checked_add(QUOTE_DECIMALS)
.unwrap()
.checked_sub(base_token_decimals as u32)
.unwrap();
let decimal_adj = I80F48::from_num(10_u32.pow(decimals));
cm!(price * decimal_adj)
} }
}) })
} }

View File

@ -71,10 +71,12 @@ pub struct PerpMarket {
/// PDA bump /// PDA bump
pub bump: u8, pub bump: u8,
pub reserved: [u8; 1],
pub base_token_decimals: u8,
/// Lookup indices /// Lookup indices
pub perp_market_index: PerpMarketIndex, pub perp_market_index: PerpMarketIndex,
pub base_token_index: TokenIndex, pub base_token_index: TokenIndex,
/// Cannot be chosen freely, must be the health-reference token, same for all PerpMarkets /// Cannot be chosen freely, must be the health-reference token, same for all PerpMarkets

View File

@ -1343,6 +1343,7 @@ pub struct PerpCreateMarketInstruction<'keypair> {
pub payer: &'keypair Keypair, pub payer: &'keypair Keypair,
pub perp_market_index: PerpMarketIndex, pub perp_market_index: PerpMarketIndex,
pub base_token_index: TokenIndex, pub base_token_index: TokenIndex,
pub base_token_decimals: u8,
pub quote_token_index: TokenIndex, pub quote_token_index: TokenIndex,
pub quote_lot_size: i64, pub quote_lot_size: i64,
pub base_lot_size: i64, pub base_lot_size: i64,
@ -1380,6 +1381,7 @@ impl<'keypair> ClientInstruction for PerpCreateMarketInstruction<'keypair> {
max_funding: 0.05, max_funding: 0.05,
min_funding: 0.05, min_funding: 0.05,
impact_quantity: 100, impact_quantity: 100,
base_token_decimals: self.base_token_decimals,
}; };
let perp_market = Pubkey::find_program_address( let perp_market = Pubkey::find_program_address(
@ -1636,6 +1638,7 @@ pub struct PerpUpdateFundingInstruction {
pub perp_market: Pubkey, pub perp_market: Pubkey,
pub bids: Pubkey, pub bids: Pubkey,
pub asks: Pubkey, pub asks: Pubkey,
pub bank: Pubkey,
pub oracle: Pubkey, pub oracle: Pubkey,
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]

View File

@ -267,6 +267,7 @@ async fn test_health_compute_perp() -> Result<(), TransportError> {
payer, payer,
perp_market_index: perp_market_index as PerpMarketIndex, perp_market_index: perp_market_index as PerpMarketIndex,
base_token_index: quote_token.index, base_token_index: quote_token.index,
base_token_decimals: quote_token.mint.decimals,
quote_token_index: token.index, quote_token_index: token.index,
quote_lot_size: 10, quote_lot_size: 10,
base_lot_size: 100, base_lot_size: 100,

View File

@ -149,6 +149,7 @@ async fn test_perp() -> Result<(), TransportError> {
payer, payer,
perp_market_index: 0, perp_market_index: 0,
base_token_index: tokens[0].index, base_token_index: tokens[0].index,
base_token_decimals: tokens[0].mint.decimals,
quote_token_index: tokens[1].index, quote_token_index: tokens[1].index,
quote_lot_size: 10, quote_lot_size: 10,
base_lot_size: 100, base_lot_size: 100,

View File

@ -700,6 +700,7 @@ export class MangoClient {
perpMarketIndex: number, perpMarketIndex: number,
name: string, name: string,
baseTokenIndex: number, baseTokenIndex: number,
baseTokenDecimals: number,
quoteTokenIndex: number, quoteTokenIndex: number,
quoteLotSize: number, quoteLotSize: number,
baseLotSize: number, baseLotSize: number,
@ -723,6 +724,7 @@ export class MangoClient {
perpMarketIndex, perpMarketIndex,
name, name,
baseTokenIndex, baseTokenIndex,
baseTokenDecimals,
quoteTokenIndex, quoteTokenIndex,
new BN(quoteLotSize), new BN(quoteLotSize),
new BN(baseLotSize), new BN(baseLotSize),

View File

@ -1212,6 +1212,12 @@ export type MangoV4 = {
"option": "u16" "option": "u16"
} }
}, },
{
"name": "baseTokenDecimalsOpt",
"type": {
"option": "u8"
}
},
{ {
"name": "quoteTokenIndex", "name": "quoteTokenIndex",
"type": "u16" "type": "u16"
@ -2149,13 +2155,8 @@ export type MangoV4 = {
"type": "u8" "type": "u8"
}, },
{ {
"name": "reserved", "name": "baseTokenDecimals",
"type": { "type": "u8"
"array": [
"u8",
1
]
}
}, },
{ {
"name": "perpMarketIndex", "name": "perpMarketIndex",
@ -4112,6 +4113,12 @@ export const IDL: MangoV4 = {
"option": "u16" "option": "u16"
} }
}, },
{
"name": "baseTokenDecimalsOpt",
"type": {
"option": "u8"
}
},
{ {
"name": "quoteTokenIndex", "name": "quoteTokenIndex",
"type": "u16" "type": "u16"
@ -5049,13 +5056,8 @@ export const IDL: MangoV4 = {
"type": "u8" "type": "u8"
}, },
{ {
"name": "reserved", "name": "baseTokenDecimals",
"type": { "type": "u8"
"array": [
"u8",
1
]
}
}, },
{ {
"name": "perpMarketIndex", "name": "perpMarketIndex",

View File

@ -204,6 +204,7 @@ async function main() {
0, 0,
'BTC/USDC', 'BTC/USDC',
0, 0,
6,
1, 1,
10, 10,
100, 100,