diff --git a/crates/sol-types/src/lib.rs b/crates/sol-types/src/lib.rs index 507d95fca..cba098b39 100644 --- a/crates/sol-types/src/lib.rs +++ b/crates/sol-types/src/lib.rs @@ -181,9 +181,9 @@ mod impl_core; mod types; pub use types::{ - data_type as sol_data, ContractError, Encodable, EventTopic, GenericContractError, Panic, - PanicKind, Revert, Selectors, SolCall, SolEnum, SolError, SolEvent, SolInterface, SolStruct, - SolType, TopicList, + data_type as sol_data, decode_revert_reason, ContractError, Encodable, EventTopic, + GenericContractError, Panic, PanicKind, Revert, Selectors, SolCall, SolEnum, SolError, + SolEvent, SolInterface, SolStruct, SolType, TopicList, }; pub mod utils; diff --git a/crates/sol-types/src/types/error.rs b/crates/sol-types/src/types/error.rs index fd8080539..29e7bd5b9 100644 --- a/crates/sol-types/src/types/error.rs +++ b/crates/sol-types/src/types/error.rs @@ -1,8 +1,11 @@ use crate::{ token::{PackedSeqToken, TokenSeq, WordToken}, - Result, SolType, TokenType, Word, + GenericContractError, Result, SolInterface, SolType, TokenType, Word, +}; +use alloc::{ + string::{String, ToString}, + vec::Vec, }; -use alloc::{string::String, vec::Vec}; use alloy_primitives::U256; use core::{borrow::Borrow, fmt}; @@ -408,6 +411,24 @@ impl PanicKind { } } +/// Returns the revert reason from the given output data. Returns `None` if the +/// content is not a valid abi encoded String or a regular utf8 string (for +/// Vyper reverts). +pub fn decode_revert_reason(out: &[u8]) -> Option { + // Try to decode as a generic contract error. + if let Ok(error) = GenericContractError::decode(out, true) { + return Some(error.to_string()) + } + + // If that fails, try to decode as a regular string. + if let Ok(decoded_string) = core::str::from_utf8(out) { + return Some(decoded_string.to_string()) + } + + // If both attempts fail, return None. + None +} + #[cfg(test)] mod tests { use super::*; @@ -448,4 +469,26 @@ mod tests { "Panic selector is incorrect" ); } + + #[test] + fn test_decode_solidity_revert_reason() { + let revert = Revert::from("test_revert_reason"); + let encoded = revert.encode(); + let decoded = decode_revert_reason(&encoded).unwrap(); + assert_eq!(decoded, String::from("revert: test_revert_reason")); + } + + #[test] + fn test_decode_random_revert_reason() { + let revert_reason = String::from("test_revert_reason"); + let decoded = decode_revert_reason(revert_reason.as_bytes()).unwrap(); + assert_eq!(decoded, String::from("test_revert_reason")); + } + + #[test] + fn test_decode_non_utf8_revert_reason() { + let revert_reason = [0xFF]; + let decoded = decode_revert_reason(&revert_reason); + assert_eq!(decoded, None); + } } diff --git a/crates/sol-types/src/types/mod.rs b/crates/sol-types/src/types/mod.rs index 67cd65a39..da6f7ebe8 100644 --- a/crates/sol-types/src/types/mod.rs +++ b/crates/sol-types/src/types/mod.rs @@ -4,7 +4,7 @@ mod r#enum; pub use r#enum::SolEnum; mod error; -pub use error::{Panic, PanicKind, Revert, SolError}; +pub use error::{decode_revert_reason, Panic, PanicKind, Revert, SolError}; mod event; pub use event::{EventTopic, SolEvent, TopicList};