stratus/eth/primitives/
execution_result.rs

1use std::borrow::Cow;
2
3use display_json::DebugAsJson;
4#[cfg(test)]
5use fake::Faker;
6
7use crate::eth::codegen::error_sig_opt;
8use crate::eth::primitives::Bytes;
9
10/// Indicates how a transaction execution was finished.
11#[derive(DebugAsJson, strum::Display, Clone, PartialEq, Eq, derive_new::new, serde::Serialize, serde::Deserialize, strum::EnumString, Default)]
12#[cfg_attr(test, derive(fake::Dummy))]
13#[serde(rename_all = "snake_case")]
14pub enum ExecutionResult {
15    /// Finished normally (RETURN opcode).
16    #[strum(to_string = "success")]
17    #[default]
18    Success,
19
20    /// Transaction execution finished with a reversion (REVERT opcode).
21    #[strum(to_string = "reverted")]
22    Reverted { reason: RevertReason },
23
24    /// Transaction execution did not finish.
25    #[strum(to_string = "halted")]
26    Halted { reason: String },
27}
28
29impl ExecutionResult {
30    pub fn is_success(&self) -> bool {
31        matches!(self, ExecutionResult::Success)
32    }
33}
34
35#[derive(Debug, thiserror::Error, serde::Serialize, serde::Deserialize, Default, Clone, PartialEq, Eq)]
36pub struct RevertReason(pub Cow<'static, str>);
37
38#[cfg(test)]
39impl fake::Dummy<Faker> for RevertReason {
40    fn dummy_with_rng<R: rand::Rng + ?Sized>(_: &Faker, _rng: &mut R) -> Self {
41        RevertReason(Cow::Borrowed("reverted"))
42    }
43}
44
45impl From<&'static str> for RevertReason {
46    fn from(value: &'static str) -> Self {
47        RevertReason(Cow::Borrowed(value))
48    }
49}
50
51impl From<&Bytes> for RevertReason {
52    fn from(value: &Bytes) -> Self {
53        let sig: Cow<str> = error_sig_opt(value).unwrap_or_else(|| Cow::Owned(value.to_string()));
54        Self(sig)
55    }
56}
57
58impl std::fmt::Display for RevertReason {
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        write!(f, "{}", self.0)
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use alloy_primitives::hex;
67
68    use super::*;
69
70    #[test]
71    fn test_revert_reason_from_bytes() {
72        // Test string revert
73        let input = hex::decode(
74            "08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000\
750000000000014437573746f6d206572726f72206d657373616765000000000000000000000000",
76        )
77        .unwrap();
78        let bytes = Bytes::from(input);
79        let revert = RevertReason::from(&bytes);
80        assert_eq!(revert.0, "Custom error message");
81
82        // Test known error
83        let input = hex::decode("22aa4404").unwrap();
84        let bytes = Bytes::from(input);
85        let revert = RevertReason::from(&bytes);
86        assert_eq!(revert.0, "KnownError()");
87
88        // Test unknown error
89        let input = hex::decode("c39a0557").unwrap();
90        let bytes = Bytes::from(input);
91        let revert = RevertReason::from(&bytes);
92        assert_eq!(revert.0, "0xc39a0557");
93    }
94}