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    pub fn bit_and(&self, other: &BigInt) -> BigInt {
156        Self::clamp(&self.0 & &other.0)
157    }
158
159    pub fn bit_or(&self, other: &BigInt) -> BigInt {
160        Self::clamp(&self.0 | &other.0)
161    }
162
163    pub fn bit_xor(&self, other: &BigInt) -> BigInt {
164        Self::clamp(&self.0 ^ &other.0)
165    }
166}
167
168impl From<i8> for BigInt {
169    fn from(value: i8) -> Self {
170        Self(value.into())
171    }
172}
173
174impl From<i16> for BigInt {
175    fn from(value: i16) -> Self {
176        Self(value.into())
177    }
178}
179
180impl From<i32> for BigInt {
181    fn from(value: i32) -> Self {
182        Self(value.into())
183    }
184}
185
186impl From<i64> for BigInt {
187    fn from(value: i64) -> Self {
188        Self(value.into())
189    }
190}
191
192impl From<u64> for BigInt {
193    fn from(value: u64) -> Self {
194        Self(value.into())
195    }
196}
197
198impl Add for &BigInt {
199    type Output = BigInt;
200
201    fn add(self, rhs: Self) -> Self::Output {
202        self.plus(rhs)
203    }
204}
205
206impl Sub for &BigInt {
207    type Output = BigInt;
208
209    fn sub(self, rhs: Self) -> Self::Output {
210        self.minus(rhs)
211    }
212}
213
214impl Mul for &BigInt {
215    type Output = BigInt;
216
217    fn mul(self, rhs: Self) -> Self::Output {
218        self.times(rhs)
219    }
220}
221
222impl Div for &BigInt {
223    type Output = BigInt;
224
225    fn div(self, rhs: Self) -> Self::Output {
226        BigInt::div(self, rhs)
227    }
228}
229
230impl Rem for &BigInt {
231    type Output = BigInt;
232
233    fn rem(self, rhs: Self) -> Self::Output {
234        BigInt::r#mod(self, rhs)
235    }
236}
237
238impl Shl<i32> for BigInt {
239    type Output = BigInt;
240
241    fn shl(self, rhs: i32) -> Self::Output {
242        Self::clamp(self.0 << (rhs as usize))
243    }
244}
245
246impl Shr<i32> for BigInt {
247    type Output = BigInt;
248
249    fn shr(self, rhs: i32) -> Self::Output {
250        Self::clamp(self.0 >> (rhs as usize))
251    }
252}
253
254impl Neg for BigInt {
255    type Output = BigInt;
256
257    fn neg(self) -> Self::Output {
258        Self(-self.0)
259    }
260}
261
262impl PartialOrd for BigInt {
263    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
264        self.0.partial_cmp(&other.0)
265    }
266}
267
268impl fmt::Display for BigInt {
269    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270        write!(f, "{}", self.0)
271    }
272}
273
274#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
275pub struct BigDecimal(pub OldBigDecimal);
276
277impl BigDecimal {
278    pub fn zero() -> Self {
279        use old_bigdecimal::Zero;
280
281        Self(OldBigDecimal::zero())
282    }
283
284    fn normalized(self) -> Self {
285        if self == Self::zero() {
286            return Self::zero();
287        }
288
289        let big_decimal = self.0.with_prec(34);
290        let (bigint, exp) = big_decimal.as_bigint_and_exponent();
291        let (sign, mut digits) = bigint.to_radix_be(10);
292        let trailing_count = digits.iter().rev().take_while(|i| **i == 0).count();
293        digits.truncate(digits.len().saturating_sub(trailing_count));
294        let int_val = num_bigint::BigInt::from_radix_be(sign, &digits, 10)
295            .unwrap_or_else(|| num_bigint::BigInt::from(0));
296        let scale = exp - trailing_count as i64;
297
298        Self(OldBigDecimal::new(int_val, scale))
299    }
300
301    pub fn from_str(s: &str) -> Self {
302        match OldBigDecimal::from_str(s) {
303            Ok(v) => Self(v).normalized(),
304            Err(_) => {
305                eprintln!(
306                    "[graph-native] WARN: BigDecimal::from_str failed for '{s}', returning zero"
307                );
308                Self::zero()
309            }
310        }
311    }
312
313    pub fn from_bigint(v: &BigInt) -> Self {
314        Self(OldBigDecimal::new(v.0.clone(), 0)).normalized()
315    }
316
317    pub fn plus(&self, other: &BigDecimal) -> BigDecimal {
318        Self(self.0.clone() + other.0.clone()).normalized()
319    }
320
321    pub fn minus(&self, other: &BigDecimal) -> BigDecimal {
322        Self(self.0.clone() - other.0.clone()).normalized()
323    }
324
325    pub fn times(&self, other: &BigDecimal) -> BigDecimal {
326        Self(self.0.clone() * other.0.clone()).normalized()
327    }
328
329    pub fn div(&self, other: &BigDecimal) -> BigDecimal {
330        if other == &Self::zero() {
331            return Self::zero();
332        }
333        Self(self.0.clone() / other.0.clone()).normalized()
334    }
335}
336
337impl PartialOrd for BigDecimal {
338    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
339        self.0.partial_cmp(&other.0)
340    }
341}
342
343impl fmt::Display for BigDecimal {
344    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345        write!(f, "{}", self.0)
346    }
347}