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-08-01 07:53:30 -07:00
|
|
|
#[msg("")]
|
|
|
|
NotImplementedError,
|
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,
|
2023-06-15 01:44:11 -07:00
|
|
|
#[msg("health must be positive or not decrease")]
|
|
|
|
HealthMustBePositiveOrIncrease, // outdated name is kept for backwards compatibility
|
2022-07-19 05:56:26 -07:00
|
|
|
#[msg("health must be negative")]
|
|
|
|
HealthMustBeNegative,
|
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,
|
2022-07-19 02:36:23 -07:00
|
|
|
#[msg("bank vault has insufficent funds")]
|
|
|
|
InsufficentBankVaultFunds,
|
2022-08-22 02:02:01 -07:00
|
|
|
#[msg("account is currently being liquidated")]
|
|
|
|
BeingLiquidated,
|
2022-09-01 09:07:57 -07:00
|
|
|
#[msg("invalid bank")]
|
|
|
|
InvalidBank,
|
|
|
|
#[msg("account profitability is mismatched")]
|
|
|
|
ProfitabilityMismatch,
|
|
|
|
#[msg("cannot settle with self")]
|
|
|
|
CannotSettleWithSelf,
|
|
|
|
#[msg("perp position does not exist")]
|
|
|
|
PerpPositionDoesNotExist,
|
|
|
|
#[msg("max settle amount must be greater than zero")]
|
|
|
|
MaxSettleAmountMustBeGreaterThanZero,
|
2022-09-07 07:10:49 -07:00
|
|
|
#[msg("the perp position has open orders or unprocessed fill events")]
|
|
|
|
HasOpenPerpOrders,
|
2022-11-10 06:47:11 -08:00
|
|
|
#[msg("an oracle does not reach the confidence threshold")]
|
|
|
|
OracleConfidence,
|
|
|
|
#[msg("an oracle is stale")]
|
|
|
|
OracleStale,
|
2022-11-17 23:58:56 -08:00
|
|
|
#[msg("settlement amount must always be positive")]
|
|
|
|
SettlementAmountMustBePositive,
|
2022-11-25 04:45:17 -08:00
|
|
|
#[msg("bank utilization has reached limit")]
|
|
|
|
BankBorrowLimitReached,
|
|
|
|
#[msg("bank net borrows has reached limit - this is an intermittent error - the limit will reset regularly")]
|
|
|
|
BankNetBorrowsLimitReached,
|
2022-12-06 00:25:24 -08:00
|
|
|
#[msg("token position does not exist")]
|
|
|
|
TokenPositionDoesNotExist,
|
2022-12-20 02:24:19 -08:00
|
|
|
#[msg("token deposits into accounts that are being liquidated must bring their health above the init threshold")]
|
|
|
|
DepositsIntoLiquidatingMustRecover,
|
2023-01-04 00:24:40 -08:00
|
|
|
#[msg("token is in reduce only mode")]
|
|
|
|
TokenInReduceOnlyMode,
|
|
|
|
#[msg("market is in reduce only mode")]
|
|
|
|
MarketInReduceOnlyMode,
|
2023-01-12 00:12:55 -08:00
|
|
|
#[msg("group is halted")]
|
|
|
|
GroupIsHalted,
|
2023-01-12 00:07:13 -08:00
|
|
|
#[msg("the perp position has non-zero base lots")]
|
|
|
|
PerpHasBaseLots,
|
|
|
|
#[msg("there are open or unsettled serum3 orders")]
|
|
|
|
HasOpenOrUnsettledSerum3Orders,
|
|
|
|
#[msg("has liquidatable token position")]
|
|
|
|
HasLiquidatableTokenPosition,
|
|
|
|
#[msg("has liquidatable perp base position")]
|
|
|
|
HasLiquidatablePerpBasePosition,
|
2023-02-02 00:00:37 -08:00
|
|
|
#[msg("has liquidatable positive perp pnl")]
|
|
|
|
HasLiquidatablePositivePerpPnl,
|
2023-01-12 04:08:10 -08:00
|
|
|
#[msg("account is frozen")]
|
|
|
|
AccountIsFrozen,
|
2023-01-23 06:02:48 -08:00
|
|
|
#[msg("Init Asset Weight can't be negative")]
|
|
|
|
InitAssetWeightCantBeNegative,
|
2023-01-23 01:45:45 -08:00
|
|
|
#[msg("has open perp taker fills")]
|
|
|
|
HasOpenPerpTakerFills,
|
2023-01-23 05:58:03 -08:00
|
|
|
#[msg("deposit crosses the current group deposit limit")]
|
|
|
|
DepositLimit,
|
2023-01-26 11:27:39 -08:00
|
|
|
#[msg("instruction is disabled")]
|
|
|
|
IxIsDisabled,
|
2023-02-02 00:00:37 -08:00
|
|
|
#[msg("no liquidatable perp base position")]
|
|
|
|
NoLiquidatablePerpBasePosition,
|
2023-02-14 01:44:51 -08:00
|
|
|
#[msg("perp order id not found on the orderbook")]
|
|
|
|
PerpOrderIdNotFound,
|
2023-03-20 06:02:35 -07:00
|
|
|
#[msg("HealthRegions allow only specific instructions between Begin and End")]
|
|
|
|
HealthRegionBadInnerInstruction,
|
2023-04-13 03:44:12 -07:00
|
|
|
#[msg("token is in force close")]
|
|
|
|
TokenInForceClose,
|
2023-05-05 00:11:25 -07:00
|
|
|
#[msg("incorrect number of health accounts")]
|
|
|
|
InvalidHealthAccountCount,
|
2023-05-15 01:40:41 -07:00
|
|
|
#[msg("would self trade")]
|
|
|
|
WouldSelfTrade,
|
2023-10-07 12:27:19 -07:00
|
|
|
#[msg("token conditional swap oracle price is not in execution range")]
|
2023-07-03 05:09:11 -07:00
|
|
|
TokenConditionalSwapPriceNotInRange,
|
2023-10-07 12:27:19 -07:00
|
|
|
#[msg("token conditional swap is expired")]
|
|
|
|
TokenConditionalSwapExpired,
|
|
|
|
#[msg("token conditional swap is not available yet")]
|
|
|
|
TokenConditionalSwapNotStarted,
|
|
|
|
#[msg("token conditional swap was already started")]
|
|
|
|
TokenConditionalSwapAlreadyStarted,
|
|
|
|
#[msg("token conditional swap it not set")]
|
|
|
|
TokenConditionalSwapNotSet,
|
|
|
|
#[msg("token conditional swap trigger did not reach min_buy_token")]
|
|
|
|
TokenConditionalSwapMinBuyTokenNotReached,
|
|
|
|
#[msg("token conditional swap cannot pay incentive")]
|
|
|
|
TokenConditionalSwapCantPayIncentive,
|
|
|
|
#[msg("token conditional swap taker price is too low")]
|
|
|
|
TokenConditionalSwapTakerPriceTooLow,
|
|
|
|
#[msg("token conditional swap index and id don't match")]
|
|
|
|
TokenConditionalSwapIndexIdMismatch,
|
|
|
|
#[msg("token conditional swap volume is too small compared to the cost of starting it")]
|
|
|
|
TokenConditionalSwapTooSmallForStartIncentive,
|
2023-10-11 06:19:53 -07:00
|
|
|
#[msg("token conditional swap type cannot be started")]
|
|
|
|
TokenConditionalSwapTypeNotStartable,
|
2023-12-05 06:39:24 -08:00
|
|
|
#[msg("a bank in the health account list should be writable but is not")]
|
|
|
|
HealthAccountBankNotWritable,
|
|
|
|
#[msg("the market does not allow limit orders too far from the current oracle value")]
|
|
|
|
Serum3PriceBandExceeded,
|
2022-12-06 00:25:24 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl MangoError {
|
|
|
|
pub fn error_code(&self) -> u32 {
|
|
|
|
(*self).into()
|
|
|
|
}
|
2022-07-07 07:55:04 -07:00
|
|
|
}
|
|
|
|
|
2023-02-14 01:44:51 -08:00
|
|
|
pub trait IsAnchorErrorWithCode {
|
|
|
|
fn is_anchor_error_with_code(&self, code: u32) -> bool;
|
2023-08-07 07:15:45 -07:00
|
|
|
fn is_oracle_error(&self) -> bool;
|
2023-02-14 01:44:51 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> IsAnchorErrorWithCode for anchor_lang::Result<T> {
|
|
|
|
fn is_anchor_error_with_code(&self, code: u32) -> bool {
|
|
|
|
match self {
|
|
|
|
Err(Error::AnchorError(error)) => error.error_code_number == code,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
2023-08-07 07:15:45 -07:00
|
|
|
fn is_oracle_error(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
Err(Error::AnchorError(e)) => {
|
|
|
|
e.error_code_number == MangoError::OracleConfidence.error_code()
|
|
|
|
|| e.error_code_number == MangoError::OracleStale.error_code()
|
|
|
|
}
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
2023-02-14 01:44:51 -08:00
|
|
|
}
|
|
|
|
|
2022-07-07 07:55:04 -07:00
|
|
|
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)*))
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-12-06 00:25:24 -08:00
|
|
|
/// Creates an Error with a particular message, using format!() style arguments
|
|
|
|
///
|
2023-01-11 05:32:15 -08:00
|
|
|
/// Example: error_msg_typed!(TokenPositionMissing, "index {} not found", index)
|
2022-12-06 00:25:24 -08:00
|
|
|
#[macro_export]
|
|
|
|
macro_rules! error_msg_typed {
|
2023-01-11 05:32:15 -08:00
|
|
|
($code:expr, $($arg:tt)*) => {
|
|
|
|
error!($code).context(format!($($arg)*))
|
2022-12-06 00:25:24 -08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-07-07 07:55:04 -07:00
|
|
|
/// Like anchor's require!(), but with a customizable message
|
|
|
|
///
|
2023-01-11 05:32:15 -08:00
|
|
|
/// Example: require_msg!(condition, "the condition on account {} was violated", account_key);
|
2022-07-07 07:55:04 -07:00
|
|
|
#[macro_export]
|
|
|
|
macro_rules! require_msg {
|
|
|
|
($invariant:expr, $($arg:tt)*) => {
|
|
|
|
if !($invariant) {
|
2022-08-02 00:04:02 -07:00
|
|
|
return Err(error_msg!($($arg)*));
|
2022-07-07 07:55:04 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-01-11 05:32:15 -08:00
|
|
|
/// Like anchor's require!(), but with a customizable message and type
|
|
|
|
///
|
|
|
|
/// Example: require_msg_typed!(condition, "the condition on account {} was violated", account_key);
|
|
|
|
#[macro_export]
|
|
|
|
macro_rules! require_msg_typed {
|
|
|
|
($invariant:expr, $code:expr, $($arg:tt)*) => {
|
|
|
|
if !($invariant) {
|
|
|
|
return Err(error_msg_typed!($code, $($arg)*));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-07-07 07:55:04 -07:00
|
|
|
pub use error_msg;
|
2022-12-06 00:25:24 -08:00
|
|
|
pub use error_msg_typed;
|
2022-07-07 07:55:04 -07:00
|
|
|
pub use require_msg;
|
2023-01-11 05:32:15 -08:00
|
|
|
pub use require_msg_typed;
|