Better error messages

This commit is contained in:
Christian Kamm 2022-07-07 16:55:04 +02:00
parent 420132cf89
commit f132f30874
9 changed files with 268 additions and 107 deletions

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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);
//

View File

@ -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);

View File

@ -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() {

View File

@ -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() {

View File

@ -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;

View File

@ -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(())
}