2022-01-21 10:21:46 -08:00
|
|
|
use anchor_lang::prelude::*;
|
2022-07-07 07:55:04 -07:00
|
|
|
use core::fmt::Display;
|
2022-01-21 10:21:46 -08:00
|
|
|
|
2022-02-28 01:36:31 -08:00
|
|
|
// todo: group error blocks by kind
|
|
|
|
// todo: add comments which indicate decimal code for an error
|
2022-02-22 00:10:44 -08:00
|
|
|
#[error_code]
|
|
|
|
pub enum MangoError {
|
2022-01-21 10:21:46 -08:00
|
|
|
#[msg("")]
|
|
|
|
SomeError,
|
2022-07-07 07:55:04 -07:00
|
|
|
#[msg("checked math error")]
|
2022-03-11 00:57:30 -08:00
|
|
|
MathError,
|
|
|
|
#[msg("")]
|
2022-02-28 21:28:12 -08:00
|
|
|
UnexpectedOracle,
|
2022-07-07 07:55:04 -07:00
|
|
|
#[msg("oracle type cannot be determined")]
|
2022-02-28 21:28:12 -08:00
|
|
|
UnknownOracleType,
|
2022-03-02 21:15:28 -08:00
|
|
|
#[msg("")]
|
2022-06-23 01:19:33 -07:00
|
|
|
InvalidFlashLoanTargetCpiProgram,
|
2022-07-07 07:55:04 -07:00
|
|
|
#[msg("health must be positive")]
|
2022-03-03 04:10:20 -08:00
|
|
|
HealthMustBePositive,
|
2022-07-07 07:55:04 -07:00
|
|
|
#[msg("the account is bankrupt")]
|
2022-03-30 01:00:52 -07:00
|
|
|
IsBankrupt,
|
2022-07-07 07:55:04 -07:00
|
|
|
#[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())
|
|
|
|
}
|
2022-01-21 10:21:46 -08:00
|
|
|
}
|
2022-07-07 07:55:04 -07:00
|
|
|
|
|
|
|
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;
|