Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions crates/rpc/rpc-eth-api/src/helpers/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
}

// transact all bundles
for bundle in bundles {
for (bundle_index, bundle) in bundles.into_iter().enumerate() {
let Bundle { transactions, block_override } = bundle;
if transactions.is_empty() {
// Skip empty bundles
Expand All @@ -306,15 +306,30 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
let block_overrides = block_override.map(Box::new);

// transact all transactions in the bundle
for tx in transactions {
for (tx_index, tx) in transactions.into_iter().enumerate() {
// Apply overrides, state overrides are only applied for the first tx in the
// request
let overrides =
EvmOverrides::new(state_override.take(), block_overrides.clone());

let (current_evm_env, prepared_tx) =
this.prepare_call_env(evm_env.clone(), tx, &mut db, overrides)?;
let res = this.transact(&mut db, current_evm_env, prepared_tx)?;
let (current_evm_env, prepared_tx) = this
.prepare_call_env(evm_env.clone(), tx, &mut db, overrides)
.map_err(|err| {
Self::Error::from_eth_err(EthApiError::call_many_error(
bundle_index,
tx_index,
err,
))
})?;
let res = this.transact(&mut db, current_evm_env, prepared_tx).map_err(
|err| {
Self::Error::from_eth_err(EthApiError::call_many_error(
bundle_index,
tx_index,
err,
))
},
)?;

match ensure_success::<_, Self::Error>(res.result) {
Ok(output) => {
Expand Down
48 changes: 48 additions & 0 deletions crates/rpc/rpc-eth-types/src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,17 @@ pub enum EthApiError {
/// Error thrown when batch tx send channel fails
#[error("Batch transaction sender channel closed")]
BatchTxSendError,
/// Error that occurred during `call_many` execution with bundle and transaction context
#[error("call_many error at bundle {bundle_index}, tx {tx_index}: {source}")]
CallManyError {
/// Bundle index where the error occurred
bundle_index: usize,
/// Transaction index within the bundle where the error occurred
tx_index: usize,
/// The underlying error
#[source]
source: Box<dyn core::error::Error + Send + Sync>,
},
/// Any other error
#[error("{0}")]
Other(Box<dyn ToRpcError>),
Expand All @@ -184,6 +195,14 @@ impl EthApiError {
Self::Other(Box::new(err))
}

/// Creates a new [`EthApiError::CallManyError`] variant.
pub fn call_many_error<E>(bundle_index: usize, tx_index: usize, source: E) -> Self
where
E: core::error::Error + Send + Sync + 'static,
{
Self::CallManyError { bundle_index, tx_index, source: Box::new(source) }
}

/// Returns `true` if error is [`RpcInvalidTransactionError::GasTooHigh`]
pub const fn is_gas_too_high(&self) -> bool {
matches!(
Expand Down Expand Up @@ -296,6 +315,9 @@ impl From<EthApiError> for jsonrpsee_types::error::ErrorObject<'static> {
EthApiError::BatchTxSendError => {
internal_rpc_err("Batch transaction sender channel closed".to_string())
}
EthApiError::CallManyError { bundle_index, tx_index, source } => internal_rpc_err(
format!("call_many error at bundle {}, tx {}: {}", bundle_index, tx_index, source),
),
}
}
}
Expand Down Expand Up @@ -1038,6 +1060,32 @@ mod tests {
assert_eq!(err.to_string(), "execution aborted (timeout = 10s)");
}

#[test]
fn call_many_error_display() {
// Test the display format of CallManyError
let source_error = std::io::Error::other("test error");
let err = EthApiError::call_many_error(2, 5, source_error);

// Check the error message format
let error_msg = err.to_string();
assert!(error_msg.contains("call_many error at bundle 2, tx 5"));
assert!(error_msg.contains("test error"));
}

#[test]
fn call_many_error_rpc_conversion() {
// Test that CallManyError converts properly to RPC error
let source_error = std::io::Error::other("test error");
let err = EthApiError::call_many_error(1, 3, source_error);

let rpc_error: jsonrpsee_types::error::ErrorObject<'static> = err.into();

// Should be an internal error with our custom message
assert_eq!(rpc_error.code(), jsonrpsee_types::error::INTERNAL_ERROR_CODE);
assert!(rpc_error.message().contains("call_many error at bundle 1, tx 3"));
assert!(rpc_error.message().contains("test error"));
}

#[test]
fn header_not_found_message() {
let err: jsonrpsee_types::error::ErrorObject<'static> =
Expand Down
Loading