Skip to content

Commit

Permalink
fix: improve json rpc error message
Browse files Browse the repository at this point in the history
  • Loading branch information
zsluedem committed Oct 8, 2023
1 parent ec79cfa commit f745ee7
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 36 deletions.
11 changes: 6 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/contracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ AA (ERC-4337) bundler smart contract interfaces
ethers = { workspace = true }
eyre = { workspace = true }
lazy_static = "1.4.0"
regex = "1.9.6"
serde = "1"
serde_json = "1"
silius-primitives = { path = "../primitives" }
Expand Down
103 changes: 76 additions & 27 deletions crates/contracts/src/entry_point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ use ethers::types::{
Address, Bytes, GethDebugTracerType, GethDebugTracingCallOptions, GethDebugTracingOptions,
GethTrace, TransactionRequest, U256,
};
use regex::Regex;
use std::fmt::Display;
use std::str::FromStr;
use std::sync::Arc;
use thiserror::Error;

Expand Down Expand Up @@ -74,30 +76,7 @@ impl<M: Middleware + 'static> EntryPoint<M> {
))),
ContractError::MiddlewareError { e } => Err(EntryPointErr::from_middleware_err::<M>(e)),
ContractError::ProviderError { e } => Err(e.into()),
ContractError::Revert(data) => {
let decoded = EntryPointAPIErrors::decode(data.as_ref());
match decoded {
Ok(res) => Ok(res),
Err(e) => {
// ethers-rs could not handle `require (true, "reason")` well in this case
// revert with `require` error would ends up with error event signature `0x08c379a0`
// we need to handle it manually
let (error_sig, reason) = data.split_at(4);
if error_sig == [0x08, 0xc3, 0x79, 0xa0] {
return <String as AbiDecode>::decode(reason)
.map(EntryPointAPIErrors::RevertString)
.map_err(|e| {
EntryPointErr::DecodeErr(format!(
"{e:?} data field could not be deserialize to revert error",
))
});
}
Err(EntryPointErr::DecodeErr(format!(
"{e:?} data field could not be deserialize to EntryPointAPIErrors",
)))
}
}
}
ContractError::Revert(data) => decode_revert_error(data),
_ => Err(EntryPointErr::UnknownErr(format!(
"Unkown error: {err_msg:?}",
))),
Expand Down Expand Up @@ -287,10 +266,35 @@ impl<M: Middleware + 'static> EntryPoint<M> {
}
}

fn decode_revert_error(data: Bytes) -> Result<EntryPointAPIErrors, EntryPointErr> {
let decoded = EntryPointAPIErrors::decode(data.as_ref());
match decoded {
Ok(res) => Ok(res),
Err(e) => {
// ethers-rs could not handle `require (true, "reason")` well in this case
// revert with `require` error would ends up with error event signature `0x08c379a0`
// we need to handle it manually
let (error_sig, reason) = data.split_at(4);
if error_sig == [0x08, 0xc3, 0x79, 0xa0] {
return <String as AbiDecode>::decode(reason)
.map(EntryPointAPIErrors::RevertString)
.map_err(|e| {
EntryPointErr::DecodeErr(format!(
"{e:?} data field could not be deserialize to revert error",
))
});
}
Err(EntryPointErr::DecodeErr(format!(
"{e:?} data field could not be deserialize to EntryPointAPIErrors",
)))
}
}
}

#[derive(Debug, Error)]
pub enum EntryPointErr {
FailedOp(FailedOp),
JsonRpcError(JsonRpcError),
RevertError(String),
NetworkErr(String),
DecodeErr(String),
UnknownErr(String), // describe impossible error. We should fix the codes here(or contract codes) if this occurs.
Expand All @@ -313,7 +317,7 @@ impl EntryPointErr {
match err {
ProviderError::JsonRpcClientError(err) => err
.as_error_response()
.map(|e| EntryPointErr::JsonRpcError(e.clone()))
.map(Self::from_json_rpc_error)
.unwrap_or(EntryPointErr::UnknownErr(format!(
"Unknown JSON-RPC client error: {err:?}"
))),
Expand All @@ -324,9 +328,54 @@ impl EntryPointErr {
}
}

fn from_json_rpc_error(err: &JsonRpcError) -> Self {
if let Some(ref value) = err.data {
match value {
serde_json::Value::String(data) => {
let re = Regex::new(r"0x[0-9a-fA-F]+").expect("Regex rules valid");
let hex = if let Some(hex) = re.find(data) {
hex
} else {
return EntryPointErr::DecodeErr(format!(
"Could not find hex string in {data:?}",
));
};
let bytes = match Bytes::from_str(hex.into()) {
Ok(bytes) => bytes,
Err(e) => {
return EntryPointErr::DecodeErr(format!(
"Error string {data:?} could not be converted to bytes because {e:?}",
))
}
};

let formal_err = decode_revert_error(bytes);
match formal_err {
Ok(EntryPointAPIErrors::FailedOp(failop)) => {
return EntryPointErr::FailedOp(failop)
}
Ok(EntryPointAPIErrors::RevertString(error_msg)) => {
return EntryPointErr::RevertError(error_msg)
}
err => {
return EntryPointErr::UnknownErr(format!(
"Unknown middleware error: {err:?}"
))
}
};
}
other => {
return Self::DecodeErr(format!("Json rpc return data {other:?} is not string"))
}
}
}

Self::UnknownErr("Json rpc error {err:?} doesn't contain data field.".to_string())
}

fn from_middleware_err<M: Middleware>(err: M::Error) -> Self {
if let Some(err) = err.as_error_response() {
return EntryPointErr::JsonRpcError(err.clone());
return Self::from_json_rpc_error(err);
}

if let Some(err) = err.as_provider_error() {
Expand Down
14 changes: 10 additions & 4 deletions crates/uopool/src/uopool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,8 +437,11 @@ where
Ok(_) => {}
Err(err) => {
return Err(match err {
EntryPointErr::JsonRpcError(err) => SimulationCheckError::Execution {
message: err.message,
EntryPointErr::FailedOp(err) => SimulationCheckError::Execution {
message: err.to_string(),
},
EntryPointErr::RevertError(err) => SimulationCheckError::Execution {
message: err.to_string(),
},
_ => SimulationCheckError::UnknownError {
message: format!("{err:?}"),
Expand All @@ -451,8 +454,11 @@ where
Ok(res) => res,
Err(err) => {
return Err(match err {
EntryPointErr::JsonRpcError(err) => SimulationCheckError::Execution {
message: err.message,
EntryPointErr::FailedOp(err) => SimulationCheckError::Execution {
message: err.to_string(),
},
EntryPointErr::RevertError(err) => SimulationCheckError::Execution {
message: err.to_string(),
},
_ => SimulationCheckError::UnknownError {
message: format!("{err:?}"),
Expand Down

0 comments on commit f745ee7

Please sign in to comment.