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)?;
let (mut liab_bank, liab_oracle) =
account_retriever.bank_mut_and_oracle(liab_token_index)?;
let asset_price = oracle_price(asset_oracle)?;
let liab_price = oracle_price(liab_oracle)?;
let asset_price = oracle_price(asset_oracle, asset_bank.mint_decimals)?;
let liab_price = oracle_price(liab_oracle, liab_bank.mint_decimals)?;
let liqee_assets_native = liqee
.tokens

View File

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

View File

@ -196,6 +196,7 @@ pub mod mango_v4 {
perp_market_index: PerpMarketIndex,
name: String,
base_token_index_opt: Option<TokenIndex>,
base_token_decimals: u8,
quote_token_index: TokenIndex,
quote_lot_size: i64,
base_lot_size: i64,
@ -215,6 +216,7 @@ pub mod mango_v4 {
perp_market_index,
name,
base_token_index_opt,
base_token_decimals,
quote_token_index,
quote_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() {
let (bank, oracle_ai) =
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
// TODO: health basis token == USDC?

View File

@ -5,9 +5,12 @@ use anchor_lang::Discriminator;
use fixed::types::I80F48;
use static_assertions::const_assert_eq;
use crate::checked_math as cm;
use crate::error::MangoError;
use crate::util::LoadZeroCopy;
pub const QUOTE_DECIMALS: u32 = 6;
#[derive(PartialEq)]
pub enum OracleType {
Stub,
@ -35,7 +38,7 @@ pub fn determine_oracle_type(data: &[u8]) -> Result<OracleType> {
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 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::Pyth => {
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
pub bump: u8,
pub reserved: [u8; 1],
pub base_token_decimals: u8,
/// Lookup indices
pub perp_market_index: PerpMarketIndex,
pub base_token_index: TokenIndex,
/// 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 perp_market_index: PerpMarketIndex,
pub base_token_index: TokenIndex,
pub base_token_decimals: u8,
pub quote_token_index: TokenIndex,
pub quote_lot_size: i64,
pub base_lot_size: i64,
@ -1380,6 +1381,7 @@ impl<'keypair> ClientInstruction for PerpCreateMarketInstruction<'keypair> {
max_funding: 0.05,
min_funding: 0.05,
impact_quantity: 100,
base_token_decimals: self.base_token_decimals,
};
let perp_market = Pubkey::find_program_address(
@ -1636,6 +1638,7 @@ pub struct PerpUpdateFundingInstruction {
pub perp_market: Pubkey,
pub bids: Pubkey,
pub asks: Pubkey,
pub bank: Pubkey,
pub oracle: Pubkey,
}
#[async_trait::async_trait(?Send)]

View File

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

View File

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

View File

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

View File

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

View File

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