1use crate::fuel_prelude::{
2 fuel_crypto::Hasher,
3 fuel_tx::StorageSlot,
4 fuel_types::{Bytes32, Bytes8},
5};
6use sway_ir::{
7 constant::{ConstantContent, ConstantValue},
8 context::Context,
9 irtype::Type,
10 Constant,
11};
12use sway_types::u256::U256;
13
14#[derive(Default)]
17enum InByte8Padding {
18 #[default]
19 Right,
20 Left,
21}
22
23pub(super) fn get_storage_key(storage_field_names: Vec<String>, key: Option<U256>) -> Bytes32 {
27 match key {
28 Some(key) => key.to_be_bytes().into(),
29 None => hash_storage_key_string(&get_storage_key_string(&storage_field_names)),
30 }
31}
32
33pub fn get_storage_key_string(storage_field_names: &[String]) -> String {
34 if storage_field_names.len() == 1 {
35 format!(
36 "{}{}{}",
37 sway_utils::constants::STORAGE_TOP_LEVEL_NAMESPACE,
38 sway_utils::constants::STORAGE_FIELD_SEPARATOR,
39 storage_field_names.last().unwrap(),
40 )
41 } else {
42 format!(
43 "{}{}{}{}{}",
44 sway_utils::constants::STORAGE_TOP_LEVEL_NAMESPACE,
45 sway_utils::constants::STORAGE_NAMESPACE_SEPARATOR,
46 storage_field_names
47 .iter()
48 .take(storage_field_names.len() - 1)
49 .cloned()
50 .collect::<Vec<_>>()
51 .join(sway_utils::constants::STORAGE_NAMESPACE_SEPARATOR),
52 sway_utils::constants::STORAGE_FIELD_SEPARATOR,
53 storage_field_names.last().unwrap(),
54 )
55 }
56}
57
58pub(super) fn get_storage_field_path_and_field_id(
61 storage_field_names: &[String],
62 struct_field_names: &[String],
63) -> (String, Bytes32) {
64 let path = format!(
65 "{}{}",
66 get_storage_key_string(storage_field_names),
67 if struct_field_names.is_empty() {
68 "".to_string()
69 } else {
70 format!(
71 "{}{}",
72 sway_utils::constants::STRUCT_FIELD_SEPARATOR,
73 struct_field_names.join(sway_utils::constants::STRUCT_FIELD_SEPARATOR),
74 )
75 }
76 );
77
78 let id = hash_storage_key_string(&path);
79 (path, id)
80}
81
82fn hash_storage_key_string(storage_key_string: &str) -> Bytes32 {
83 let mut hasher = Hasher::default();
84 hasher.input(sway_utils::constants::STORAGE_DOMAIN);
97 hasher.input(storage_key_string);
98 hasher.finalize()
99}
100
101use uint::construct_uint;
102
103#[allow(
104clippy::assign_op_pattern,
106 clippy::ptr_offset_with_cast,
107 clippy::manual_div_ceil
108)]
109pub(super) fn add_to_b256(x: Bytes32, y: u64) -> Bytes32 {
110 construct_uint! {
111 struct U256(4);
112 }
113 let x = U256::from(*x);
114 let y = U256::from(y);
115 let res: [u8; 32] = (x + y).into();
116 Bytes32::from(res)
117}
118
119pub fn serialize_to_storage_slots(
128 constant: &Constant,
129 context: &Context,
130 storage_field_names: Vec<String>,
131 key: Option<U256>,
132 ty: &Type,
133) -> Vec<StorageSlot> {
134 match &constant.get_content(context).value {
135 ConstantValue::Undef => vec![],
136 ConstantValue::Unit if ty.is_unit(context) => vec![StorageSlot::new(
139 get_storage_key(storage_field_names, key),
140 Bytes32::new([0; 32]),
141 )],
142 ConstantValue::Bool(b) if ty.is_bool(context) => {
143 vec![StorageSlot::new(
144 get_storage_key(storage_field_names, key),
145 Bytes32::new([
146 if *b { 1 } else { 0 },
147 0,
148 0,
149 0,
150 0,
151 0,
152 0,
153 0,
154 0,
155 0,
156 0,
157 0,
158 0,
159 0,
160 0,
161 0,
162 0,
163 0,
164 0,
165 0,
166 0,
167 0,
168 0,
169 0,
170 0,
171 0,
172 0,
173 0,
174 0,
175 0,
176 0,
177 0,
178 ]),
179 )]
180 }
181 ConstantValue::Uint(b) if ty.is_uint8(context) => {
182 vec![StorageSlot::new(
183 get_storage_key(storage_field_names, key),
184 Bytes32::new([
185 *b as u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
186 0, 0, 0, 0, 0, 0, 0, 0,
187 ]),
188 )]
189 }
190 ConstantValue::Uint(n) if ty.is_uint(context) => {
192 vec![StorageSlot::new(
193 get_storage_key(storage_field_names, key),
194 Bytes32::new(
195 n.to_be_bytes()
196 .iter()
197 .cloned()
198 .chain([0; 24].iter().cloned())
199 .collect::<Vec<u8>>()
200 .try_into()
201 .unwrap(),
202 ),
203 )]
204 }
205 ConstantValue::U256(b) if ty.is_uint_of(context, 256) => {
206 vec![StorageSlot::new(
207 get_storage_key(storage_field_names, key),
208 Bytes32::new(b.to_be_bytes()),
209 )]
210 }
211 ConstantValue::B256(b) if ty.is_b256(context) => {
212 vec![StorageSlot::new(
213 get_storage_key(storage_field_names, key),
214 Bytes32::new(b.to_be_bytes()),
215 )]
216 }
217 ConstantValue::Array(_a) if ty.is_array(context) => {
218 unimplemented!("Arrays in storage have not been implemented yet.")
219 }
220 _ if ty.is_string_array(context) || ty.is_struct(context) || ty.is_union(context) => {
221 let mut packed = serialize_to_words(
226 constant.get_content(context),
227 context,
228 ty,
229 InByte8Padding::default(),
230 );
231 packed.extend(vec![
232 Bytes8::new([0; 8]);
233 packed.len().div_ceil(4) * 4 - packed.len()
234 ]);
235
236 assert!(packed.len().is_multiple_of(4));
237
238 let type_size_in_bytes = ty.size(context).in_bytes();
248 assert!(
249 type_size_in_bytes.is_multiple_of(8),
250 "Expected string arrays, structs, and enums to be aligned to word boundary. The type size in bytes was {} and the type was {}.",
251 type_size_in_bytes,
252 ty.as_string(context)
253 );
254
255 let storage_key = get_storage_key(storage_field_names, key);
256 (0..type_size_in_bytes.div_ceil(32))
257 .map(|i| add_to_b256(storage_key, i))
258 .zip((0..packed.len() / 4).map(|i| {
259 Bytes32::new(
260 Vec::from_iter((0..4).flat_map(|j| *packed[4 * i + j]))
261 .try_into()
262 .unwrap(),
263 )
264 }))
265 .map(|(k, r)| StorageSlot::new(k, r))
266 .collect()
267 }
268 _ => vec![],
269 }
270}
271
272fn serialize_to_words(
275 constant: &ConstantContent,
276 context: &Context,
277 ty: &Type,
278 padding: InByte8Padding,
279) -> Vec<Bytes8> {
280 match &constant.value {
281 ConstantValue::Undef => vec![],
282 ConstantValue::Unit if ty.is_unit(context) => vec![Bytes8::new([0; 8])],
283 ConstantValue::Bool(b) if ty.is_bool(context) => match padding {
284 InByte8Padding::Right => {
285 vec![Bytes8::new([if *b { 1 } else { 0 }, 0, 0, 0, 0, 0, 0, 0])]
286 }
287 InByte8Padding::Left => {
288 vec![Bytes8::new([0, 0, 0, 0, 0, 0, 0, if *b { 1 } else { 0 }])]
289 }
290 },
291 ConstantValue::Uint(n) if ty.is_uint8(context) => match padding {
292 InByte8Padding::Right => vec![Bytes8::new([*n as u8, 0, 0, 0, 0, 0, 0, 0])],
293 InByte8Padding::Left => vec![Bytes8::new([0, 0, 0, 0, 0, 0, 0, *n as u8])],
294 },
295 ConstantValue::Uint(n) if ty.is_uint(context) => {
296 vec![Bytes8::new(n.to_be_bytes())]
297 }
298 ConstantValue::U256(b) if ty.is_uint_of(context, 256) => {
299 let b = b.to_be_bytes();
300 Vec::from_iter((0..4).map(|i| Bytes8::new(b[8 * i..8 * i + 8].try_into().unwrap())))
301 }
302 ConstantValue::B256(b) if ty.is_b256(context) => {
303 let b = b.to_be_bytes();
304 Vec::from_iter((0..4).map(|i| Bytes8::new(b[8 * i..8 * i + 8].try_into().unwrap())))
305 }
306 ConstantValue::String(s) if ty.is_string_array(context) => {
307 let mut s = s.clone();
309 s.extend(vec![0; s.len().div_ceil(8) * 8 - s.len()]);
310
311 assert!(s.len() % 8 == 0);
312
313 Vec::from_iter((0..s.len() / 8).map(|i| {
315 Bytes8::new(
316 Vec::from_iter((0..8).map(|j| s[8 * i + j]))
317 .try_into()
318 .unwrap(),
319 )
320 }))
321 }
322 ConstantValue::Array(_) if ty.is_array(context) => {
323 unimplemented!("Arrays in storage have not been implemented yet.")
324 }
325 ConstantValue::Struct(vec) if ty.is_struct(context) => {
326 let field_tys = ty.get_field_types(context);
327 vec.iter()
328 .zip(field_tys.iter())
329 .flat_map(|(f, ty)| serialize_to_words(f, context, ty, InByte8Padding::Right))
332 .collect()
333 }
334 _ if ty.is_union(context) => {
335 let value_size_in_words = ty.size(context).in_words();
336 let constant_size_in_words = constant.ty.size(context).in_words();
337 assert!(value_size_in_words >= constant_size_in_words);
338
339 let padding_size_in_words = value_size_in_words - constant_size_in_words;
347 vec![Bytes8::new([0; 8]); padding_size_in_words as usize]
348 .iter()
349 .cloned()
350 .chain(
351 serialize_to_words(constant, context, &constant.ty, InByte8Padding::Left)
352 .iter()
353 .cloned(),
354 )
355 .collect()
356 }
357 _ => vec![],
358 }
359}