solana_safe_math/
lib.rs

1//! # Anchor Safe Math
2//!
3//! `anchor_safe_math` is a collection of helper numeric operation functions that removes the 
4//! verbosity of checking for overflow, underflow and division by zero errors.
5//! 
6//! # Examples
7//!
8//! ```
9//! use solana_safe_math::{SafeMath};
10//! use solana_program::{entrypoint::ProgramResult};
11//! 
12//! fn process_init_escrow(
13//!   accounts: &[AccountInfo],
14//!   amount: u64,
15//!   program_id: &Pubkey
16//! ) -> ProgramResult {
17//!   let val = 10_u64;
18//!  
19//!   val.safe_add(amount)?;
20//!   val.safe_sub(amount)?;
21//!   val.safe_mul(amount)?;
22//!   val.safe_div(amount)?;
23//!   val.safe_pow(8_u32)?;
24//! }
25//! ```
26use solana_program::program_error::ProgramError;
27use thiserror::Error;
28use std::{
29  result::Result as StdResult
30};
31
32#[derive(Error, Debug, Copy, Clone)]
33pub enum ErrorCode {
34  #[error("overflow")]
35  Overflow,
36  #[error("underflow")]
37  Underflow,
38  #[error("division by zero")]
39  DivisionByZero,
40}
41
42impl From<ErrorCode> for ProgramError {
43  fn from(e:ErrorCode) -> Self {
44    ProgramError::Custom(e as u32)
45  }
46}
47
48/// Defines a set of safe math operations that return a `ProgramError` which is expected in an anchor instruction execution.
49pub trait SafeMath {
50  type Output;
51
52  fn safe_add(&self, rhs: Self::Output) -> StdResult<Self::Output, ProgramError>;
53  fn safe_sub(&self, rhs: Self::Output) -> StdResult<Self::Output, ProgramError>;
54  fn safe_div(&self, rhs: Self::Output) -> StdResult<Self::Output, ProgramError>;
55  fn safe_mul(&self, rhs: Self::Output) -> StdResult<Self::Output, ProgramError>;
56  fn safe_pow(&self, exp: u32) -> StdResult<Self::Output, ProgramError>;
57}
58
59macro_rules! safe_math {
60  ($type: ident) => {
61    /// $type implementation of the SafeMath trait
62    impl SafeMath for $type {
63      type Output = $type;
64
65      fn safe_add(&self, rhs: Self::Output) -> StdResult<Self::Output, ProgramError> {
66        match self.checked_add(rhs) {
67          Some(result) => Ok(result),
68          None => return Err(ErrorCode::Overflow.into())
69        }
70      }
71    
72      fn safe_sub(&self, rhs: Self::Output) -> StdResult<Self::Output, ProgramError> {
73        match self.checked_sub(rhs) {
74          Some(result) => Ok(result),
75          None => return Err(ErrorCode::Underflow.into())
76        }
77      }
78
79      fn safe_mul(&self, rhs: Self::Output) -> StdResult<Self::Output, ProgramError> {
80        match self.checked_mul(rhs) {
81          Some(result) => Ok(result),
82          None => return Err(ErrorCode::Underflow.into())
83        }
84      }
85
86      fn safe_div(&self, rhs: Self::Output) -> StdResult<Self::Output, ProgramError> {
87        match self.checked_div(rhs) {
88          Some(result) => Ok(result),
89          None => return Err(ErrorCode::DivisionByZero.into())
90        }
91      }
92
93      fn safe_pow(&self, exp: u32) -> StdResult<Self::Output, ProgramError> {
94        match self.checked_pow(exp) {
95          Some(result) => Ok(result),
96          None => return Err(ErrorCode::Overflow.into())
97        }
98      }
99    }
100  }
101}
102
103safe_math!(u128);
104safe_math!(u64);
105safe_math!(u32);
106safe_math!(u16);
107safe_math!(u8);