diff --git a/crates/sol-types/src/coder/decoder.rs b/crates/sol-types/src/coder/decoder.rs index 7c72072746..fca8fb8723 100644 --- a/crates/sol-types/src/coder/decoder.rs +++ b/crates/sol-types/src/coder/decoder.rs @@ -616,4 +616,35 @@ mod tests { MyTy::decode_params(&input, true).unwrap_err(); assert!(MyTy2::decode_params(&input, true).is_ok()); } + + #[test] + fn signed_int_dirty_high_bytes() { + type MyTy = sol_data::Int<8>; + + let dirty_negative = + hex!("f0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + + assert_eq!(MyTy::decode_single(&dirty_negative, false).unwrap(), -1); + + assert!( + matches!( + MyTy::decode_single(&dirty_negative, true), + Err(crate::Error::TypeCheckFail { .. }), + ), + "did not match error" + ); + + let dirty_positive = + hex!("700000000000000000000000000000000000000000000000000000000000007f"); + + assert_eq!(MyTy::decode_single(&dirty_positive, false).unwrap(), 127); + + assert!( + matches!( + MyTy::decode_single(&dirty_positive, true), + Err(crate::Error::TypeCheckFail { .. }), + ), + "did not match error" + ); + } } diff --git a/crates/sol-types/src/types/data_type.rs b/crates/sol-types/src/types/data_type.rs index 8caf7a1aa9..4ae69727dd 100644 --- a/crates/sol-types/src/types/data_type.rs +++ b/crates/sol-types/src/types/data_type.rs @@ -120,8 +120,25 @@ macro_rules! impl_int_sol_type { } #[inline] - fn type_check(_token: &Self::TokenType) -> Result<()> { - Ok(()) + fn type_check(token: &Self::TokenType) -> Result<()> { + // check for 256 can be omitted as this macro expansion is not used for 256 bits + + let bytes = token.as_slice(); + let meaningful_idx = 32 - ($bits / 8); + + let sign_extension = if bytes[meaningful_idx] & 0x80 == 0x80 { + 0xff + } else { + 0 + }; + + // check that all upper bytes are an extension of the sign bit + bytes + .iter() + .take(meaningful_idx) + .all(|byte| *byte == sign_extension) + .then(|| ()) + .ok_or_else(|| Self::type_check_fail(bytes)) } #[inline] @@ -173,9 +190,27 @@ macro_rules! impl_int_sol_type { } #[inline] - fn type_check(_token: &Self::TokenType) -> Result<()> { - // TODO - Ok(()) + fn type_check(token: &Self::TokenType) -> Result<()> { + if $bits == 256 { + return Ok(()) + } + + let bytes = token.as_slice(); + let meaningful_idx = 32 - ($bits / 8); + + let sign_extension = if bytes[meaningful_idx] & 0x80 == 0x80 { + 0xff + } else { + 0 + }; + + // check that all upper bytes are an extension of the sign bit + bytes + .iter() + .take(meaningful_idx) + .all(|byte| *byte == sign_extension) + .then(|| ()) + .ok_or_else(|| Self::type_check_fail(bytes)) } #[inline]