1use crate::error::ZError;
2use crate::types::{ZAddress, ZArray, ZBool, ZBytes, ZCallResult, ZRevert, ZString, ZU256};
3use core::convert::TryInto;
4use core::str;
5
6#[inline]
18pub fn read_selector(data: &[u8]) -> Result<&[u8; 4], ZError> {
19 if data.len() < 4 {
20 return Err(ZError::OutOfBounds(4, data.len()));
21 }
22 Ok(data[0..4].try_into().unwrap())
23}
24
25#[inline]
37pub fn skip_selector(data: &[u8]) -> Result<&[u8], ZError> {
38 if data.len() < 4 {
39 return Err(ZError::OutOfBounds(4, data.len()));
40 }
41 Ok(&data[4..])
42}
43
44#[inline(always)]
47pub fn peek_word(data: &[u8], offset: usize) -> Result<&[u8; 32], ZError> {
48 if offset + 32 > data.len() {
49 return Err(ZError::OutOfBounds(offset + 32, data.len()));
50 }
51 let slice = &data[offset..offset + 32];
58 let array_ref: &[u8; 32] = slice
59 .try_into()
60 .map_err(|_| ZError::Custom("Slice conversion failed"))?;
61 Ok(array_ref)
62}
63
64#[inline(always)]
69pub unsafe fn peek_word_unchecked(data: &[u8], offset: usize) -> &[u8; 32] {
70 let slice = data.get_unchecked(offset..offset + 32);
72 &*(slice.as_ptr() as *const [u8; 32])
76}
77
78#[inline(always)]
80pub fn read_address_from_word(data: &[u8], offset: usize) -> Result<ZAddress<'_>, ZError> {
81 let word = peek_word(data, offset)?;
82 let addr_slice = &word[12..32];
84 let addr_ref: &[u8; 20] = addr_slice
85 .try_into()
86 .map_err(|_| ZError::Custom("Address slice conversion failed"))?;
87 Ok(ZAddress(addr_ref))
88}
89
90#[inline(always)]
95pub unsafe fn read_address_unchecked(data: &[u8], offset: usize) -> ZAddress<'_> {
96 let word = peek_word_unchecked(data, offset);
98 let addr_slice = word.get_unchecked(12..32);
100 let addr_ref = &*(addr_slice.as_ptr() as *const [u8; 20]);
102 ZAddress(addr_ref)
103}
104
105#[inline(always)]
106pub fn read_u256(data: &[u8], offset: usize) -> Result<ZU256<'_>, ZError> {
107 let word = peek_word(data, offset)?;
108 Ok(ZU256(word))
109}
110
111#[inline(always)]
116pub unsafe fn read_u256_unchecked(data: &[u8], offset: usize) -> ZU256<'_> {
117 let word = peek_word_unchecked(data, offset);
119 ZU256(word)
120}
121
122#[inline(always)]
123pub fn read_int256(data: &[u8], offset: usize) -> Result<crate::types::ZInt256<'_>, ZError> {
124 let word = peek_word(data, offset)?;
125 Ok(crate::types::ZInt256(word))
126}
127
128#[inline(always)]
129pub fn read_u8(data: &[u8], offset: usize) -> Result<u8, ZError> {
130 let word = peek_word(data, offset)?;
131 if word.iter().take(31).any(|&b| b != 0) {
133 return Err(ZError::Custom("u8 value invalid (high bits set)"));
134 }
135 Ok(word[31])
136}
137
138#[inline(always)]
139pub fn read_i8(data: &[u8], offset: usize) -> Result<i8, ZError> {
140 let word = peek_word(data, offset)?;
141 let val = word[31] as i8;
142 let padding_byte = if val < 0 { 0xff } else { 0x00 };
143 if word.iter().take(31).any(|&b| b != padding_byte) {
144 return Err(ZError::Custom("i8 value invalid (bad padding)"));
145 }
146 Ok(val)
147}
148
149#[inline(always)]
150pub fn read_u16(data: &[u8], offset: usize) -> Result<u16, ZError> {
151 let word = peek_word(data, offset)?;
152 if !word[0..30].iter().all(|&b| b == 0) {
153 return Err(ZError::Custom("u16 value invalid (high bits set)"));
154 }
155 Ok(u16::from_be_bytes([word[30], word[31]]))
156}
157
158#[inline(always)]
159pub fn read_i16(data: &[u8], offset: usize) -> Result<i16, ZError> {
160 let word = peek_word(data, offset)?;
161 let val = i16::from_be_bytes([word[30], word[31]]);
162 let padding_byte = if val < 0 { 0xff } else { 0x00 };
163 if !word[0..30].iter().all(|&b| b == padding_byte) {
164 return Err(ZError::Custom("i16 value invalid (bad padding)"));
165 }
166 Ok(val)
167}
168
169#[inline(always)]
170pub fn read_u32(data: &[u8], offset: usize) -> Result<u32, ZError> {
171 let word = peek_word(data, offset)?;
172 if !word[0..28].iter().all(|&b| b == 0) {
173 return Err(ZError::Custom("u32 value invalid (high bits set)"));
174 }
175 Ok(u32::from_be_bytes(word[28..32].try_into().unwrap()))
177}
178
179#[inline(always)]
180pub fn read_i32(data: &[u8], offset: usize) -> Result<i32, ZError> {
181 let word = peek_word(data, offset)?;
182 let val = i32::from_be_bytes(word[28..32].try_into().unwrap());
183 let padding_byte = if val < 0 { 0xff } else { 0x00 };
184 if !word[0..28].iter().all(|&b| b == padding_byte) {
185 return Err(ZError::Custom("i32 value invalid (bad padding)"));
186 }
187 Ok(val)
188}
189
190#[inline(always)]
191pub fn read_u64(data: &[u8], offset: usize) -> Result<u64, ZError> {
192 let word = peek_word(data, offset)?;
193 if !word[0..24].iter().all(|&b| b == 0) {
194 return Err(ZError::Custom("u64 value invalid (high bits set)"));
195 }
196 Ok(u64::from_be_bytes(word[24..32].try_into().unwrap()))
197}
198
199#[inline(always)]
200pub fn read_i64(data: &[u8], offset: usize) -> Result<i64, ZError> {
201 let word = peek_word(data, offset)?;
202 let val = i64::from_be_bytes(word[24..32].try_into().unwrap());
203 let padding_byte = if val < 0 { 0xff } else { 0x00 };
204 if !word[0..24].iter().all(|&b| b == padding_byte) {
205 return Err(ZError::Custom("i64 value invalid (bad padding)"));
206 }
207 Ok(val)
208}
209
210#[inline(always)]
211pub fn read_u128(data: &[u8], offset: usize) -> Result<u128, ZError> {
212 let word = peek_word(data, offset)?;
213 if !word[0..16].iter().all(|&b| b == 0) {
214 return Err(ZError::Custom("u128 value invalid (high bits set)"));
215 }
216 Ok(u128::from_be_bytes(word[16..32].try_into().unwrap()))
217}
218
219#[inline(always)]
220pub fn read_i128(data: &[u8], offset: usize) -> Result<i128, ZError> {
221 let word = peek_word(data, offset)?;
222 let val = i128::from_be_bytes(word[16..32].try_into().unwrap());
223 let padding_byte = if val < 0 { 0xff } else { 0x00 };
224 if !word[0..16].iter().all(|&b| b == padding_byte) {
225 return Err(ZError::Custom("i128 value invalid (bad padding)"));
226 }
227 Ok(val)
228}
229
230#[inline(always)]
231pub fn read_bool(data: &[u8], offset: usize) -> Result<ZBool, ZError> {
232 let word = peek_word(data, offset)?;
233 let is_zero = word[0..31].iter().all(|&b| b == 0);
238 if !is_zero {
239 return Err(ZError::Custom("Boolean value has dirty high bits"));
240 }
241
242 match word[31] {
243 0 => Ok(ZBool(false)),
244 1 => Ok(ZBool(true)),
245 _ => Err(ZError::Custom("Boolean value invalid (not 0 or 1)")),
246 }
247}
248
249pub fn read_bytes(data: &[u8], initial_offset: usize) -> Result<ZBytes<'_>, ZError> {
253 let offset_word = peek_word(data, initial_offset)?;
255 let data_offset_usize = usize::from_be_bytes(offset_word[24..32].try_into().unwrap()); if data_offset_usize >= data.len() {
262 return Err(ZError::OutOfBounds(data_offset_usize, data.len()));
263 }
264
265 let len_word = peek_word(data, data_offset_usize)?;
267 let length = usize::from_be_bytes(len_word[24..32].try_into().unwrap());
268
269 let start = data_offset_usize + 32;
271 let end = start + length;
272
273 if end > data.len() {
274 return Err(ZError::OutOfBounds(end, data.len()));
275 }
276
277 Ok(ZBytes(&data[start..end]))
278}
279
280pub fn read_string(data: &[u8], initial_offset: usize) -> Result<ZString<'_>, ZError> {
281 let zbytes = read_bytes(data, initial_offset)?;
282 let s = str::from_utf8(zbytes.0).map_err(|_| ZError::Custom("Invalid UTF-8 string"))?;
283 Ok(ZString(s))
284}
285
286pub fn read_array_fixed<'a, T>(
287 data: &'a [u8],
288 offset: usize,
289 length: usize,
290) -> Result<ZArray<'a, T>, ZError> {
291 let end = offset + length * 32;
293 if end > data.len() {
294 return Err(ZError::OutOfBounds(end, data.len()));
295 }
296 Ok(ZArray::new(data, offset, length))
297}
298
299pub fn read_array_dyn<'a, T>(
300 data: &'a [u8],
301 initial_offset: usize,
302) -> Result<ZArray<'a, T>, ZError> {
303 let offset_word = peek_word(data, initial_offset)?;
306 let data_offset_usize = usize::from_be_bytes(offset_word[24..32].try_into().unwrap());
307
308 if data_offset_usize >= data.len() {
309 return Err(ZError::OutOfBounds(data_offset_usize, data.len()));
310 }
311
312 let len_word = peek_word(data, data_offset_usize)?;
314 let length = usize::from_be_bytes(len_word[24..32].try_into().unwrap());
315
316 let start_offset = data_offset_usize + 32;
318
319 if start_offset + length * 32 > data.len() {
322 return Err(ZError::OutOfBounds(start_offset + length * 32, data.len()));
323 }
324
325 Ok(ZArray::new(data, start_offset, length))
326}
327
328pub fn decode_revert(data: &[u8]) -> Result<ZRevert<'_>, ZError> {
330 if data.is_empty() {
331 return Ok(ZRevert::Unknown);
332 }
333
334 if data.len() < 4 {
335 return Ok(ZRevert::Unknown);
336 }
337
338 let selector = read_selector(data)?;
339 let params = skip_selector(data)?;
340
341 match selector {
342 [0x08, 0xc3, 0x79, 0xa0] => {
344 let s = read_string(params, 0)?;
345 Ok(ZRevert::Error(s))
346 }
347 [0x4e, 0x48, 0x7b, 0x71] => {
349 let p = read_u256(params, 0)?;
350 Ok(ZRevert::Panic(p))
351 }
352 _ => {
353 Ok(ZRevert::Custom(selector, params))
355 }
356 }
357}
358
359pub fn decode_call_result<'a, T>(data: &'a [u8]) -> Result<ZCallResult<'a, T>, ZError>
363where
364 T: crate::ZDecode<'a>,
365{
366 if data.len() >= 4 {
373 let sel = &data[0..4];
374 if sel == [0x08, 0xc3, 0x79, 0xa0] || sel == [0x4e, 0x48, 0x7b, 0x71] {
375 return Ok(ZCallResult::Revert(decode_revert(data)?));
376 }
377 }
378
379 Ok(ZCallResult::Success(T::decode(data, 0)?))
380}