starknet_rust_providers/sequencer/models/
state_update.rs

1use serde::Deserialize;
2use serde_with::serde_as;
3use starknet_rust_core::{
4    serde::unsigned_field_element::UfeHex,
5    types::{Felt, MigratedCompiledClassItem},
6};
7use std::collections::HashMap;
8
9use super::Block;
10
11#[serde_as]
12#[derive(Debug, Deserialize)]
13#[cfg_attr(feature = "no_unknown_fields", serde(deny_unknown_fields))]
14pub struct StateUpdateWithBlock {
15    pub state_update: StateUpdate,
16    pub block: Block,
17}
18
19#[serde_as]
20#[derive(Debug, Deserialize)]
21#[cfg_attr(feature = "no_unknown_fields", serde(deny_unknown_fields))]
22pub struct StateUpdate {
23    #[serde_as(as = "Option<UfeHex>")]
24    pub block_hash: Option<Felt>,
25    #[serde_as(as = "Option<UfeHex>")]
26    pub new_root: Option<Felt>,
27    #[serde_as(as = "Option<UfeHex>")]
28    pub old_root: Option<Felt>,
29    pub state_diff: StateDiff,
30}
31
32#[serde_as]
33#[derive(Debug, Deserialize)]
34#[cfg_attr(feature = "no_unknown_fields", serde(deny_unknown_fields))]
35pub struct StateDiff {
36    #[serde_as(as = "HashMap<UfeHex, _>")]
37    pub storage_diffs: HashMap<Felt, Vec<StorageDiff>>,
38    pub deployed_contracts: Vec<DeployedContract>,
39    #[serde_as(as = "Vec<UfeHex>")]
40    pub old_declared_contracts: Vec<Felt>,
41    pub declared_classes: Vec<DeclaredContract>,
42    pub migrated_compiled_classes: Option<Vec<DeclaredContract>>,
43    #[serde(default)]
44    #[serde_as(as = "HashMap<UfeHex, UfeHex>")]
45    pub nonces: HashMap<Felt, Felt>,
46    pub replaced_classes: Vec<DeployedContract>,
47}
48
49#[serde_as]
50#[derive(Debug, Deserialize)]
51#[cfg_attr(feature = "no_unknown_fields", serde(deny_unknown_fields))]
52pub struct StorageDiff {
53    #[serde_as(as = "UfeHex")]
54    pub key: Felt,
55    #[serde_as(as = "UfeHex")]
56    pub value: Felt,
57}
58
59#[serde_as]
60#[derive(Debug, Deserialize)]
61#[cfg_attr(feature = "no_unknown_fields", serde(deny_unknown_fields))]
62pub struct DeployedContract {
63    #[serde_as(as = "UfeHex")]
64    pub address: Felt,
65    #[serde_as(as = "UfeHex")]
66    pub class_hash: Felt,
67}
68
69#[serde_as]
70#[derive(Debug, Deserialize)]
71#[cfg_attr(feature = "no_unknown_fields", serde(deny_unknown_fields))]
72pub struct DeclaredContract {
73    #[serde_as(as = "UfeHex")]
74    pub class_hash: Felt,
75    #[serde_as(as = "UfeHex")]
76    pub compiled_class_hash: Felt,
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
85    fn test_state_update_deser() {
86        let raw =
87            include_str!("../../../test-data/raw_gateway_responses/get_state_update/1_success.txt");
88
89        let state_update: StateUpdate = serde_json::from_str(raw).unwrap();
90
91        let storage_diff = &state_update
92            .state_diff
93            .storage_diffs
94            .get(
95                &Felt::from_hex(
96                    "0x197b9913e67947b0605934ec72db497d341a0199282c1da6d4aae46b17e0e76",
97                )
98                .unwrap(),
99            )
100            .unwrap()[0];
101
102        assert_eq!(
103            storage_diff.key,
104            Felt::from_hex("0x34c0d833897dbc937dd35e9f49a5184aecd6ab47829de999f9587549f82d0e")
105                .unwrap()
106        );
107        assert_eq!(
108            storage_diff.value,
109            Felt::from_hex("0x74fd8ca6e84097ad9ae00febfb690ad032fc4477155ec2193382c1b30b5e12b")
110                .unwrap()
111        );
112    }
113
114    #[test]
115    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
116    fn test_pending_state_update_deser() {
117        let raw = include_str!(
118            "../../../test-data/raw_gateway_responses/get_state_update/2_pending_block.txt"
119        );
120
121        serde_json::from_str::<StateUpdate>(raw).unwrap();
122    }
123
124    #[test]
125    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
126    fn test_state_update_deser_with_nonce_changes() {
127        let raw = include_str!(
128            "../../../test-data/raw_gateway_responses/get_state_update/4_with_nonce_changes.txt"
129        );
130
131        let state_update: StateUpdate = serde_json::from_str(raw).unwrap();
132        assert_eq!(state_update.state_diff.nonces.len(), 2);
133    }
134
135    #[test]
136    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
137    fn test_state_update_deser_with_declare_v2() {
138        let raw = include_str!(
139            "../../../test-data/raw_gateway_responses/get_state_update/5_with_declare_v2.txt"
140        );
141        serde_json::from_str::<StateUpdate>(raw).unwrap();
142    }
143
144    #[test]
145    #[ignore = "block with the same criteria not found in alpha-sepolia yet"]
146    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
147    fn test_state_update_deser_with_replaced_classes() {
148        let raw = include_str!(
149            "../../../test-data/raw_gateway_responses/get_state_update/6_with_replaced_classes.txt"
150        );
151
152        let state_update: StateUpdate = serde_json::from_str(raw).unwrap();
153        assert_eq!(state_update.state_diff.replaced_classes.len(), 1);
154    }
155
156    #[test]
157    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
158    fn test_state_update_deser_with_deployed_contracts() {
159        let raw = include_str!(
160            "../../../test-data/raw_gateway_responses/get_state_update/7_with_deployed_contracts.txt"
161        );
162
163        let state_update: StateUpdate = serde_json::from_str(raw).unwrap();
164        assert_eq!(state_update.state_diff.deployed_contracts.len(), 5);
165    }
166
167    #[test]
168    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
169    fn test_state_diff_migrated_compiled_classes_none() {
170        let raw = include_str!(
171            "../../../test-data/raw_gateway_responses/get_state_update/7_with_deployed_contracts.txt"
172        );
173
174        let state_update: StateUpdate = serde_json::from_str(raw).unwrap();
175        assert!(state_update.state_diff.migrated_compiled_classes.is_none());
176    }
177
178    #[test]
179    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
180    fn test_state_update_deser_with_migrated_compiled_classes() {
181        let raw = include_str!(
182            "../../../test-data/raw_gateway_responses/get_state_update/8_with_migrated_compiled_classes.txt"
183        );
184
185        let state_update: StateUpdate = serde_json::from_str(raw).unwrap();
186        assert!(state_update.state_diff.migrated_compiled_classes.is_some());
187        assert_eq!(
188            state_update
189                .state_diff
190                .migrated_compiled_classes
191                .as_ref()
192                .unwrap()
193                .len(),
194            2
195        );
196    }
197
198    #[test]
199    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
200    fn test_state_update_conversion_pending_without_old_root() {
201        let raw = include_str!(
202            "../../../test-data/raw_gateway_responses/get_state_update/9_pending_without_old_root.txt"
203        );
204
205        let state_update: StateUpdate = serde_json::from_str(raw).unwrap();
206        let converted: starknet_rust_core::types::MaybePreConfirmedStateUpdate =
207            state_update.try_into().unwrap();
208
209        match converted {
210            starknet_rust_core::types::MaybePreConfirmedStateUpdate::PreConfirmedUpdate(update) => {
211                assert!(update.old_root.is_none());
212            }
213            _ => panic!("Expected PreConfirmedUpdate variant"),
214        }
215    }
216}