use crate::errors::{revert, ExecutionError};
use jsonrpc_core::Error as JsonrpcError;
const REVERTED: &str = "Reverted 0x";
const INVALID: &str = "Bad instruction";
pub fn get_encoded_error(err: &JsonrpcError) -> Option<ExecutionError> {
let message = get_error_message(err)?;
if let Some(hex) = message.strip_prefix(REVERTED) {
if hex.is_empty() {
return Some(ExecutionError::Revert(None));
} else {
let bytes = hex::decode(hex).ok()?;
let reason = revert::decode_reason(&bytes)?;
return Some(ExecutionError::Revert(Some(reason)));
}
} else if message.starts_with(INVALID) {
return Some(ExecutionError::InvalidOpcode);
}
None
}
fn get_error_message(err: &JsonrpcError) -> Option<&'_ str> {
err.data.as_ref().and_then(|data| data.as_str())
}
#[cfg(test)]
pub use tests::*;
#[cfg(test)]
mod tests {
use super::*;
use crate::test::prelude::*;
use jsonrpc_core::ErrorCode;
pub fn rpc_error(data: &str) -> JsonrpcError {
JsonrpcError {
code: ErrorCode::from(-32015),
message: "vm execution error".to_owned(),
data: Some(json!(data)),
}
}
#[test]
fn execution_error_from_revert_with_message() {
let jsonrpc_err = rpc_error(&format!(
"Reverted {}",
revert::encode_reason_hex("message")
));
let err = get_encoded_error(&jsonrpc_err);
assert!(
matches!(
&err,
Some(ExecutionError::Revert(Some(reason))) if reason == "message"
),
"bad error conversion {:?}",
err
);
}
#[test]
fn execution_error_from_revert() {
let jsonrpc_err = rpc_error("Reverted 0x");
let err = get_encoded_error(&jsonrpc_err);
assert!(
matches!(err, Some(ExecutionError::Revert(None))),
"bad error conversion {:?}",
err
);
}
#[test]
fn execution_error_from_invalid_opcode() {
let jsonrpc_err = rpc_error("Bad instruction fd");
let err = get_encoded_error(&jsonrpc_err);
assert!(
matches!(err, Some(ExecutionError::InvalidOpcode)),
"bad error conversion {:?}",
err
);
}
}