hdp_primitives/processed_types/
uint256.rs

1//! This module contains the `Uint256` type, which is a 256-bit unsigned integer.
2//! This is compatible with Cairo `uint256` type.
3
4use alloy::primitives::{hex::FromHex, B256};
5use anyhow::Result;
6use serde::{Deserialize, Serialize};
7use serde_with::serde_as;
8use starknet::core::serde::unsigned_field_element::UfeHex;
9use starknet_crypto::FieldElement;
10use std::str::FromStr;
11
12use crate::utils::bytes_to_hex_string;
13
14#[serde_as]
15#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, Hash)]
16pub struct Uint256 {
17    #[serde_as(as = "UfeHex")]
18    pub low: FieldElement,
19    #[serde_as(as = "UfeHex")]
20    pub high: FieldElement,
21}
22
23impl Uint256 {
24    pub fn from_strs(high: &str, low: &str) -> Result<Self> {
25        Ok(Self {
26            high: FieldElement::from_hex_be(high)?,
27            low: FieldElement::from_hex_be(low)?,
28        })
29    }
30
31    pub fn from_felts(high: FieldElement, low: FieldElement) -> Self {
32        Self { high, low }
33    }
34
35    pub fn from_le_hex_str(hex_str: &str) -> Result<Self> {
36        let clean_hex = hex_str.trim_start_matches("0x");
37        let mut fix_hex: B256 = B256::from_hex(clean_hex)?;
38        fix_hex.reverse();
39
40        let high_part = fix_hex[..16].to_vec();
41        let low_part = fix_hex[16..].to_vec();
42
43        Ok(Self {
44            high: FieldElement::from_hex_be(&bytes_to_hex_string(&high_part))?,
45            low: FieldElement::from_hex_be(&bytes_to_hex_string(&low_part))?,
46        })
47    }
48
49    pub fn from_be_hex_str(hex_str: &str) -> Result<Self> {
50        let clean_hex = hex_str.trim_start_matches("0x");
51        let padded_hex = format!("{:0>64}", clean_hex);
52        let (high_part, low_part) = padded_hex.split_at(32);
53        Ok(Self {
54            high: FieldElement::from_hex_be(&format!("0x{}", high_part))?,
55            low: FieldElement::from_hex_be(&format!("0x{}", low_part))?,
56        })
57    }
58
59    /// combine_parts_into_big_endian_hex
60    pub fn to_combined_string(&self) -> B256 {
61        // Ensure both parts are exactly 32 hex characters long
62        let high_padded = format!(
63            "{:0>32}",
64            bytes_to_hex_string(&self.high.to_bytes_be()[16..])
65        )
66        .trim_start_matches("0x")
67        .to_string();
68        let low_padded = format!(
69            "{:0>32}",
70            bytes_to_hex_string(&self.low.to_bytes_be()[16..])
71        )
72        .trim_start_matches("0x")
73        .to_string();
74
75        B256::from_str(&format!("0x{}{}", high_padded, low_padded)).unwrap()
76    }
77}
78
79#[cfg(test)]
80mod tests {
81
82    use starknet::macros::felt;
83
84    use super::*;
85
86    #[test]
87    fn test_combine_parts_into_big_endian_hex() {
88        let uint256 = Uint256::from_felts(
89            FieldElement::from_hex_be("0x988c19313bcbfb19fcc4da12e3adb46c").unwrap(),
90            FieldElement::from_hex_be("0xf6fbdd08af91b1d8df80c6e755159f1").unwrap(),
91        );
92        assert_eq!(
93            uint256.to_combined_string(),
94            B256::from_str("0x988c19313bcbfb19fcc4da12e3adb46c0f6fbdd08af91b1d8df80c6e755159f1")
95                .unwrap()
96        );
97
98        let uint256 = Uint256::from_felts(
99            felt!("0x988c19313bcbfb19fcc4da12e3adb46"),
100            felt!("0xf6fbdd08af91b1d8df80c6e755159f1"),
101        );
102        assert_eq!(
103            uint256.to_combined_string(),
104            B256::from_str("0x0988c19313bcbfb19fcc4da12e3adb460f6fbdd08af91b1d8df80c6e755159f1")
105                .unwrap()
106        );
107
108        let uint256 = Uint256::from_felts(
109            felt!("0x988c19313bcbfb19fcc4da12e3adb4"),
110            felt!("0xf6fbdd08af91b1d8df80c6e755159f1"),
111        );
112        assert_eq!(
113            uint256.to_combined_string(),
114            B256::from_str("0x00988c19313bcbfb19fcc4da12e3adb40f6fbdd08af91b1d8df80c6e755159f1")
115                .unwrap()
116        );
117    }
118
119    #[test]
120    fn test_split_big_endian_hex_into_parts() {
121        let hex_str = "0x60870c80ce4e1d0c35e34f08b1648e8a4fdc7818eea7caedbd316c63a3863562";
122        let result = Uint256::from_be_hex_str(hex_str).unwrap();
123        assert_eq!(
124            result,
125            Uint256::from_felts(
126                felt!("0x60870c80ce4e1d0c35e34f08b1648e8a"),
127                felt!("0x4fdc7818eea7caedbd316c63a3863562"),
128            )
129        );
130        assert_eq!(result.to_combined_string().to_string(), hex_str);
131    }
132
133    #[test]
134    fn test_split_little_endian_hex_into_parts() {
135        let hex_str = "0x8ddadb3a246d9988d78871b11dca322a2df53381bfacb9edc42cedfd263b691d";
136        let result = Uint256::from_le_hex_str(hex_str).unwrap();
137        assert_eq!(
138            result,
139            Uint256::from_felts(
140                felt!("0x1d693b26fded2cc4edb9acbf8133f52d"),
141                felt!("0x2a32ca1db17188d788996d243adbda8d"),
142            )
143        );
144    }
145
146    #[test]
147    fn test_uint256_serde() {
148        let target = Uint256::from_felts(
149            felt!("0x1d693b26fded2cc4edb9acbf8133f52d"),
150            felt!("0x2a32ca1db17188d788996d243adbda8d"),
151        );
152        let string = serde_json::to_string_pretty(&target).unwrap();
153        let json_file = std::fs::read_to_string("./fixtures/uint256.json").unwrap();
154        let expected = json_file.trim();
155        assert_eq!(string, expected);
156    }
157}