ethers_primitives/int/
signed.rs

1use std::{
2    fmt::Display,
3    ops::{Add, Mul, Sub},
4};
5
6use hex::FromHexError;
7use num::{bigint::ToBigInt, BigInt, FromPrimitive, Signed, ToPrimitive};
8use serde::{de, Deserialize, Serialize};
9
10use crate::{BytesVisitor, FromEtherHex, ToEtherHex};
11
12use concat_idents::concat_idents;
13
14use thiserror::Error;
15
16#[derive(Debug, Error)]
17pub enum SignedError {
18    #[error("OutOfRange: {0}")]
19    OutOfRange(String),
20
21    #[error("ToBigUnit: {0}")]
22    ToBigUnit(String),
23    #[error("FromHex: {0}")]
24    FromHex(#[from] FromHexError),
25}
26
27/// `int<M>` type mapping
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
29pub struct Int<const BITS: usize>(pub [u8; 32]);
30
31fn to_bytes32(value: BigInt, bits: usize) -> Result<[u8; 32], SignedError> {
32    if value.bits() as usize > bits {
33        return Err(SignedError::OutOfRange(format!(
34            "{} convert to uint<{}> failed",
35            value, bits
36        )));
37    }
38
39    let bytes = value.to_signed_bytes_be();
40
41    let mut buff = if value.is_negative() {
42        [0xffu8; 32]
43    } else {
44        [0u8; 32]
45    };
46
47    buff[(32 - bytes.len())..].copy_from_slice(&bytes);
48
49    Ok(buff)
50}
51
52impl<const BITS: usize> Int<BITS> {
53    /// Create `Unit<BITS>` from [`ToBigInt`].
54    /// Returns [`OutOfRange`](SignedError::OutOfRange) or [`ToBigUnit`](SignedError::ToBigUnit) if failed.
55    pub fn new<N: ToBigInt + Signed>(value: N) -> Result<Self, SignedError> {
56        if let Some(value) = value.to_bigint() {
57            to_bytes32(value, BITS).map(|c| Self(c))
58        } else {
59            Err(SignedError::ToBigUnit(
60                "convert input into BigUnit failed".to_owned(),
61            ))
62        }
63    }
64}
65
66impl<const BITS: usize> Display for Int<BITS> {
67    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68        let lead_ones = self.0.iter().take_while(|c| **c == 0xff).count();
69        let lead_zeros = self.0.iter().take_while(|c| **c == 0x00).count();
70
71        if lead_ones > 0 {
72            write!(f, "{}", (&self.0[(lead_ones - 1)..]).to_eth_value_hex())
73        } else {
74            write!(f, "{}", (&self.0[lead_zeros..]).to_eth_value_hex())
75        }
76    }
77}
78
79impl<const BITS: usize> TryFrom<&str> for Int<BITS> {
80    type Error = SignedError;
81    fn try_from(v: &str) -> Result<Self, Self::Error> {
82        let value = Vec::<u8>::from_eth_hex(v)?;
83
84        let lead_ones = value.iter().take_while(|c| **c == 0xff).count();
85
86        if value.len() - lead_ones + 1 > BITS / 8 {
87            return Err(SignedError::OutOfRange(format!(
88                "{} convert to int<{}> failed",
89                v, BITS
90            )));
91        }
92
93        let mut buff = if lead_ones > 0 {
94            [0xffu8; 32]
95        } else {
96            [0x0u8; 32]
97        };
98
99        buff[(32 - (value.len() - lead_ones))..].copy_from_slice(&value[lead_ones..]);
100
101        Ok(Int(buff))
102    }
103}
104
105impl<const BITS: usize> From<Int<BITS>> for BigInt {
106    fn from(value: Int<BITS>) -> Self {
107        BigInt::from(&value)
108    }
109}
110
111impl<const BITS: usize> From<&Int<BITS>> for BigInt {
112    fn from(value: &Int<BITS>) -> Self {
113        BigInt::from_signed_bytes_be(&value.0[(32 - BITS / 8)..])
114    }
115}
116
117impl<const BITS: usize> PartialOrd for Int<BITS> {
118    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
119        BigInt::from(self).partial_cmp(&BigInt::from(other))
120    }
121}
122
123impl<const BITS: usize> Sub for Int<BITS> {
124    type Output = Int<BITS>;
125
126    fn sub(self, rhs: Self) -> Self::Output {
127        // underflow will panic
128        Self::new(BigInt::from(self) - BigInt::from(rhs)).unwrap()
129    }
130}
131
132impl<const BITS: usize, N> Sub<N> for Int<BITS>
133where
134    N: ToBigInt,
135{
136    type Output = Int<BITS>;
137
138    fn sub(self, rhs: N) -> Self::Output {
139        // underflow will panic
140        Self::new(BigInt::from(self) - rhs.to_bigint().unwrap()).unwrap()
141    }
142}
143
144impl<const BITS: usize> Mul for Int<BITS> {
145    type Output = Int<BITS>;
146
147    fn mul(self, rhs: Self) -> Self::Output {
148        Self::new(BigInt::from(self) + BigInt::from(rhs)).unwrap()
149    }
150}
151
152impl<const BITS: usize, N> Mul<N> for Int<BITS>
153where
154    N: ToBigInt,
155{
156    type Output = Int<BITS>;
157
158    fn mul(self, rhs: N) -> Self::Output {
159        Self::new(BigInt::from(self) * rhs.to_bigint().unwrap()).unwrap()
160    }
161}
162
163impl<const BITS: usize> Add for Int<BITS> {
164    type Output = Int<BITS>;
165
166    fn add(self, rhs: Self) -> Self::Output {
167        Self::new(BigInt::from(self) + BigInt::from(rhs)).unwrap()
168    }
169}
170
171impl<const BITS: usize, N> Add<N> for Int<BITS>
172where
173    N: ToBigInt,
174{
175    type Output = Int<BITS>;
176
177    fn add(self, rhs: N) -> Self::Output {
178        Self::new(BigInt::from(self) + rhs.to_bigint().unwrap()).unwrap()
179    }
180}
181
182impl<const BITS: usize> Serialize for Int<BITS> {
183    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
184    where
185        S: serde::Serializer,
186    {
187        if serializer.is_human_readable() {
188            serializer.serialize_str(&self.to_string())
189        } else {
190            // for rlp/eip712/abi serializers
191            let name = format!("int{}", BITS);
192
193            let static_name = unsafe { &*(&name as *const String) };
194
195            serializer.serialize_newtype_struct(&static_name, &self.0)
196        }
197    }
198}
199
200struct IntVisitor<const BITS: usize>;
201
202impl<'de, const BITS: usize> de::Visitor<'de> for IntVisitor<BITS> {
203    type Value = Int<BITS>;
204    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
205        write!(formatter, "expect string/number")
206    }
207
208    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
209    where
210        E: de::Error,
211    {
212        Int::<BITS>::try_from(v).map_err(de::Error::custom)
213    }
214
215    fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
216    where
217        E: de::Error,
218    {
219        self.visit_u128(v as u128)
220    }
221
222    fn visit_u128<E>(self, v: u128) -> Result<Self::Value, E>
223    where
224        E: de::Error,
225    {
226        let value = BigInt::from_u128(v)
227            .ok_or(SignedError::ToBigUnit(format!(
228                "convert {} to BigInt failed",
229                v
230            )))
231            .map_err(de::Error::custom)?;
232
233        if value.bits() as usize > BITS {
234            return Err(SignedError::OutOfRange(format!(
235                "{} convert to uint<{}> failed",
236                value, BITS
237            )))
238            .map_err(de::Error::custom);
239        }
240
241        let value = to_bytes32(value, BITS).map_err(de::Error::custom)?;
242
243        Ok(Int(value))
244    }
245}
246
247impl<'de, const BITS: usize> Deserialize<'de> for Int<BITS> {
248    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
249    where
250        D: serde::Deserializer<'de>,
251    {
252        if deserializer.is_human_readable() {
253            deserializer.deserialize_any(IntVisitor)
254        } else {
255            // for rlp/eip712/abi serializers
256            let name = format!("int{}", BITS);
257
258            let static_name = unsafe { &*(&name as *const String) };
259
260            let value = deserializer
261                .deserialize_newtype_struct(static_name.as_str(), BytesVisitor::default())?;
262
263            let lead_ones = value.iter().take_while(|c| **c == 0xff).count();
264
265            if value.len() - lead_ones + 1 > BITS / 8 {
266                return Err(SignedError::OutOfRange(format!(
267                    "{} convert to uint<{}> failed",
268                    value.to_eth_hex(),
269                    BITS
270                )))
271                .map_err(de::Error::custom);
272            }
273
274            let mut buff = if lead_ones > 0 {
275                [0xffu8; 32]
276            } else {
277                [0x0u8; 32]
278            };
279
280            buff[(32 - (value.len() - lead_ones))..].copy_from_slice(&value[lead_ones..]);
281
282            Ok(Int(buff))
283        }
284    }
285}
286
287/// impl from builin num
288macro_rules! convert_builtin_signed {
289    ($t: ident,$expr: tt,$($tails:tt),+) => {
290        impl<const BITS: usize> From<$expr> for $t<BITS> {
291            fn from(value: $expr) -> Self {
292                let value: BigInt = value.to_bigint().expect("usize to bigint");
293                // overflow panic
294                Int::new(value).expect("Overflow")
295            }
296        }
297
298        concat_idents!(to_fn=to_,$expr  {
299            impl<const BITS: usize> From<$t<BITS>> for Option<$expr> {
300                fn from(value: $t<BITS>) -> Self {
301                    BigInt::from(value).to_fn()
302                }
303            }
304        });
305
306
307
308        convert_builtin_signed!($t,$($tails),*);
309    };
310    ($t: ident, $expr: tt) => {
311        impl<const BITS: usize> From<$expr> for $t<BITS> {
312            fn from(value: $expr) -> Self {
313                let value: BigInt = value.to_bigint().expect("usize to biguint");
314                // overflow panic
315                Int::new(value).expect("Overflow")
316            }
317        }
318
319        concat_idents!(to_fn=to_,$expr  {
320            impl<const BITS: usize> From<$t<BITS>> for Option<$expr> {
321                fn from(value: $t<BITS>) -> Self {
322                    BigInt::from(value).to_fn()
323                }
324            }
325        });
326    };
327}
328
329convert_builtin_signed!(Int, isize, i128, i64, i32, i16, i8);
330
331pub type I256 = Int<256>;
332pub type I64 = Int<64>;
333
334#[cfg(test)]
335mod tests {
336    use serde_ethabi::{from_abi, to_abi};
337    use serde_ethrlp::rlp_encode;
338
339    use crate::ToEtherHex;
340
341    use super::*;
342
343    #[test]
344    fn test_arith() {
345        if let Some(value) = Option::<i8>::from(I256::new(-1i8).unwrap()) {
346            assert_eq!(value, -1i8);
347        }
348
349        if let Some(value) = Option::<i8>::from(I256::new(-4i8).unwrap()) {
350            assert_eq!(value, -4i8);
351        }
352
353        let lhs = Int::<8>::new(-1i8).unwrap();
354        let rhs = Int::<8>::new(-4i8).unwrap();
355
356        assert_eq!(lhs > rhs, true);
357
358        assert_eq!((rhs - lhs), Int::<8>::new(-3i8).unwrap());
359
360        assert_eq!(
361            BigInt::from(I256::new(-4isize).unwrap()),
362            BigInt::from_i8(-4).unwrap()
363        );
364    }
365
366    #[test]
367    fn test_rlp() {
368        _ = pretty_env_logger::try_init();
369
370        assert_eq!(
371            rlp_encode(&I256::from(0isize)).unwrap(),
372            rlp_encode(&0usize).unwrap()
373        );
374
375        assert_eq!(
376            rlp_encode(&I256::from(100000isize)).unwrap(),
377            rlp_encode(&100000isize).unwrap()
378        );
379    }
380
381    #[test]
382    fn test_abi() {
383        fn check<const BITS: usize>(value: Int<BITS>, expect: &str) {
384            assert_eq!(to_abi(&value).unwrap().to_eth_hex(), expect);
385
386            let buff = Vec::<u8>::from_eth_hex(expect).unwrap();
387
388            let expect: Int<BITS> = from_abi(buff).unwrap();
389
390            assert_eq!(value, expect);
391        }
392
393        check(
394            I256::from(-69isize),
395            "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbb",
396        );
397
398        check(
399            I256::from(-1isize),
400            "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
401        );
402    }
403}