dojo_types/
primitive_conversion.rs

1// This is a partial implementation of https://github.com/starknet-io/types-rs/pull/74
2// and is required because signed integers are not coverted from Felt correctly with the
3// current implementation
4// TODO: remove when https://github.com/starknet-io/types-rs/pull/74 is merged.
5
6use core::convert::TryInto;
7
8use starknet::core::types::Felt;
9
10#[derive(Debug, Copy, Clone)]
11pub struct PrimitiveFromFeltError;
12
13impl core::fmt::Display for PrimitiveFromFeltError {
14    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
15        write!(f, "Failed to convert `Felt` into primitive type")
16    }
17}
18
19impl std::error::Error for PrimitiveFromFeltError {
20    fn description(&self) -> &str {
21        "Failed to convert `Felt` into primitive type"
22    }
23}
24
25const MINUS_TWO_BYTES_REPR: [u8; 32] = [
26    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
27    255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 8,
28];
29
30pub trait FromFelt: Sized {
31    fn try_from_felt(value: Felt) -> Result<Self, PrimitiveFromFeltError>;
32}
33
34macro_rules! impl_from_felt {
35    ($into:ty) => {
36        impl FromFelt for $into {
37            fn try_from_felt(value: Felt) -> Result<Self, PrimitiveFromFeltError> {
38                let size_of_type = core::mem::size_of::<$into>();
39                let bytes_le = value.to_bytes_le();
40
41                if bytes_le[size_of_type..].iter().all(|&v| v == 0)
42                    && bytes_le[size_of_type - 1] <= 0b01111111
43                {
44                    Ok(<$into>::from_le_bytes(bytes_le[..size_of_type].try_into().unwrap()))
45                } else if bytes_le[size_of_type..] == MINUS_TWO_BYTES_REPR[size_of_type..]
46                    && bytes_le[size_of_type - 1] >= 0b10000000
47                {
48                    let offsetted_value =
49                        <$into>::from_le_bytes(bytes_le[..size_of_type].try_into().unwrap());
50
51                    offsetted_value.checked_sub(1).ok_or(PrimitiveFromFeltError)
52                } else if bytes_le[24..] == [17, 0, 0, 0, 0, 0, 0, 8] {
53                    return Ok(-1);
54                } else {
55                    Err(PrimitiveFromFeltError)
56                }
57            }
58        }
59    };
60}
61
62impl_from_felt!(i8);
63impl_from_felt!(i16);
64impl_from_felt!(i32);
65impl_from_felt!(i64);
66impl_from_felt!(i128);
67
68pub fn try_from_felt<T: FromFelt>(value: Felt) -> Result<T, PrimitiveFromFeltError> {
69    T::try_from_felt(value)
70}
71
72#[cfg(test)]
73mod tests {
74    use starknet::core::types::Felt;
75
76    use super::try_from_felt;
77
78    #[test]
79    fn test_try_from_felt() {
80        let i_8: i8 = -64;
81        let felt = Felt::from(i_8);
82        let signed_integer = try_from_felt::<i8>(felt).unwrap();
83        assert_eq!(i_8, signed_integer);
84
85        let i_16: i16 = -14293;
86        let felt = Felt::from(i_16);
87        let signed_integer = try_from_felt::<i16>(felt).unwrap();
88        assert_eq!(i_16, signed_integer);
89
90        let i_32: i32 = -194875;
91        let felt = Felt::from(i_32);
92        let signed_integer = try_from_felt::<i32>(felt).unwrap();
93        assert_eq!(i_32, signed_integer);
94
95        let i_64: i64 = -3147483648;
96        let felt = Felt::from(i_64);
97        let signed_integer = try_from_felt::<i64>(felt).unwrap();
98        assert_eq!(i_64, signed_integer);
99
100        let i_128: i128 = -170141183460469231731687303715884105728;
101        let felt = Felt::from(i_128);
102        let signed_integer = try_from_felt::<i128>(felt).unwrap();
103        assert_eq!(i_128, signed_integer);
104    }
105}