use thiserror::Error;
use crate::math::quantities::{MathError, QuoteLots, SignedQuoteLots, Slot};
#[derive(Clone, Debug, Eq, PartialEq, Error)]
pub enum ProgramError {
#[error("Custom program error: {0:#x}")]
Custom(u32),
#[error("Invalid argument")]
InvalidArgument,
#[error("Invalid instruction data")]
InvalidInstructionData,
#[error("Invalid account data")]
InvalidAccountData,
#[error("Account data too small")]
AccountDataTooSmall,
#[error("Insufficient funds")]
InsufficientFunds,
#[error("Incorrect program id")]
IncorrectProgramId,
#[error("Missing required signature")]
MissingRequiredSignature,
#[error("Account already initialized")]
AccountAlreadyInitialized,
#[error("Uninitialized account")]
UninitializedAccount,
#[error("Not enough account keys")]
NotEnoughAccountKeys,
#[error("Account borrow failed")]
AccountBorrowFailed,
#[error("Max seed length exceeded")]
MaxSeedLengthExceeded,
#[error("Invalid seeds")]
InvalidSeeds,
#[error("Borsh IO error")]
BorshIoError,
#[error("Account not rent exempt")]
AccountNotRentExempt,
#[error("Unsupported sysvar")]
UnsupportedSysvar,
#[error("Illegal owner")]
IllegalOwner,
#[error("Max accounts data allocations exceeded")]
MaxAccountsDataAllocationsExceeded,
#[error("Invalid realloc")]
InvalidRealloc,
#[error("Max instruction trace length exceeded")]
MaxInstructionTraceLengthExceeded,
#[error("Builtin programs must consume compute units")]
BuiltinProgramsMustConsumeComputeUnits,
#[error("Invalid account owner")]
InvalidAccountOwner,
#[error("Arithmetic overflow")]
ArithmeticOverflow,
#[error("Account is immutable")]
Immutable,
#[error("Incorrect authority")]
IncorrectAuthority,
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum MarginError {
#[error("Insufficient funds")]
InsufficientFunds,
#[error("Invalid state change")]
InvalidStateChange,
#[error("Overflow")]
Overflow,
#[error("Math error: {0:?}")]
Math(
#[source]
#[from]
MathError,
),
#[error("Mark price error: {0:?}")]
MarkPrice(ProgramError),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum RiskAction {
View,
Liquidation { current_slot: Slot },
PlacingOrder { current_slot: Slot },
Funding { current_slot: Slot },
Withdrawal { current_slot: Slot },
ADL { current_slot: Slot },
}
impl RiskAction {
#[inline]
pub const fn current_slot(&self) -> Slot {
match self {
Self::View => Slot::ZERO,
Self::Liquidation { current_slot } => *current_slot,
Self::PlacingOrder { current_slot } => *current_slot,
Self::Funding { current_slot } => *current_slot,
Self::Withdrawal { current_slot } => *current_slot,
Self::ADL { current_slot } => *current_slot,
}
}
#[inline]
pub const fn as_index(&self) -> usize {
match self {
Self::View => 0,
Self::Liquidation { .. } => 1,
Self::PlacingOrder { .. } => 2,
Self::Funding { .. } => 3,
Self::Withdrawal { .. } => 4,
Self::ADL { .. } => 5,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum RiskState {
Healthy,
Unhealthy,
Underwater,
ZeroCollateralNoPositions,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum RiskTier {
Safe = 0,
AtRisk = 1,
Cancellable = 2,
Liquidatable = 3,
BackstopLiquidatable = 4,
HighRisk = 5,
}
#[derive(Debug, Clone)]
pub struct MarginState {
pub initial_margin: QuoteLots,
pub effective_collateral: SignedQuoteLots,
}
impl MarginState {
pub fn new(initial_margin: QuoteLots, effective_collateral: SignedQuoteLots) -> Self {
Self {
initial_margin,
effective_collateral,
}
}
pub fn risk_state(&self) -> Result<RiskState, MarginError> {
let collateral = self.effective_collateral;
let initial_margin = self.initial_margin.checked_as_signed()?;
if collateral < SignedQuoteLots::ZERO {
Ok(RiskState::Underwater)
} else if collateral == SignedQuoteLots::ZERO {
if self.initial_margin == QuoteLots::ZERO {
Ok(RiskState::ZeroCollateralNoPositions)
} else {
Ok(RiskState::Underwater)
}
} else if collateral >= initial_margin {
Ok(RiskState::Healthy)
} else {
Ok(RiskState::Unhealthy)
}
}
}