add pyth oracle and instruction to switch
This commit is contained in:
parent
4a88ae5045
commit
7f27d4508a
|
@ -67,6 +67,8 @@ pub enum MangoErrorCode {
|
|||
FeeDiscountFunctionality,
|
||||
#[error("MangoErrorCode::Deprecated")]
|
||||
Deprecated,
|
||||
#[error("MangoErrorCode::OracleOffline")]
|
||||
OracleOffline,
|
||||
|
||||
#[error("MangoErrorCode::Default Check the source code for more info")]
|
||||
Default = u32::MAX_VALUE,
|
||||
|
@ -92,6 +94,14 @@ impl From<serum_dex::error::DexError> for MangoError {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl From<pyth_client::PythError> for MangoError {
|
||||
fn from(pyth_e: pyth_client::PythError) -> Self {
|
||||
let prog_e: ProgramError = pyth_e.into();
|
||||
prog_e.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn check_assert(
|
||||
cond: bool,
|
||||
|
|
|
@ -36,7 +36,7 @@ pub enum MangoInstruction {
|
|||
/// 7+2*NUM_TOKENS..7+2*NUM_TOKENS+NUM_MARKETS `[]`
|
||||
/// spot_market_accs - MarketState account from serum dex for each of the spot markets
|
||||
/// 7+2*NUM_TOKENS+NUM_MARKETS..7+2*NUM_TOKENS+2*NUM_MARKETS `[]`
|
||||
/// oracle_accs - Solana Flux Aggregator accounts corresponding to each trading pair
|
||||
/// oracle_accs - Pyth Price / Solana Flux Aggregator accounts corresponding to each trading pair
|
||||
InitMangoGroup {
|
||||
signer_nonce: u64,
|
||||
maint_coll_ratio: U64F64,
|
||||
|
@ -83,7 +83,7 @@ pub enum MangoInstruction {
|
|||
/// 7. `[]` clock_acc - Clock sysvar account
|
||||
/// 8..8+NUM_MARKETS `[]` open_orders_accs - open orders for each of the spot market
|
||||
/// 8+NUM_MARKETS..8+2*NUM_MARKETS `[]`
|
||||
/// oracle_accs - flux aggregator feed accounts
|
||||
/// oracle_accs - Pyth Price / Solana Flux Aggregator accounts corresponding to each trading pair
|
||||
Withdraw {
|
||||
quantity: u64
|
||||
},
|
||||
|
@ -98,7 +98,7 @@ pub enum MangoInstruction {
|
|||
/// 3. `[]` clock_acc - Clock sysvar account
|
||||
/// 4..4+NUM_MARKETS `[]` open_orders_accs - open orders for each of the spot market
|
||||
/// 4+NUM_MARKETS..4+2*NUM_MARKETS `[]`
|
||||
/// oracle_accs - flux aggregator feed accounts
|
||||
/// oracle_accs - Pyth Price / Solana Flux Aggregator accounts corresponding to each trading pair
|
||||
Borrow {
|
||||
token_index: usize,
|
||||
quantity: u64
|
||||
|
@ -128,7 +128,7 @@ pub enum MangoInstruction {
|
|||
/// 4. `[]` clock_acc - Clock sysvar account
|
||||
/// 5..5+NUM_MARKETS `[]` open_orders_accs - open orders for each of the spot market
|
||||
/// 5+NUM_MARKETS..5+2*NUM_MARKETS `[]`
|
||||
/// oracle_accs - flux aggregator feed accounts
|
||||
/// oracle_accs - Pyth Price / Solana Flux Aggregator accounts corresponding to each trading pair
|
||||
/// 5+2*NUM_MARKETS..5+2*NUM_MARKETS+NUM_TOKENS `[writable]`
|
||||
/// vault_accs - MangoGroup vaults
|
||||
/// 5+2*NUM_MARKETS+NUM_TOKENS..5+2*NUM_MARKETS+2*NUM_TOKENS `[writable]`
|
||||
|
@ -197,7 +197,7 @@ pub enum MangoInstruction {
|
|||
/// 16. `[writable]` srm_vault_acc - MangoGroup's srm_vault used for fee reduction
|
||||
/// 17..17+NUM_MARKETS `[writable]` open_orders_accs - open orders for each of the spot market
|
||||
/// 17+NUM_MARKETS..17+2*NUM_MARKETS `[]`
|
||||
/// oracle_accs - flux aggregator feed accounts
|
||||
/// oracle_accs - Pyth Price / Solana Flux Aggregator accounts corresponding to each trading pair
|
||||
PlaceOrder {
|
||||
order: serum_dex::instruction::NewOrderInstructionV3
|
||||
},
|
||||
|
@ -297,7 +297,7 @@ pub enum MangoInstruction {
|
|||
/// 18. `[]` dex_signer_acc - signer for serum dex MarketState
|
||||
/// 19..19+NUM_MARKETS `[writable]` open_orders_accs - open orders for each of the spot market
|
||||
/// 19+NUM_MARKETS..19+2*NUM_MARKETS `[]`
|
||||
/// oracle_accs - flux aggregator feed accounts
|
||||
/// oracle_accs - Pyth Price / Solana Flux Aggregator accounts corresponding to each trading pair
|
||||
PlaceAndSettle {
|
||||
order: serum_dex::instruction::NewOrderInstructionV3
|
||||
},
|
||||
|
@ -324,7 +324,7 @@ pub enum MangoInstruction {
|
|||
/// 15. `[]` clock_acc - Clock sysvar account
|
||||
/// 16..16+NUM_MARKETS `[writable]` open_orders_accs - open orders for each of the spot market
|
||||
/// 16+NUM_MARKETS..16+2*NUM_MARKETS `[]`
|
||||
/// oracle_accs - flux aggregator feed accounts
|
||||
/// oracle_accs - Pyth Price / Solana Flux Aggregator accounts corresponding to each trading pair
|
||||
ForceCancelOrders {
|
||||
/// Max orders to cancel -- could be useful to lower this if running into compute limits
|
||||
/// Recommended: 5
|
||||
|
@ -347,7 +347,7 @@ pub enum MangoInstruction {
|
|||
/// 9. `[]` clock_acc - Clock sysvar account
|
||||
/// 10..10+NUM_MARKETS `[]` open_orders_accs - open orders for each of the spot market
|
||||
/// 10+NUM_MARKETS..10+2*NUM_MARKETS `[]`
|
||||
/// oracle_accs - flux aggregator feed accounts
|
||||
/// oracle_accs - Pyth Price / Solana Flux Aggregator accounts corresponding to each trading pair
|
||||
PartialLiquidate {
|
||||
/// Quantity of the token being deposited to repay borrows
|
||||
max_deposit: u64
|
||||
|
@ -356,7 +356,13 @@ pub enum MangoInstruction {
|
|||
|
||||
AddMarginAccountInfo {
|
||||
info: [u8; INFO_LEN]
|
||||
}
|
||||
},
|
||||
|
||||
/// Allows to switch the oracles to a new set
|
||||
/// 0. `[writable]` mango_group_acc - the data account to store mango group state vars
|
||||
/// 1. `[signer]` admin_acc - admin key that created the group
|
||||
/// 2..2+NUM_MARKETS `[]` oracle_accs - Pyth Price / Solana Flux Aggregator accounts corresponding to each trading pair
|
||||
SwitchOracles
|
||||
}
|
||||
|
||||
|
||||
|
@ -508,6 +514,9 @@ impl MangoInstruction {
|
|||
info: *info
|
||||
}
|
||||
}
|
||||
18 => {
|
||||
MangoInstruction::SwitchOracles
|
||||
}
|
||||
_ => { return None; }
|
||||
})
|
||||
}
|
||||
|
@ -1233,4 +1242,26 @@ pub fn add_margin_account_info(
|
|||
accounts,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
pub fn switch_oracles(
|
||||
program_id: &Pubkey,
|
||||
mango_group_pk: &Pubkey,
|
||||
admin_pk: &Pubkey,
|
||||
oracle_pks: &[Pubkey],
|
||||
) -> Result<Instruction, ProgramError> {
|
||||
let mut accounts = vec![
|
||||
AccountMeta::new(*mango_group_pk, false),
|
||||
AccountMeta::new_readonly(*admin_pk, true),
|
||||
];
|
||||
accounts.extend(oracle_pks.iter().map(
|
||||
|pk| AccountMeta::new_readonly(*pk, false))
|
||||
);
|
||||
let instr = MangoInstruction::SwitchOracles {};
|
||||
let data = instr.pack();
|
||||
Ok(Instruction {
|
||||
program_id: *program_id,
|
||||
accounts,
|
||||
data
|
||||
})
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
use std::cmp;
|
||||
use std::cmp::min;
|
||||
use std::mem::size_of;
|
||||
use std::ops::Neg;
|
||||
|
||||
use arrayref::{array_ref, array_refs};
|
||||
use fixed::types::U64F64;
|
||||
use fixed_macro::types::U64F64;
|
||||
use flux_aggregator::borsh_state::InitBorshState;
|
||||
use pyth_client::PriceStatus;
|
||||
use serum_dex::matching::Side;
|
||||
use serum_dex::state::ToAlignedBytes;
|
||||
use solana_program::account_info::AccountInfo;
|
||||
|
@ -1375,6 +1377,52 @@ impl Processor {
|
|||
margin_account.info = info;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn switch_oracles(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
) -> MangoResult<()> {
|
||||
const NUM_FIXED: usize = 2;
|
||||
let accounts = array_ref![accounts, 0, NUM_FIXED + NUM_MARKETS];
|
||||
let (fixed_accs, oracle_accs) = array_refs![accounts, NUM_FIXED, NUM_MARKETS];
|
||||
let [
|
||||
mango_group_acc,
|
||||
admin_acc,
|
||||
] = fixed_accs;
|
||||
|
||||
let mut mango_group = MangoGroup::load_mut_checked(
|
||||
mango_group_acc, program_id)?;
|
||||
check_eq!(admin_acc.key, &mango_group.admin, MangoErrorCode::InvalidGroupOwner)?;
|
||||
check!(admin_acc.is_signer, MangoErrorCode::SignerNecessary)?;
|
||||
|
||||
for i in 0..NUM_MARKETS {
|
||||
mango_group.oracles[i] = *oracle_accs[i].key;
|
||||
|
||||
// determine oracle type
|
||||
let borrowed = oracle_accs[i].data.borrow();
|
||||
let magic = u32::from_le_bytes(*array_ref![borrowed, 0, 4]);
|
||||
|
||||
// read oracle decimals
|
||||
let decimals = if magic == pyth_client::MAGIC {
|
||||
// detected pyth oracle
|
||||
let price_account = pyth_client::load_price(&borrowed)?;
|
||||
// usually expo is -8, verify anyways that it's within bounds
|
||||
check!(price_account.expo <= 0, MangoErrorCode::Default);
|
||||
check!(price_account.expo >= -255, MangoErrorCode::Default);
|
||||
price_account.expo.neg() as u8
|
||||
} else {
|
||||
// fall back to legacy flux aggregator
|
||||
let oracle = flux_aggregator::state::Aggregator::load_initialized(&oracle_accs[i])?;
|
||||
oracle.config.decimals
|
||||
};
|
||||
|
||||
mango_group.oracle_decimals[i] = decimals;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn process(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
|
@ -1492,6 +1540,10 @@ impl Processor {
|
|||
msg!("Mango: AddMarginAccountInfo");
|
||||
Self::add_margin_account_info(program_id, accounts, info)?;
|
||||
}
|
||||
MangoInstruction::SwitchOracles => {
|
||||
msg!("Mango: SwitchOracles");
|
||||
Self::switch_oracles(program_id, accounts)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1671,16 +1723,27 @@ pub fn get_prices(
|
|||
for i in 0..NUM_MARKETS {
|
||||
check_eq_default!(&mango_group.oracles[i], oracle_accs[i].key)?;
|
||||
|
||||
// TODO store this info in MangoGroup, first make sure it cannot be changed by solink
|
||||
let base_adj = U64F64::from_num(10u64.pow(mango_group.mint_decimals[i] as u32));
|
||||
let quote_adj = U64F64::from_num(
|
||||
10u64.pow(quote_decimals.checked_sub(mango_group.oracle_decimals[i]).unwrap() as u32)
|
||||
);
|
||||
|
||||
let answer = flux_aggregator::read_median(&oracle_accs[i])?; // this is in USD cents
|
||||
// determine oracle type
|
||||
let borrowed = oracle_accs[i].data.borrow();
|
||||
let magic = u32::from_le_bytes(*array_ref![borrowed, 0, 4]);
|
||||
|
||||
let value = U64F64::from_num(answer.median);
|
||||
// read oracle value
|
||||
let value = if magic == pyth_client::MAGIC {
|
||||
// detected pyth oracle
|
||||
let price_account = pyth_client::load_price(&borrowed)?;
|
||||
check_eq!(price_account.get_current_price_status(), PriceStatus::Trading, MangoErrorCode::OracleOffline);
|
||||
U64F64::from_num(price_account.agg.price)
|
||||
} else {
|
||||
// fall back to legacy flux aggregator
|
||||
let answer = flux_aggregator::read_median(&oracle_accs[i])?; // this is in USD cents
|
||||
U64F64::from_num(answer.median)
|
||||
};
|
||||
|
||||
let base_adj = U64F64::from_num(10u64.pow(mango_group.mint_decimals[i] as u32));
|
||||
prices[i] = quote_adj
|
||||
.checked_div(base_adj).unwrap()
|
||||
.checked_mul(value).unwrap();
|
||||
|
|
Loading…
Reference in New Issue