Skip to main content

graph_native/
types.rs

1use old_bigdecimal::BigDecimal as OldBigDecimal;
2use num_traits::Signed;
3use serde::{Deserialize, Serialize};
4use std::fmt;
5use std::ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub};
6use std::str::FromStr;
7
8pub use num_bigint::Sign as BigIntSign;
9
10/// 20-byte Ethereum address.
11#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
12pub struct Address(pub [u8; 20]);
13
14impl Address {
15    pub fn zero() -> Self {
16        Self([0u8; 20])
17    }
18
19    pub fn from_string(s: &str) -> Self {
20        let s = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")).unwrap_or(s);
21        let mut bytes = [0u8; 20];
22        if let Ok(decoded) = hex::decode(s) {
23            let len = decoded.len().min(20);
24            bytes[..len].copy_from_slice(&decoded[..len]);
25        }
26        Self(bytes)
27    }
28
29    /// Construct an Address from a byte slice.
30    /// Mirrors graph-ts `Address.fromBytes()` — strict: requires exactly 20 bytes.
31    /// Panics if input length is not 20, matching graph-ts semantics.
32    pub fn from_bytes(bytes: &[u8]) -> Self {
33        assert!(
34            bytes.len() == 20,
35            "Bytes of length {} can not be converted to 20 byte addresses",
36            bytes.len()
37        );
38        let mut arr = [0u8; 20];
39        arr.copy_from_slice(bytes);
40        Self(arr)
41    }
42
43    pub fn to_hex_string(&self) -> String {
44        format!("0x{}", hex::encode(self.0))
45    }
46
47    pub fn to_bytes(&self) -> Vec<u8> {
48        self.0.to_vec()
49    }
50}
51
52impl fmt::Display for Address {
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        write!(f, "{}", self.to_hex_string())
55    }
56}
57
58#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
59pub struct BigInt(pub num_bigint::BigInt);
60
61impl BigInt {
62    const MAX_BITS: usize = 435_412;
63
64    fn clamp(value: num_bigint::BigInt) -> Self {
65        let bits = value.bits() + 1;
66        if bits > Self::MAX_BITS {
67            eprintln!(
68                "[graph-native] WARN: BigInt exceeded max bits ({bits}), returning zero"
69            );
70            return Self::zero();
71        }
72        Self(value)
73    }
74
75    pub fn zero() -> Self {
76        Self(num_bigint::BigInt::from(0))
77    }
78
79    pub fn from(v: i32) -> Self {
80        Self(num_bigint::BigInt::from(v))
81    }
82
83    pub fn from_i64(v: i64) -> Self {
84        Self(num_bigint::BigInt::from(v))
85    }
86
87    pub fn from_str(s: &str) -> Self {
88        match num_bigint::BigInt::from_str(s) {
89            Ok(v) => Self::clamp(v),
90            Err(_) => {
91                eprintln!("[graph-native] WARN: BigInt::from_str failed for '{s}', returning zero");
92                Self::zero()
93            }
94        }
95    }
96
97    pub fn from_be_bytes(bytes: &[u8]) -> Self {
98        Self::clamp(num_bigint::BigInt::from_signed_bytes_be(bytes))
99    }
100
101    pub fn from_unsigned_be_bytes(bytes: &[u8]) -> Self {
102        Self::clamp(num_bigint::BigInt::from_bytes_be(BigIntSign::Plus, bytes))
103    }
104
105    pub fn is_zero(&self) -> bool {
106        self.0 == num_bigint::BigInt::from(0)
107    }
108
109    pub fn abs(&self) -> Self {
110        Self(self.0.clone().abs())
111    }
112
113    pub fn pow(&self, exp: u32) -> Self {
114        let mut result = num_bigint::BigInt::from(1);
115        for _ in 0..exp {
116            result *= self.0.clone();
117        }
118        Self::clamp(result)
119    }
120
121    pub fn to_big_decimal(&self) -> BigDecimal {
122        BigDecimal::from_bigint(self)
123    }
124
125    pub fn to_i32(&self) -> i32 {
126        self.0.to_string().parse::<i32>().unwrap_or(0)
127    }
128
129    pub fn plus(&self, other: &BigInt) -> BigInt {
130        Self::clamp(self.0.clone() + other.0.clone())
131    }
132
133    pub fn minus(&self, other: &BigInt) -> BigInt {
134        Self::clamp(self.0.clone() - other.0.clone())
135    }
136
137    pub fn times(&self, other: &BigInt) -> BigInt {
138        Self::clamp(self.0.clone() * other.0.clone())
139    }
140
141    pub fn div(&self, other: &BigInt) -> BigInt {
142        if other.is_zero() {
143            return Self::zero();
144        }
145        Self::clamp(self.0.clone() / other.0.clone())
146    }
147
148    pub fn r#mod(&self, other: &BigInt) -> BigInt {
149        if other.is_zero() {
150            return Self::zero();
151        }
152        Self::clamp(self.0.clone() % other.0.clone())
153    }
154}
155
156impl From<i8> for BigInt {
157    fn from(value: i8) -> Self {
158        Self(value.into())
159    }
160}
161
162impl From<i16> for BigInt {
163    fn from(value: i16) -> Self {
164        Self(value.into())
165    }
166}
167
168impl From<i32> for BigInt {
169    fn from(value: i32) -> Self {
170        Self(value.into())
171    }
172}
173
174impl From<i64> for BigInt {
175    fn from(value: i64) -> Self {
176        Self(value.into())
177    }
178}
179
180impl From<u64> for BigInt {
181    fn from(value: u64) -> Self {
182        Self(value.into())
183    }
184}
185
186impl Add for &BigInt {
187    type Output = BigInt;
188
189    fn add(self, rhs: Self) -> Self::Output {
190        self.plus(rhs)
191    }
192}
193
194impl Sub for &BigInt {
195    type Output = BigInt;
196
197    fn sub(self, rhs: Self) -> Self::Output {
198        self.minus(rhs)
199    }
200}
201
202impl Mul for &BigInt {
203    type Output = BigInt;
204
205    fn mul(self, rhs: Self) -> Self::Output {
206        self.times(rhs)
207    }
208}
209
210impl Div for &BigInt {
211    type Output = BigInt;
212
213    fn div(self, rhs: Self) -> Self::Output {
214        BigInt::div(self, rhs)
215    }
216}
217
218impl Rem for &BigInt {
219    type Output = BigInt;
220
221    fn rem(self, rhs: Self) -> Self::Output {
222        BigInt::r#mod(self, rhs)
223    }
224}
225
226impl Shl<i32> for BigInt {
227    type Output = BigInt;
228
229    fn shl(self, rhs: i32) -> Self::Output {
230        Self::clamp(self.0 << (rhs as usize))
231    }
232}
233
234impl Shr<i32> for BigInt {
235    type Output = BigInt;
236
237    fn shr(self, rhs: i32) -> Self::Output {
238        Self::clamp(self.0 >> (rhs as usize))
239    }
240}
241
242impl Neg for BigInt {
243    type Output = BigInt;
244
245    fn neg(self) -> Self::Output {
246        Self(-self.0)
247    }
248}
249
250impl PartialOrd for BigInt {
251    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
252        self.0.partial_cmp(&other.0)
253    }
254}
255
256impl fmt::Display for BigInt {
257    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258        write!(f, "{}", self.0)
259    }
260}
261
262#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
263pub struct BigDecimal(pub OldBigDecimal);
264
265impl BigDecimal {
266    pub fn zero() -> Self {
267        use old_bigdecimal::Zero;
268
269        Self(OldBigDecimal::zero())
270    }
271
272    fn normalized(self) -> Self {
273        if self == Self::zero() {
274            return Self::zero();
275        }
276
277        let big_decimal = self.0.with_prec(34);
278        let (bigint, exp) = big_decimal.as_bigint_and_exponent();
279        let (sign, mut digits) = bigint.to_radix_be(10);
280        let trailing_count = digits.iter().rev().take_while(|i| **i == 0).count();
281        digits.truncate(digits.len().saturating_sub(trailing_count));
282        let int_val = num_bigint::BigInt::from_radix_be(sign, &digits, 10)
283            .unwrap_or_else(|| num_bigint::BigInt::from(0));
284        let scale = exp - trailing_count as i64;
285
286        Self(OldBigDecimal::new(int_val, scale))
287    }
288
289    pub fn from_str(s: &str) -> Self {
290        match OldBigDecimal::from_str(s) {
291            Ok(v) => Self(v).normalized(),
292            Err(_) => {
293                eprintln!(
294                    "[graph-native] WARN: BigDecimal::from_str failed for '{s}', returning zero"
295                );
296                Self::zero()
297            }
298        }
299    }
300
301    pub fn from_bigint(v: &BigInt) -> Self {
302        Self(OldBigDecimal::new(v.0.clone(), 0)).normalized()
303    }
304
305    pub fn plus(&self, other: &BigDecimal) -> BigDecimal {
306        Self(self.0.clone() + other.0.clone()).normalized()
307    }
308
309    pub fn minus(&self, other: &BigDecimal) -> BigDecimal {
310        Self(self.0.clone() - other.0.clone()).normalized()
311    }
312
313    pub fn times(&self, other: &BigDecimal) -> BigDecimal {
314        Self(self.0.clone() * other.0.clone()).normalized()
315    }
316
317    pub fn div(&self, other: &BigDecimal) -> BigDecimal {
318        if other == &Self::zero() {
319            return Self::zero();
320        }
321        Self(self.0.clone() / other.0.clone()).normalized()
322    }
323}
324
325impl PartialOrd for BigDecimal {
326    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
327        self.0.partial_cmp(&other.0)
328    }
329}
330
331impl fmt::Display for BigDecimal {
332    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333        write!(f, "{}", self.0)
334    }
335}