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:
parent
2274d5cade
commit
a20d04b6ff
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)?;
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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?
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -204,6 +204,7 @@ async function main() {
|
||||||
0,
|
0,
|
||||||
'BTC/USDC',
|
'BTC/USDC',
|
||||||
0,
|
0,
|
||||||
|
6,
|
||||||
1,
|
1,
|
||||||
10,
|
10,
|
||||||
100,
|
100,
|
||||||
|
|
Loading…
Reference in New Issue