Better error messages
This commit is contained in:
parent
420132cf89
commit
f132f30874
|
@ -1,4 +1,5 @@
|
|||
use anchor_lang::prelude::*;
|
||||
use core::fmt::Display;
|
||||
|
||||
// todo: group error blocks by kind
|
||||
// todo: add comments which indicate decimal code for an error
|
||||
|
@ -6,16 +7,111 @@ use anchor_lang::prelude::*;
|
|||
pub enum MangoError {
|
||||
#[msg("")]
|
||||
SomeError,
|
||||
#[msg("")]
|
||||
#[msg("checked math error")]
|
||||
MathError,
|
||||
#[msg("")]
|
||||
UnexpectedOracle,
|
||||
#[msg("")]
|
||||
#[msg("oracle type cannot be determined")]
|
||||
UnknownOracleType,
|
||||
#[msg("")]
|
||||
InvalidFlashLoanTargetCpiProgram,
|
||||
#[msg("")]
|
||||
#[msg("health must be positive")]
|
||||
HealthMustBePositive,
|
||||
#[msg("The account is bankrupt")]
|
||||
#[msg("the account is bankrupt")]
|
||||
IsBankrupt,
|
||||
#[msg("the account is not bankrupt")]
|
||||
IsNotBankrupt,
|
||||
#[msg("no free token position index")]
|
||||
NoFreeTokenPositionIndex,
|
||||
#[msg("no free serum3 open orders index")]
|
||||
NoFreeSerum3OpenOrdersIndex,
|
||||
#[msg("no free perp position index")]
|
||||
NoFreePerpPositionIndex,
|
||||
#[msg("serum3 open orders exist already")]
|
||||
Serum3OpenOrdersExistAlready,
|
||||
}
|
||||
|
||||
pub trait Contextable {
|
||||
/// Add a context string `c` to a Result or Error
|
||||
///
|
||||
/// Example: foo().context("calling foo")?;
|
||||
fn context(self, c: impl Display) -> Self;
|
||||
|
||||
/// Like `context()`, but evaluate the context string lazily
|
||||
///
|
||||
/// Use this if it's expensive to generate, like a format!() call.
|
||||
fn with_context<C, F>(self, c: F) -> Self
|
||||
where
|
||||
C: Display,
|
||||
F: FnOnce() -> C;
|
||||
}
|
||||
|
||||
impl Contextable for Error {
|
||||
fn context(self, c: impl Display) -> Self {
|
||||
match self {
|
||||
Error::AnchorError(err) => Error::AnchorError(AnchorError {
|
||||
error_msg: if err.error_msg.is_empty() {
|
||||
format!("{}", c)
|
||||
} else {
|
||||
format!("{}; {}", err.error_msg, c)
|
||||
},
|
||||
..err
|
||||
}),
|
||||
// Maybe wrap somehow?
|
||||
Error::ProgramError(err) => Error::ProgramError(err),
|
||||
}
|
||||
}
|
||||
fn with_context<C, F>(self, c: F) -> Self
|
||||
where
|
||||
C: Display,
|
||||
F: FnOnce() -> C,
|
||||
{
|
||||
self.context(c())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Contextable for Result<T> {
|
||||
fn context(self, c: impl Display) -> Self {
|
||||
if let Err(err) = self {
|
||||
Err(err.context(c))
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
fn with_context<C, F>(self, c: F) -> Self
|
||||
where
|
||||
C: Display,
|
||||
F: FnOnce() -> C,
|
||||
{
|
||||
if let Err(err) = self {
|
||||
Err(err.context(c()))
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an Error with a particular message, using format!() style arguments
|
||||
///
|
||||
/// Example: error_msg!("index {} not found", index)
|
||||
#[macro_export]
|
||||
macro_rules! error_msg {
|
||||
($($arg:tt)*) => {
|
||||
error!(MangoError::SomeError).context(format!($($arg)*))
|
||||
};
|
||||
}
|
||||
|
||||
/// Like anchor's require!(), but with a customizable message
|
||||
///
|
||||
/// Example: require!(condition, "the condition on account {} was violated", account_key);
|
||||
#[macro_export]
|
||||
macro_rules! require_msg {
|
||||
($invariant:expr, $($arg:tt)*) => {
|
||||
if !($invariant) {
|
||||
Err(error_msg!($($arg)*))?;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub use error_msg;
|
||||
pub use require_msg;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::accounts_zerocopy::*;
|
||||
use crate::error::MangoError;
|
||||
use crate::error::*;
|
||||
use crate::group_seeds;
|
||||
use crate::logs::{FlashLoanLog, FlashLoanTokenDetail, TokenBalanceLog};
|
||||
use crate::state::{
|
||||
|
@ -55,11 +55,7 @@ pub fn flash_loan3_begin<'key, 'accounts, 'remaining, 'info>(
|
|||
loan_amounts: Vec<u64>,
|
||||
) -> Result<()> {
|
||||
let num_loans = loan_amounts.len();
|
||||
require_eq!(
|
||||
ctx.remaining_accounts.len(),
|
||||
3 * num_loans,
|
||||
MangoError::SomeError
|
||||
);
|
||||
require_eq!(ctx.remaining_accounts.len(), 3 * num_loans);
|
||||
let banks = &ctx.remaining_accounts[..num_loans];
|
||||
let vaults = &ctx.remaining_accounts[num_loans..2 * num_loans];
|
||||
let token_accounts = &ctx.remaining_accounts[2 * num_loans..];
|
||||
|
@ -106,10 +102,9 @@ pub fn flash_loan3_begin<'key, 'accounts, 'remaining, 'info>(
|
|||
|
||||
// Forbid FlashLoan3Begin to be called from CPI (it does not have to be the first instruction)
|
||||
let current_ix = tx_instructions::load_instruction_at_checked(current_index, ixs)?;
|
||||
require_keys_eq!(
|
||||
current_ix.program_id,
|
||||
*ctx.program_id,
|
||||
MangoError::SomeError
|
||||
require_msg!(
|
||||
current_ix.program_id == *ctx.program_id,
|
||||
"FlashLoan3Begin must be a top-level instruction"
|
||||
);
|
||||
|
||||
// The only other mango instruction that must appear before the end of the tx is
|
||||
|
@ -127,31 +122,37 @@ pub fn flash_loan3_begin<'key, 'accounts, 'remaining, 'info>(
|
|||
if ix.program_id == crate::id() {
|
||||
// must be the last mango ix -- this could possibly be relaxed, but right now
|
||||
// we need to guard against multiple FlashLoanEnds
|
||||
require!(!found_end, MangoError::SomeError);
|
||||
require_msg!(
|
||||
!found_end,
|
||||
"the transaction must not contain a Mango instruction after FlashLoan3End"
|
||||
);
|
||||
found_end = true;
|
||||
|
||||
// must be the FlashLoan3End instruction
|
||||
require!(
|
||||
require_msg!(
|
||||
&ix.data[0..8] == &[163, 231, 155, 56, 201, 68, 84, 148],
|
||||
MangoError::SomeError
|
||||
"the next Mango instruction after FlashLoan3Begin must be FlashLoan3End"
|
||||
);
|
||||
|
||||
// check that the same vaults are passed
|
||||
let begin_accounts = &ctx.remaining_accounts[num_loans..];
|
||||
let end_accounts = &ix.accounts[ix.accounts.len() - 2 * num_loans..];
|
||||
for (begin_account, end_account) in begin_accounts.iter().zip(end_accounts.iter()) {
|
||||
require_keys_eq!(*begin_account.key, end_account.pubkey);
|
||||
require_msg!(*begin_account.key == end_account.pubkey, "the trailing accounts passed to FlashLoan3Begin and End must match, found {} on begin and {} on end", begin_account.key, end_account.pubkey);
|
||||
}
|
||||
} else {
|
||||
// ensure no one can cpi into mango either
|
||||
for meta in ix.accounts.iter() {
|
||||
require_keys_neq!(meta.pubkey, crate::id());
|
||||
require_msg!(meta.pubkey != crate::id(), "instructions between FlashLoan3Begin and End may not use the Mango program account");
|
||||
}
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
require!(found_end, MangoError::SomeError);
|
||||
require_msg!(
|
||||
found_end,
|
||||
"found no FlashLoan3End instruction in transaction"
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -182,7 +183,7 @@ pub fn flash_loan3_end<'key, 'accounts, 'remaining, 'info>(
|
|||
|
||||
maybe_token_account.unwrap().owner == account.group
|
||||
})
|
||||
.ok_or_else(|| error!(MangoError::SomeError))?;
|
||||
.ok_or_else(|| error_msg!("expected at least one vault token account to be passed"))?;
|
||||
let vaults_len = (ctx.remaining_accounts.len() - vaults_index) / 2;
|
||||
require_eq!(ctx.remaining_accounts.len(), vaults_index + 2 * vaults_len);
|
||||
|
||||
|
@ -253,7 +254,14 @@ pub fn flash_loan3_end<'key, 'accounts, 'remaining, 'info>(
|
|||
}
|
||||
|
||||
// all vaults must have had matching banks
|
||||
require!(vaults_with_banks.iter().all(|&b| b), MangoError::SomeError);
|
||||
for (i, has_bank) in vaults_with_banks.iter().enumerate() {
|
||||
require_msg!(
|
||||
has_bank,
|
||||
"missing bank for vault index {}, address {}",
|
||||
i,
|
||||
vaults[i].key
|
||||
);
|
||||
}
|
||||
|
||||
// Check pre-cpi health
|
||||
// NOTE: This health check isn't strictly necessary. It will be, later, when
|
||||
|
|
|
@ -83,7 +83,7 @@ pub fn liq_token_bankruptcy(
|
|||
require!(!liqor.is_bankrupt(), MangoError::IsBankrupt);
|
||||
|
||||
let mut liqee = ctx.accounts.liqee.load_mut()?;
|
||||
require!(liqee.is_bankrupt(), MangoError::SomeError);
|
||||
require!(liqee.is_bankrupt(), MangoError::IsNotBankrupt);
|
||||
|
||||
let liab_bank = bank_ais[0].load::<Bank>()?;
|
||||
let liab_deposit_index = liab_bank.deposit_index;
|
||||
|
|
|
@ -55,7 +55,7 @@ impl<'info> TokenDeposit<'info> {
|
|||
// That would save a lot of computation that needs to go into finding the
|
||||
// right index for the mint.
|
||||
pub fn token_deposit(ctx: Context<TokenDeposit>, amount: u64) -> Result<()> {
|
||||
require!(amount > 0, MangoError::SomeError);
|
||||
require_msg!(amount > 0, "deposit amount must be positive");
|
||||
|
||||
let token_index = ctx.accounts.bank.load()?.token_index;
|
||||
|
||||
|
@ -98,7 +98,8 @@ pub fn token_deposit(ctx: Context<TokenDeposit>, amount: u64) -> Result<()> {
|
|||
// TODO: This will be used to disable is_bankrupt or being_liquidated
|
||||
// when health recovers sufficiently
|
||||
//
|
||||
let health = compute_health(&account, HealthType::Init, &retriever)?;
|
||||
let health = compute_health(&account, HealthType::Init, &retriever)
|
||||
.context("post-deposit init health")?;
|
||||
msg!("health: {}", health);
|
||||
|
||||
//
|
||||
|
|
|
@ -58,7 +58,7 @@ impl<'info> TokenWithdraw<'info> {
|
|||
// right index for the mint.
|
||||
// TODO: https://github.com/blockworks-foundation/mango-v4/commit/15961ec81c7e9324b37d79d0e2a1650ce6bd981d comments
|
||||
pub fn token_withdraw(ctx: Context<TokenWithdraw>, amount: u64, allow_borrow: bool) -> Result<()> {
|
||||
require!(amount > 0, MangoError::SomeError);
|
||||
require_msg!(amount > 0, "withdraw amount must be positive");
|
||||
|
||||
let group = ctx.accounts.group.load()?;
|
||||
let token_index = ctx.accounts.bank.load()?.token_index;
|
||||
|
@ -130,7 +130,8 @@ pub fn token_withdraw(ctx: Context<TokenWithdraw>, amount: u64, allow_borrow: bo
|
|||
//
|
||||
// Health check
|
||||
//
|
||||
let health = compute_health(&account, HealthType::Init, &retriever)?;
|
||||
let health = compute_health(&account, HealthType::Init, &retriever)
|
||||
.context("post-withdraw init health")?;
|
||||
msg!("health: {}", health);
|
||||
require!(health >= 0, MangoError::HealthMustBePositive);
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use super::{OracleConfig, TokenIndex, TokenPosition};
|
||||
use crate::error::MangoError;
|
||||
use crate::util::checked_math as cm;
|
||||
use anchor_lang::prelude::*;
|
||||
use fixed::types::I80F48;
|
||||
|
@ -207,7 +206,7 @@ impl Bank {
|
|||
position: &mut TokenPosition,
|
||||
mut native_amount: I80F48,
|
||||
) -> Result<bool> {
|
||||
require!(native_amount >= 0, MangoError::SomeError);
|
||||
require_gte!(native_amount, 0);
|
||||
let native_position = position.native(self);
|
||||
|
||||
// Adding DELTA to amount/index helps because (amount/index)*index <= amount, but
|
||||
|
@ -290,7 +289,7 @@ impl Bank {
|
|||
mut native_amount: I80F48,
|
||||
with_loan_origination_fee: bool,
|
||||
) -> Result<bool> {
|
||||
require!(native_amount >= 0, MangoError::SomeError);
|
||||
require_gte!(native_amount, 0);
|
||||
let native_position = position.native(self);
|
||||
|
||||
if native_position.is_positive() {
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
use anchor_lang::prelude::*;
|
||||
use anchor_lang::ZeroCopy;
|
||||
|
||||
use fixed::types::I80F48;
|
||||
use fixed_macro::types::I80F48;
|
||||
use serum_dex::state::OpenOrders;
|
||||
|
||||
use std::cell::Ref;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::accounts_zerocopy::*;
|
||||
use crate::error::MangoError;
|
||||
use crate::error::*;
|
||||
use crate::serum3_cpi;
|
||||
use crate::state::{oracle_price, Bank, MangoAccount, PerpMarket, PerpMarketIndex, TokenIndex};
|
||||
use crate::util::checked_math as cm;
|
||||
|
@ -65,7 +67,7 @@ pub fn new_fixed_order_account_retriever<'a, 'info>(
|
|||
let expected_ais = cm!(active_token_len * 2 // banks + oracles
|
||||
+ active_perp_len // PerpMarkets
|
||||
+ active_serum3_len); // open_orders
|
||||
require_eq!(ais.len(), expected_ais, MangoError::SomeError);
|
||||
require_eq!(ais.len(), expected_ais);
|
||||
|
||||
Ok(FixedOrderAccountRetriever {
|
||||
ais: ais
|
||||
|
@ -79,11 +81,22 @@ pub fn new_fixed_order_account_retriever<'a, 'info>(
|
|||
}
|
||||
|
||||
impl<T: KeyedAccountReader> FixedOrderAccountRetriever<T> {
|
||||
fn bank(&self, group: &Pubkey, account_index: usize) -> Result<&Bank> {
|
||||
fn bank(&self, group: &Pubkey, account_index: usize, token_index: TokenIndex) -> Result<&Bank> {
|
||||
let bank = self.ais[account_index].load::<Bank>()?;
|
||||
require!(&bank.group == group, MangoError::SomeError);
|
||||
require_keys_eq!(bank.group, *group);
|
||||
require_eq!(bank.token_index, token_index);
|
||||
Ok(bank)
|
||||
}
|
||||
|
||||
fn oracle_price(&self, account_index: usize, bank: &Bank) -> Result<I80F48> {
|
||||
let oracle = &self.ais[cm!(self.n_banks + account_index)];
|
||||
require_keys_eq!(bank.oracle, *oracle.key());
|
||||
Ok(oracle_price(
|
||||
oracle,
|
||||
bank.oracle_config.conf_filter,
|
||||
bank.mint_decimals,
|
||||
)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: KeyedAccountReader> AccountRetriever for FixedOrderAccountRetriever<T> {
|
||||
|
@ -93,14 +106,27 @@ impl<T: KeyedAccountReader> AccountRetriever for FixedOrderAccountRetriever<T> {
|
|||
account_index: usize,
|
||||
token_index: TokenIndex,
|
||||
) -> Result<(&Bank, I80F48)> {
|
||||
let bank = self.bank(group, account_index)?;
|
||||
require_eq!(bank.token_index, token_index, MangoError::SomeError);
|
||||
let oracle = &self.ais[cm!(self.n_banks + account_index)];
|
||||
require_keys_eq!(bank.oracle, *oracle.key(), MangoError::SomeError);
|
||||
Ok((
|
||||
bank,
|
||||
oracle_price(oracle, bank.oracle_config.conf_filter, bank.mint_decimals)?,
|
||||
))
|
||||
let bank = self
|
||||
.bank(group, account_index, token_index)
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"loading bank with health account index {}, token index {}, passed account {}",
|
||||
account_index,
|
||||
token_index,
|
||||
self.ais[account_index].key(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let oracle_price = self.oracle_price(account_index, bank).with_context(|| {
|
||||
format!(
|
||||
"getting oracle for bank with health account index {} and token index {}, passed account {}",
|
||||
account_index,
|
||||
token_index,
|
||||
self.ais[self.n_banks + account_index].key(),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok((bank, oracle_price))
|
||||
}
|
||||
|
||||
fn perp_market(
|
||||
|
@ -110,19 +136,35 @@ impl<T: KeyedAccountReader> AccountRetriever for FixedOrderAccountRetriever<T> {
|
|||
perp_market_index: PerpMarketIndex,
|
||||
) -> Result<&PerpMarket> {
|
||||
let ai = &self.ais[cm!(self.begin_perp + account_index)];
|
||||
let market = ai.load::<PerpMarket>()?;
|
||||
require!(&market.group == group, MangoError::SomeError);
|
||||
require!(
|
||||
market.perp_market_index == perp_market_index,
|
||||
MangoError::SomeError
|
||||
);
|
||||
Ok(market)
|
||||
(|| {
|
||||
let market = ai.load::<PerpMarket>()?;
|
||||
require_keys_eq!(market.group, *group);
|
||||
require_eq!(market.perp_market_index, perp_market_index);
|
||||
Ok(market)
|
||||
})()
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"loading perp market with health account index {} and perp market index {}, passed account {}",
|
||||
account_index,
|
||||
perp_market_index,
|
||||
ai.key(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn serum_oo(&self, account_index: usize, key: &Pubkey) -> Result<&OpenOrders> {
|
||||
let ai = &self.ais[cm!(self.begin_serum3 + account_index)];
|
||||
require!(key == ai.key(), MangoError::SomeError);
|
||||
serum3_cpi::load_open_orders(ai)
|
||||
(|| {
|
||||
require_keys_eq!(*key, *ai.key());
|
||||
serum3_cpi::load_open_orders(ai)
|
||||
})()
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"loading serum open orders with health account index {}, passed account {}",
|
||||
account_index,
|
||||
ai.key(),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,47 +181,58 @@ pub struct ScanningAccountRetriever<'a, 'info> {
|
|||
perp_index_map: HashMap<PerpMarketIndex, usize>,
|
||||
}
|
||||
|
||||
// Returns None if `ai` doesn't have the owner or discriminator for T
|
||||
fn can_load_as<'a, T: ZeroCopy + Owner>(
|
||||
(i, ai): (usize, &'a AccountInfo),
|
||||
) -> Option<(usize, Result<Ref<'a, T>>)> {
|
||||
let load_result = ai.load::<T>();
|
||||
match load_result {
|
||||
Err(Error::AnchorError(error))
|
||||
if error.error_code_number == ErrorCode::AccountDiscriminatorMismatch as u32
|
||||
|| error.error_code_number == ErrorCode::AccountOwnedByWrongProgram as u32 =>
|
||||
{
|
||||
return None;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
Some((i, load_result))
|
||||
}
|
||||
|
||||
impl<'a, 'info> ScanningAccountRetriever<'a, 'info> {
|
||||
pub fn new(ais: &'a [AccountInfo<'info>], group: &Pubkey) -> Result<Self> {
|
||||
// find all Bank accounts
|
||||
let mut token_index_map = HashMap::with_capacity(ais.len() / 2);
|
||||
for (i, ai) in ais.iter().enumerate() {
|
||||
match ai.load::<Bank>() {
|
||||
Ok(bank) => {
|
||||
require!(&bank.group == group, MangoError::SomeError);
|
||||
ais.iter()
|
||||
.enumerate()
|
||||
.map_while(can_load_as::<Bank>)
|
||||
.try_for_each(|(i, loaded)| {
|
||||
(|| {
|
||||
let bank = loaded?;
|
||||
require_keys_eq!(bank.group, *group);
|
||||
token_index_map.insert(bank.token_index, i);
|
||||
}
|
||||
Err(Error::AnchorError(error))
|
||||
if error.error_code_number
|
||||
== ErrorCode::AccountDiscriminatorMismatch as u32
|
||||
|| error.error_code_number
|
||||
== ErrorCode::AccountOwnedByWrongProgram as u32 =>
|
||||
{
|
||||
break;
|
||||
}
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
})()
|
||||
.with_context(|| format!("scanning banks, health account index {}", i))
|
||||
})?;
|
||||
|
||||
// skip all banks and oracles, then find number of PerpMarket accounts
|
||||
let skip = token_index_map.len() * 2;
|
||||
let mut perp_index_map = HashMap::with_capacity(ais.len() - skip);
|
||||
for (i, ai) in ais[skip..].iter().enumerate() {
|
||||
match ai.load::<PerpMarket>() {
|
||||
Ok(perp_market) => {
|
||||
require!(&perp_market.group == group, MangoError::SomeError);
|
||||
ais[skip..]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map_while(can_load_as::<PerpMarket>)
|
||||
.try_for_each(|(i, loaded)| {
|
||||
(|| {
|
||||
let perp_market = loaded?;
|
||||
require_keys_eq!(perp_market.group, *group);
|
||||
perp_index_map.insert(perp_market.perp_market_index, cm!(skip + i));
|
||||
}
|
||||
Err(Error::AnchorError(error))
|
||||
if error.error_code_number
|
||||
== ErrorCode::AccountDiscriminatorMismatch as u32
|
||||
|| error.error_code_number
|
||||
== ErrorCode::AccountOwnedByWrongProgram as u32 =>
|
||||
{
|
||||
break;
|
||||
}
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
})()
|
||||
.with_context(|| {
|
||||
format!("scanning perp markets, health account index {}", i + skip)
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(Self {
|
||||
ais: ais
|
||||
|
@ -204,7 +257,7 @@ impl<'a, 'info> ScanningAccountRetriever<'a, 'info> {
|
|||
Ok(*self
|
||||
.token_index_map
|
||||
.get(&token_index)
|
||||
.ok_or_else(|| error!(MangoError::SomeError))?)
|
||||
.ok_or_else(|| error_msg!("token index {} not found", token_index))?)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -212,7 +265,7 @@ impl<'a, 'info> ScanningAccountRetriever<'a, 'info> {
|
|||
Ok(*self
|
||||
.perp_index_map
|
||||
.get(&perp_market_index)
|
||||
.ok_or_else(|| error!(MangoError::SomeError))?)
|
||||
.ok_or_else(|| error_msg!("perp market index {} not found", perp_market_index))?)
|
||||
}
|
||||
|
||||
pub fn banks_mut_and_oracles(
|
||||
|
@ -226,7 +279,7 @@ impl<'a, 'info> ScanningAccountRetriever<'a, 'info> {
|
|||
let (bank_part, oracle_part) = self.ais.split_at_mut(index + 1);
|
||||
let bank = bank_part[index].load_mut_fully_unchecked::<Bank>()?;
|
||||
let oracle = &oracle_part[n_banks - 1];
|
||||
require!(&bank.oracle == oracle.key, MangoError::SomeError);
|
||||
require_keys_eq!(bank.oracle, *oracle.key);
|
||||
let price = oracle_price(oracle, bank.oracle_config.conf_filter, bank.mint_decimals)?;
|
||||
return Ok((bank, price, None));
|
||||
}
|
||||
|
@ -246,8 +299,8 @@ impl<'a, 'info> ScanningAccountRetriever<'a, 'info> {
|
|||
let bank2 = second_bank_part[second - (first + 1)].load_mut_fully_unchecked::<Bank>()?;
|
||||
let oracle1 = &oracles_part[cm!(n_banks + first - (second + 1))];
|
||||
let oracle2 = &oracles_part[cm!(n_banks + second - (second + 1))];
|
||||
require!(&bank1.oracle == oracle1.key, MangoError::SomeError);
|
||||
require!(&bank2.oracle == oracle2.key, MangoError::SomeError);
|
||||
require_keys_eq!(bank1.oracle, *oracle1.key);
|
||||
require_keys_eq!(bank2.oracle, *oracle2.key);
|
||||
let mint_decimals1 = bank1.mint_decimals;
|
||||
let mint_decimals2 = bank2.mint_decimals;
|
||||
let price1 = oracle_price(oracle1, bank1.oracle_config.conf_filter, mint_decimals1)?;
|
||||
|
@ -263,7 +316,7 @@ impl<'a, 'info> ScanningAccountRetriever<'a, 'info> {
|
|||
let index = self.bank_index(token_index)?;
|
||||
let bank = self.ais[index].load_fully_unchecked::<Bank>()?;
|
||||
let oracle = &self.ais[cm!(self.n_banks() + index)];
|
||||
require!(&bank.oracle == oracle.key, MangoError::SomeError);
|
||||
require_keys_eq!(bank.oracle, *oracle.key);
|
||||
Ok((
|
||||
bank,
|
||||
oracle_price(oracle, bank.oracle_config.conf_filter, bank.mint_decimals)?,
|
||||
|
@ -279,7 +332,7 @@ impl<'a, 'info> ScanningAccountRetriever<'a, 'info> {
|
|||
let oo = self.ais[self.begin_serum3()..]
|
||||
.iter()
|
||||
.find(|ai| ai.key == key)
|
||||
.ok_or_else(|| error!(MangoError::SomeError))?;
|
||||
.ok_or_else(|| error_msg!("no serum3 open orders for key {}", key))?;
|
||||
serum3_cpi::load_open_orders(oo)
|
||||
}
|
||||
}
|
||||
|
@ -334,7 +387,7 @@ pub fn compute_health_from_fixed_accounts(
|
|||
let expected_ais = cm!(active_token_len * 2 // banks + oracles
|
||||
+ active_perp_len // PerpMarkets
|
||||
+ active_serum3_len); // open_orders
|
||||
require!(ais.len() == expected_ais, MangoError::SomeError);
|
||||
require_eq!(ais.len(), expected_ais);
|
||||
|
||||
let retriever = FixedOrderAccountRetriever {
|
||||
ais: ais
|
||||
|
@ -501,7 +554,7 @@ impl HealthCache {
|
|||
.token_infos
|
||||
.iter_mut()
|
||||
.find(|t| t.token_index == token_index)
|
||||
.ok_or_else(|| error!(MangoError::SomeError))?;
|
||||
.ok_or_else(|| error_msg!("token index {} not found", token_index))?;
|
||||
entry.balance = cm!(entry.balance + change * entry.oracle_price);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -557,7 +610,7 @@ pub fn new_health_cache(
|
|||
infos
|
||||
.iter()
|
||||
.position(|ti| ti.token_index == token_index)
|
||||
.ok_or_else(|| error!(MangoError::SomeError))
|
||||
.ok_or_else(|| error_msg!("token index {} not found", token_index))
|
||||
}
|
||||
|
||||
for (i, position) in account.tokens.iter_active().enumerate() {
|
||||
|
|
|
@ -129,7 +129,7 @@ impl MangoAccountTokenPositions {
|
|||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(raw_index, p)| p.is_active_for_token(token_index).then(|| (p, raw_index)))
|
||||
.ok_or_else(|| error!(MangoError::SomeError)) // TODO: not found error
|
||||
.ok_or_else(|| error_msg!("position for token index {} not found", token_index))
|
||||
}
|
||||
|
||||
/// Returns
|
||||
|
@ -140,7 +140,7 @@ impl MangoAccountTokenPositions {
|
|||
.iter_mut()
|
||||
.enumerate()
|
||||
.find_map(|(raw_index, p)| p.is_active_for_token(token_index).then(|| (p, raw_index)))
|
||||
.ok_or_else(|| error!(MangoError::SomeError)) // TODO: not found error
|
||||
.ok_or_else(|| error_msg!("position for token index {} not found", token_index))
|
||||
}
|
||||
|
||||
pub fn get_mut_raw(&mut self, raw_token_index: usize) -> &mut TokenPosition {
|
||||
|
@ -186,7 +186,8 @@ impl MangoAccountTokenPositions {
|
|||
}
|
||||
Ok((v, raw_index, bank_index))
|
||||
} else {
|
||||
err!(MangoError::SomeError) // TODO: No free space
|
||||
err!(MangoError::NoFreeTokenPositionIndex)
|
||||
.context(format!("when looking for token index {}", token_index))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,7 +296,7 @@ impl MangoAccountSerum3Orders {
|
|||
|
||||
pub fn create(&mut self, market_index: Serum3MarketIndex) -> Result<&mut Serum3Orders> {
|
||||
if self.find(market_index).is_some() {
|
||||
return err!(MangoError::SomeError); // exists already
|
||||
return err!(MangoError::Serum3OpenOrdersExistAlready);
|
||||
}
|
||||
if let Some(v) = self.values.iter_mut().find(|p| !p.is_active()) {
|
||||
*v = Serum3Orders {
|
||||
|
@ -304,7 +305,7 @@ impl MangoAccountSerum3Orders {
|
|||
};
|
||||
Ok(v)
|
||||
} else {
|
||||
err!(MangoError::SomeError) // no space
|
||||
err!(MangoError::NoFreeSerum3OpenOrdersIndex)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,7 +314,10 @@ impl MangoAccountSerum3Orders {
|
|||
.values
|
||||
.iter()
|
||||
.position(|p| p.is_active_for_market(market_index))
|
||||
.ok_or(MangoError::SomeError)?;
|
||||
.ok_or(error_msg!(
|
||||
"serum3 open orders index {} not found",
|
||||
market_index
|
||||
))?;
|
||||
|
||||
self.values[index].market_index = Serum3MarketIndex::MAX;
|
||||
|
||||
|
@ -549,7 +553,7 @@ impl MangoAccountPerpPositions {
|
|||
if let Some(i) = pos {
|
||||
Ok((&mut self.accounts[i], i))
|
||||
} else {
|
||||
err!(MangoError::SomeError) // TODO: No free space
|
||||
err!(MangoError::NoFreePerpPositionIndex)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -595,10 +599,7 @@ impl MangoAccountPerpPositions {
|
|||
}
|
||||
|
||||
pub fn remove_order(&mut self, slot: usize, quantity: i64) -> Result<()> {
|
||||
require!(
|
||||
self.order_market[slot] != FREE_ORDER_SLOT,
|
||||
MangoError::SomeError
|
||||
);
|
||||
require_neq!(self.order_market[slot], FREE_ORDER_SLOT);
|
||||
let order_side = self.order_side[slot];
|
||||
let perp_market_index = self.order_market[slot];
|
||||
let perp_account = self.get_account_mut_or_create(perp_market_index).unwrap().0;
|
||||
|
|
|
@ -2,7 +2,7 @@ use anchor_lang::prelude::*;
|
|||
use static_assertions::const_assert_eq;
|
||||
use std::mem::size_of;
|
||||
|
||||
use crate::error::MangoError;
|
||||
use crate::error::*;
|
||||
|
||||
use super::TokenIndex;
|
||||
|
||||
|
@ -62,9 +62,11 @@ impl MintInfo {
|
|||
}
|
||||
|
||||
pub fn verify_banks_ais(&self, all_bank_ais: &[AccountInfo]) -> Result<()> {
|
||||
require!(
|
||||
require_msg!(
|
||||
all_bank_ais.iter().map(|ai| ai.key).eq(self.banks().iter()),
|
||||
MangoError::SomeError
|
||||
"the passed banks {:?} don't match banks in mint_info {:?}",
|
||||
all_bank_ais.iter().map(|ai| ai.key).collect::<Vec<_>>(),
|
||||
self.banks()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue