anvil_zksync_core/node/
error.rs

1use alloy::hex::ToHexExt;
2use anvil_zksync_traces::identifier::SignaturesIdentifier;
3use async_trait::async_trait;
4use zksync_error::anvil_zksync::halt::HaltError;
5use zksync_error::anvil_zksync::revert::RevertError;
6use zksync_multivm::interface::{Halt, VmRevertReason};
7
8async fn handle_vm_revert_reason(reason: &VmRevertReason) -> (String, &[u8]) {
9    match reason {
10        VmRevertReason::General { msg, data } => (msg.to_string(), data),
11        VmRevertReason::InnerTxError => ("Inner transaction error".to_string(), &[]),
12        VmRevertReason::VmError => ("VM Error".to_string(), &[]),
13        VmRevertReason::Unknown {
14            function_selector,
15            data,
16        } => {
17            if function_selector.is_empty() {
18                ("Error: no function selector available".to_string(), &[])
19            } else {
20                let hex_selector = function_selector.encode_hex();
21                match SignaturesIdentifier::global()
22                    .identify_function(function_selector)
23                    .await
24                {
25                    Some(decoded_name) => (decoded_name.name, data),
26                    None => (
27                        format!("Error with function selector: 0x{hex_selector}"),
28                        data,
29                    ),
30                }
31            }
32        }
33        _ => ("".to_string(), &[]),
34    }
35}
36
37/// Extracts the VmRevertReason from a Halt, if available.
38fn revert_reason(halt: &Halt) -> Option<&VmRevertReason> {
39    match halt {
40        Halt::ValidationFailed(reason)
41        | Halt::PaymasterValidationFailed(reason)
42        | Halt::PrePaymasterPreparationFailed(reason)
43        | Halt::PayForTxFailed(reason)
44        | Halt::FailedToMarkFactoryDependencies(reason)
45        | Halt::FailedToChargeFee(reason)
46        | Halt::Unknown(reason) => Some(reason),
47        _ => None,
48    }
49}
50
51#[async_trait]
52pub trait ToRevertReason {
53    async fn to_revert_reason(self) -> RevertError;
54}
55
56#[async_trait]
57impl ToRevertReason for VmRevertReason {
58    async fn to_revert_reason(self) -> RevertError {
59        let (message, data) = handle_vm_revert_reason(&self).await;
60
61        match self {
62            VmRevertReason::General { .. } => RevertError::General {
63                msg: message,
64                data: data.encode_hex().into(),
65            },
66            VmRevertReason::InnerTxError => RevertError::InnerTxError,
67            VmRevertReason::VmError => RevertError::VmError,
68            VmRevertReason::Unknown { .. } => RevertError::Unknown {
69                function_selector: message,
70                data: data.encode_hex(),
71            },
72            _ => RevertError::Unknown {
73                function_selector: message,
74                data: data.encode_hex(),
75            },
76        }
77    }
78}
79
80#[async_trait]
81pub trait ToHaltError {
82    async fn to_halt_error(self) -> HaltError;
83}
84
85#[async_trait]
86impl ToHaltError for Halt {
87    async fn to_halt_error(self) -> HaltError {
88        let (msg_opt, data_opt) = if let Some(reason) = revert_reason(&self) {
89            let (msg, data) = handle_vm_revert_reason(reason).await;
90            (Some(msg), Some(data.encode_hex()))
91        } else {
92            (None, None)
93        };
94
95        let msg = msg_opt.unwrap_or_else(|| "Unknown revert reason".into());
96        let data = data_opt.unwrap_or_default();
97
98        match self {
99            Halt::ValidationFailed(_) => HaltError::ValidationFailed { msg, data },
100            Halt::PaymasterValidationFailed(_) => {
101                HaltError::PaymasterValidationFailed { msg, data }
102            }
103            Halt::PrePaymasterPreparationFailed(_) => {
104                HaltError::PrePaymasterPreparationFailed { msg, data }
105            }
106            Halt::PayForTxFailed(_) => HaltError::PayForTxFailed { msg, data },
107            Halt::FailedToMarkFactoryDependencies(_) => {
108                HaltError::FailedToMarkFactoryDependencies { msg, data }
109            }
110            Halt::FailedToChargeFee(_) => HaltError::FailedToChargeFee { msg, data },
111            Halt::Unknown(_) => HaltError::Unknown { msg, data },
112            // Other variants that do not include a VmRevertReason:
113            Halt::UnexpectedVMBehavior(msg) => HaltError::UnexpectedVMBehavior { problem: msg },
114            Halt::FailedToSetL2Block(msg) => HaltError::FailedToSetL2Block { msg },
115            Halt::FailedToAppendTransactionToL2Block(msg) => {
116                HaltError::FailedToAppendTransactionToL2Block { msg }
117            }
118            Halt::TracerCustom(msg) => HaltError::TracerCustom { msg },
119            Halt::FromIsNotAnAccount => HaltError::FromIsNotAnAccount,
120            Halt::InnerTxError => HaltError::InnerTxError,
121            Halt::BootloaderOutOfGas => HaltError::BootloaderOutOfGas,
122            Halt::ValidationOutOfGas => HaltError::ValidationOutOfGas,
123            Halt::TooBigGasLimit => HaltError::TooBigGasLimit,
124            Halt::NotEnoughGasProvided => HaltError::NotEnoughGasProvided,
125            Halt::MissingInvocationLimitReached => HaltError::MissingInvocationLimitReached,
126            Halt::VMPanic => HaltError::VMPanic,
127            Halt::FailedToPublishCompressedBytecodes => {
128                HaltError::FailedToPublishCompressedBytecodes
129            }
130            Halt::FailedBlockTimestampAssertion => HaltError::FailedBlockTimestampAssertion,
131        }
132    }
133}