use std::{
fmt::Debug,
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign},
};
use ethers::types::U256;
use eyre::{eyre, Result};
use paste::paste;
macro_rules! conversion_fns {
($($type_name:ident),*) => {
$(
paste! {
fn [<from_ $type_name:snake>](value: $type_name) -> Result<Self> {
Self::try_from(value).map_err(|_| {
eyre!(
r#"Failed to convert {type} to underlying FixedPointValue type:
{type} value: {value:?}
Underlying range: {min:?} to {max:?}
"#,
type = stringify!($type_name),
min = Self::MIN,
max = Self::MAX,
)
})
}
fn [<to_ $type_name:snake>](self) -> Result<$type_name> {
self.try_into().map_err(|_| {
eyre!(
"Failed to convert underlying FixedPointValue to {type}: {self:?}",
type = stringify!($type_name)
)
})
}
})*
};
}
pub trait FixedPointValue:
Copy
+ Debug
+ Default
+ Sized
+ Eq
+ PartialEq
+ Ord
+ PartialOrd
+ Add<Output = Self>
+ AddAssign
+ Sub<Output = Self>
+ SubAssign
+ Mul<Output = Self>
+ MulAssign
+ Div<Output = Self>
+ DivAssign
+ Rem<Output = Self>
+ TryFrom<u128>
+ TryInto<u128>
+ From<u64>
+ TryFrom<U256>
+ TryInto<U256>
{
const MIN: Self;
const MAX: Self;
const MAX_DECIMALS: u8 = 18;
fn is_signed() -> bool {
Self::MIN.is_negative()
}
fn is_negative(&self) -> bool {
self < &Self::from(0)
}
fn is_positive(&self) -> bool {
!self.is_negative()
}
fn is_zero(&self) -> bool {
self == &Self::from(0)
}
fn flip_sign(self) -> Self {
if !Self::is_signed() {
panic!("Cannot flip sign of unsigned type: {self:?}");
}
Self::from(0) - self
}
fn flip_sign_if(self, condition: bool) -> Self {
if condition {
self.flip_sign()
} else {
self
}
}
fn abs(self) -> Self {
self.flip_sign_if(self.is_negative())
}
fn unsigned_abs(self) -> U256 {
if self.is_negative() && self == Self::MIN {
let abs = (self + 1.into()).flip_sign();
return abs.to_u256().unwrap() + U256::from(1);
}
self.abs().to_u256().unwrap()
}
conversion_fns!(u128, U256);
}