ethcontract/errors/
geth.rs

1//! This module implements Geth specific error decoding in order to try and
2//! provide more accurate errors from Geth nodes.
3
4use crate::errors::ExecutionError;
5use jsonrpc_core::Error as JsonrpcError;
6
7const REVERTED: &str = "execution reverted";
8const INVALID_OPCODE: &str = "invalid opcode";
9
10/// Tries to get a more accurate error from a generic Geth JSON RPC error.
11/// Returns `None` when a more accurate error cannot be determined.
12pub fn get_encoded_error(err: &JsonrpcError) -> Option<ExecutionError> {
13    if let Some(str) = err.message.strip_prefix(REVERTED) {
14        let reason = str.strip_prefix(": ").map(ToString::to_string);
15        Some(ExecutionError::Revert(reason))
16    } else if err.message.strip_prefix(INVALID_OPCODE).is_some() {
17        Some(ExecutionError::InvalidOpcode)
18    } else {
19        None
20    }
21}
22
23#[cfg(test)]
24mod tests {
25    use super::*;
26
27    #[test]
28    fn revert_without_reason() {
29        let error = JsonrpcError {
30            code: 3.into(),
31            message: REVERTED.to_string(),
32            data: None,
33        };
34        let result = get_encoded_error(&error);
35        assert!(matches!(result, Some(ExecutionError::Revert(None))));
36    }
37
38    #[test]
39    fn revert_with_reason() {
40        let reason = "SafeMath: subtraction overflow";
41        let error = JsonrpcError {
42            code: 3.into(),
43            message: format!("{}: {}", REVERTED, reason),
44            data: None,
45        };
46        let result = get_encoded_error(&error);
47        assert!(matches!(result, Some(ExecutionError::Revert(Some(reason_))) if reason_ == reason));
48    }
49}