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_id(
61 storage_field_names: &[String],
62 struct_field_names: &[String],
63) -> Bytes32 {
64 let data = 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 hash_storage_key_string(data)
79}
80
81fn hash_storage_key_string(storage_key_string: String) -> Bytes32 {
82 let mut hasher = Hasher::default();
83 hasher.input(sway_utils::constants::STORAGE_DOMAIN);
96 hasher.input(storage_key_string);
97 hasher.finalize()
98}
99
100use uint::construct_uint;
101
102#[allow(
103clippy::assign_op_pattern,
105 clippy::ptr_offset_with_cast,
106 clippy::manual_div_ceil
107)]
108pub(super) fn add_to_b256(x: Bytes32, y: u64) -> Bytes32 {
109 construct_uint! {
110 struct U256(4);
111 }
112 let x = U256::from(*x);
113 let y = U256::from(y);
114 let res: [u8; 32] = (x + y).into();
115 Bytes32::from(res)
116}
117
118pub fn serialize_to_storage_slots(
127 constant: &Constant,
128 context: &Context,
129 storage_field_names: Vec<String>,
130 key: Option<U256>,
131 ty: &Type,
132) -> Vec<StorageSlot> {
133 match &constant.get_content(context).value {
134 ConstantValue::Undef => vec![],
135 ConstantValue::Unit if ty.is_unit(context) => vec![StorageSlot::new(
138 get_storage_key(storage_field_names, key),
139 Bytes32::new([0; 32]),
140 )],
141 ConstantValue::Bool(b) if ty.is_bool(context) => {
142 vec![StorageSlot::new(
143 get_storage_key(storage_field_names, key),
144 Bytes32::new([
145 if *b { 1 } else { 0 },
146 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 ]),
178 )]
179 }
180 ConstantValue::Uint(b) if ty.is_uint8(context) => {
181 vec![StorageSlot::new(
182 get_storage_key(storage_field_names, key),
183 Bytes32::new([
184 *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,
185 0, 0, 0, 0, 0, 0, 0, 0,
186 ]),
187 )]
188 }
189 ConstantValue::Uint(n) if ty.is_uint(context) => {
191 vec![StorageSlot::new(
192 get_storage_key(storage_field_names, key),
193 Bytes32::new(
194 n.to_be_bytes()
195 .iter()
196 .cloned()
197 .chain([0; 24].iter().cloned())
198 .collect::<Vec<u8>>()
199 .try_into()
200 .unwrap(),
201 ),
202 )]
203 }
204 ConstantValue::U256(b) if ty.is_uint_of(context, 256) => {
205 vec![StorageSlot::new(
206 get_storage_key(storage_field_names, key),
207 Bytes32::new(b.to_be_bytes()),
208 )]
209 }
210 ConstantValue::B256(b) if ty.is_b256(context) => {
211 vec![StorageSlot::new(
212 get_storage_key(storage_field_names, key),
213 Bytes32::new(b.to_be_bytes()),
214 )]
215 }
216 ConstantValue::Array(_a) if ty.is_array(context) => {
217 unimplemented!("Arrays in storage have not been implemented yet.")
218 }
219 _ if ty.is_string_array(context) || ty.is_struct(context) || ty.is_union(context) => {
220 let mut packed = serialize_to_words(
225 constant.get_content(context),
226 context,
227 ty,
228 InByte8Padding::default(),
229 );
230 packed.extend(vec![
231 Bytes8::new([0; 8]);
232 packed.len().div_ceil(4) * 4 - packed.len()
233 ]);
234
235 assert!(packed.len() % 4 == 0);
236
237 let type_size_in_bytes = ty.size(context).in_bytes();
247 assert!(
248 type_size_in_bytes % 8 == 0,
249 "Expected string arrays, structs, and enums to be aligned to word boundary. The type size in bytes was {} and the type was {}.",
250 type_size_in_bytes,
251 ty.as_string(context)
252 );
253
254 let storage_key = get_storage_key(storage_field_names, key);
255 (0..type_size_in_bytes.div_ceil(32))
256 .map(|i| add_to_b256(storage_key, i))
257 .zip((0..packed.len() / 4).map(|i| {
258 Bytes32::new(
259 Vec::from_iter((0..4).flat_map(|j| *packed[4 * i + j]))
260 .try_into()
261 .unwrap(),
262 )
263 }))
264 .map(|(k, r)| StorageSlot::new(k, r))
265 .collect()
266 }
267 _ => vec![],
268 }
269}
270
271fn serialize_to_words(
274 constant: &ConstantContent,
275 context: &Context,
276 ty: &Type,
277 padding: InByte8Padding,
278) -> Vec<Bytes8> {
279 match &constant.value {
280 ConstantValue::Undef => vec![],
281 ConstantValue::Unit if ty.is_unit(context) => vec![Bytes8::new([0; 8])],
282 ConstantValue::Bool(b) if ty.is_bool(context) => match padding {
283 InByte8Padding::Right => {
284 vec![Bytes8::new([if *b { 1 } else { 0 }, 0, 0, 0, 0, 0, 0, 0])]
285 }
286 InByte8Padding::Left => {
287 vec![Bytes8::new([0, 0, 0, 0, 0, 0, 0, if *b { 1 } else { 0 }])]
288 }
289 },
290 ConstantValue::Uint(n) if ty.is_uint8(context) => match padding {
291 InByte8Padding::Right => vec![Bytes8::new([*n as u8, 0, 0, 0, 0, 0, 0, 0])],
292 InByte8Padding::Left => vec![Bytes8::new([0, 0, 0, 0, 0, 0, 0, *n as u8])],
293 },
294 ConstantValue::Uint(n) if ty.is_uint(context) => {
295 vec![Bytes8::new(n.to_be_bytes())]
296 }
297 ConstantValue::U256(b) if ty.is_uint_of(context, 256) => {
298 let b = b.to_be_bytes();
299 Vec::from_iter((0..4).map(|i| Bytes8::new(b[8 * i..8 * i + 8].try_into().unwrap())))
300 }
301 ConstantValue::B256(b) if ty.is_b256(context) => {
302 let b = b.to_be_bytes();
303 Vec::from_iter((0..4).map(|i| Bytes8::new(b[8 * i..8 * i + 8].try_into().unwrap())))
304 }
305 ConstantValue::String(s) if ty.is_string_array(context) => {
306 let mut s = s.clone();
308 s.extend(vec![0; s.len().div_ceil(8) * 8 - s.len()]);
309
310 assert!(s.len() % 8 == 0);
311
312 Vec::from_iter((0..s.len() / 8).map(|i| {
314 Bytes8::new(
315 Vec::from_iter((0..8).map(|j| s[8 * i + j]))
316 .try_into()
317 .unwrap(),
318 )
319 }))
320 }
321 ConstantValue::Array(_) if ty.is_array(context) => {
322 unimplemented!("Arrays in storage have not been implemented yet.")
323 }
324 ConstantValue::Struct(vec) if ty.is_struct(context) => {
325 let field_tys = ty.get_field_types(context);
326 vec.iter()
327 .zip(field_tys.iter())
328 .flat_map(|(f, ty)| serialize_to_words(f, context, ty, InByte8Padding::Right))
331 .collect()
332 }
333 _ if ty.is_union(context) => {
334 let value_size_in_words = ty.size(context).in_words();
335 let constant_size_in_words = constant.ty.size(context).in_words();
336 assert!(value_size_in_words >= constant_size_in_words);
337
338 let padding_size_in_words = value_size_in_words - constant_size_in_words;
346 vec![Bytes8::new([0; 8]); padding_size_in_words as usize]
347 .iter()
348 .cloned()
349 .chain(
350 serialize_to_words(constant, context, &constant.ty, InByte8Padding::Left)
351 .iter()
352 .cloned(),
353 )
354 .collect()
355 }
356 _ => vec![],
357 }
358}