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