1const ANCHOR_ERROR_BASE: u32 = 6000;
24
25macro_rules! define_kvault_errors {
26 (
27 $(
28 $(#[doc = $doc:expr])*
29 $variant:ident = $offset:literal => $msg:expr
30 ),*
31 $(,)?
32 ) => {
33 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34 #[repr(u32)]
35 pub enum KvaultError {
36 $(
37 $(#[doc = $doc])*
38 $variant = ANCHOR_ERROR_BASE + $offset,
39 )*
40 }
41
42 impl KvaultError {
43 pub const fn error_code(self) -> u32 {
45 self as u32
46 }
47
48 pub const fn message(self) -> &'static str {
50 match self {
51 $(Self::$variant => $msg,)*
52 }
53 }
54
55 pub const fn from_error_code(code: u32) -> Option<Self> {
57 match code {
58 $(x if x == ANCHOR_ERROR_BASE + $offset => Some(Self::$variant),)*
59 _ => None,
60 }
61 }
62 }
63
64 impl core::fmt::Display for KvaultError {
65 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
66 write!(f, "{}", self.message())
67 }
68 }
69
70 impl TryFrom<u32> for KvaultError {
71 type Error = u32;
72
73 fn try_from(code: u32) -> Result<Self, Self::Error> {
76 Self::from_error_code(code).ok_or(code)
77 }
78 }
79 };
80}
81
82define_kvault_errors! {
83 DepositAmountsZero = 1000 => "Cannot deposit zero tokens",
85 SharesIssuedAmountDoesNotMatch = 1001 => "Post check failed on share issued",
87 MathOverflow = 1002 => "Math operation overflowed",
89 IntegerOverflow = 1003 => "Integer conversion overflowed",
91 WithdrawAmountBelowMinimum = 1004 => "Withdrawn amount is below minimum",
93 TooMuchLiquidityToWithdraw = 1005 => "TooMuchLiquidityToWithdraw",
95 ReserveAlreadyExists = 1006 => "ReserveAlreadyExists",
97 ReserveNotPartOfAllocations = 1007 => "ReserveNotPartOfAllocations",
99 CouldNotDeserializeAccountAsReserve = 1008 => "CouldNotDeserializeAccountAsReserve",
101 ReserveNotProvidedInTheAccounts = 1009 => "ReserveNotProvidedInTheAccounts",
103 ReserveAccountAndKeyMismatch = 1010 => "ReserveAccountAndKeyMismatch",
105 OutOfRangeOfReserveIndex = 1011 => "OutOfRangeOfReserveIndex",
107 CannotFindReserveInAllocations = 1012 => "OutOfRangeOfReserveIndex",
109 InvestAmountBelowMinimum = 1013 => "Invested amount is below minimum",
111 AdminAuthorityIncorrect = 1014 => "AdminAuthorityIncorrect",
113 BaseVaultAuthorityIncorrect = 1015 => "BaseVaultAuthorityIncorrect",
115 BaseVaultAuthorityBumpIncorrect = 1016 => "BaseVaultAuthorityBumpIncorrect",
117 TokenMintIncorrect = 1017 => "TokenMintIncorrect",
119 TokenMintDecimalsIncorrect = 1018 => "TokenMintDecimalsIncorrect",
121 TokenVaultIncorrect = 1019 => "TokenVaultIncorrect",
123 SharesMintDecimalsIncorrect = 1020 => "SharesMintDecimalsIncorrect",
125 SharesMintIncorrect = 1021 => "SharesMintIncorrect",
127 InitialAccountingIncorrect = 1022 => "InitialAccountingIncorrect",
129 ReserveIsStale = 1023 => "Reserve is stale and must be refreshed before any operation",
131 NotEnoughLiquidityDisinvestedToSendToUser = 1024 => "Not enough liquidity disinvested to send to user",
133 BPSValueTooBig = 1025 => "BPS value is greater than 10000",
135 DepositAmountBelowMinimum = 1026 => "Deposited amount is below minimum",
137 ReserveSpaceExhausted = 1027 => "Vault have no space for new reserves",
139 CannotWithdrawFromEmptyVault = 1028 => "Cannot withdraw from empty vault",
141 TokensDepositedAmountDoesNotMatch = 1029 => "TokensDepositedAmountDoesNotMatch",
143 AmountToWithdrawDoesNotMatch = 1030 => "Amount to withdraw does not match",
145 LiquidityToWithdrawDoesNotMatch = 1031 => "Liquidity to withdraw does not match",
147 UserReceivedAmountDoesNotMatch = 1032 => "User received amount does not match",
149 SharesBurnedAmountDoesNotMatch = 1033 => "Shares burned amount does not match",
151 DisinvestedLiquidityAmountDoesNotMatch = 1034 => "Disinvested liquidity amount does not match",
153 SharesMintedAmountDoesNotMatch = 1035 => "SharesMintedAmountDoesNotMatch",
155 AUMDecreasedAfterInvest = 1036 => "AUM decreased after invest",
157 AUMBelowPendingFees = 1037 => "AUM is below pending fees",
159 DepositAmountsZeroShares = 1038 => "Deposit amount results in 0 shares",
161 WithdrawResultsInZeroShares = 1039 => "Withdraw amount results in 0 shares",
163 CannotWithdrawZeroShares = 1040 => "Cannot withdraw zero shares",
165 ManagementFeeGreaterThanMaxAllowed = 1041 => "Management fee is greater than maximum allowed",
167 VaultAUMZero = 1042 => "Vault assets under management are empty",
169 MissingReserveForBatchRefresh = 1043 => "Missing reserve for batch refresh",
171 MinWithdrawAmountTooBig = 1044 => "Min withdraw amount is too big",
173 InvestTooSoon = 1045 => "Invest is called too soon after last invest",
175 WrongAdminOrAllocationAdmin = 1046 => "Wrong admin or allocation admin",
177 ReserveHasNonZeroAllocationOrCTokens = 1047 => "Reserve has non-zero allocation or ctokens so cannot be removed",
179 DepositAmountGreaterThanRequestedAmount = 1048 => "Deposit amount is greater than requested amount",
181 WithdrawAmountLessThanWithdrawalPenalty = 1049 => "Withdraw amount is less than withdrawal penalty",
183 CannotWithdrawZeroLamports = 1050 => "Cannot withdraw 0 lamports",
185 NoUpgradeAuthority = 1051 => "Cannot initialize global config because there is no upgrade authority to the program",
187 WithdrawalFeeBPSGreaterThanMaxAllowed = 1052 => "Withdrawal fee BPS is greater than maximum allowed",
189 WithdrawalFeeLamportsGreaterThanMaxAllowed = 1053 => "Withdrawal fee lamports is greater than maximum allowed",
191 ReserveNotWhitelisted = 1054 => "Reserve is not whitelisted",
193 InvalidBoolLikeValue = 1055 => "Invalid bool-like value passed in (should be 0 or 1)",
195 AUMDecreasedMoreThanExpected = 1056 => "AUM decreased more than expected during redeem in kind",
197 RewardTopupAmountZero = 1057 => "Reward topup amount is zero",
198 RewardTopupAmountNotExpected = 1058 => "Reward topup is not as expected after transfer",
199 RewardWithdrawAmountZero = 1059 => "Reward withdraw amount is zero",
200 RewardWithdrawAmountNotExpected = 1060 => "Reward withdraw is not as expected after transfer",
201 RewardsStaleForFeeUpdate = 1061 => "Rewards are stale - must be refreshed before updating fees",
202 VaultDepositCapReached = 1062 => "Vault deposit cap reached",
204 MaxInvestAmountMustBeGreaterThanZero = 1063 => "max_amount must be greater than 0",
206}
207
208impl std::error::Error for KvaultError {}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 #[test]
215 fn verify_error_codes() {
216 assert_eq!(KvaultError::DepositAmountsZero.error_code(), 7000);
217 assert_eq!(KvaultError::MathOverflow.error_code(), 7002);
218 assert_eq!(KvaultError::AUMDecreasedMoreThanExpected.error_code(), 7056);
219 assert_eq!(KvaultError::RewardsStaleForFeeUpdate.error_code(), 7061);
220 assert_eq!(KvaultError::VaultDepositCapReached.error_code(), 7062);
221 assert_eq!(
222 KvaultError::MaxInvestAmountMustBeGreaterThanZero.error_code(),
223 7063
224 );
225
226 assert_eq!(
227 KvaultError::from_error_code(7000),
228 Some(KvaultError::DepositAmountsZero)
229 );
230 assert_eq!(
231 KvaultError::from_error_code(7056),
232 Some(KvaultError::AUMDecreasedMoreThanExpected)
233 );
234 assert_eq!(KvaultError::from_error_code(5000), None);
235 assert_eq!(KvaultError::from_error_code(9999), None);
236
237 assert_eq!(
239 KvaultError::MathOverflow.to_string(),
240 "Math operation overflowed"
241 );
242
243 assert_eq!(KvaultError::try_from(7002), Ok(KvaultError::MathOverflow));
245 assert_eq!(KvaultError::try_from(9999), Err(9999));
246 }
247}