-
Notifications
You must be signed in to change notification settings - Fork 124
feat: implement eip-7966 #3095
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: nightly
Are you sure you want to change the base?
feat: implement eip-7966 #3095
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| //! EIP-7966: eth_sendRawTransactionSync method | ||
| //! Description: A JSON-RPC method to reduce transaction submission latency | ||
| //! by allowing synchronous receipt of transaction hash and block inclusion. | ||
| //! | ||
| //! This module provides utilities for implementing EIP-7966, which submits | ||
| //! a signed raw transaction and waits synchronously for the transaction receipt | ||
| //! or a configurable timeout before returning. | ||
| //! | ||
| //! See: https://eips.ethereum.org/EIPS/eip-7966 | ||
| use alloy_primitives::B256; | ||
| use jsonrpsee::types::ErrorObjectOwned; | ||
|
|
||
| /// EIP-7966 error code 4: Transaction was added to mempool but not processed within timeout. | ||
| pub const TIMEOUT_ERROR_CODE: i32 = 4; | ||
|
|
||
| /// EIP-7966 error code 5: Node is not ready to process the transaction or the transaction is erroneous. | ||
| pub const UNREADY_ERROR_CODE: i32 = 5; | ||
|
|
||
| /// Default timeout in milliseconds. (2secs) | ||
| pub const DEFAULT_TIMEOUT_MS: u64 = 2_000; | ||
|
|
||
| /// Maximum allowed timeout in milliseconds. (1min) | ||
| pub const MAX_TIMEOUT_MS: u64 = 60_000; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we make this configurable?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should be passable from RpcConfig but default value is used when env var is not found |
||
|
|
||
| /// Creates an EIP-7966 timeout error (code 4). | ||
| /// | ||
| /// Returned when the transaction was added to the mempool but wasn't | ||
| /// processed within the specified timeout period. | ||
| /// | ||
| /// # Arguments | ||
| /// * `hash` - The transaction hash that was submitted | ||
| /// * `timeout_ms` - The timeout that was used (in milliseconds) | ||
| pub fn timeout_error(hash: B256, timeout_ms: u64) -> ErrorObjectOwned { | ||
| let timeout_secs = timeout_ms as f64 / 1000.0; | ||
| ErrorObjectOwned::owned( | ||
| TIMEOUT_ERROR_CODE, | ||
| format!( | ||
| "The transaction was added to the mempool but wasn't processed in {timeout_secs}s." | ||
| ), | ||
| Some(hash), | ||
| ) | ||
| } | ||
|
|
||
| /// Creates an EIP-7966 unreadiness error (code 5). | ||
| /// | ||
| /// Returned when the processing node is not ready to accept a new transaction or the transaction is erroneous. | ||
| /// | ||
| /// # Arguments | ||
| /// * `reason` - Unreadiness error string | ||
| /// * `hash` - Optional transaction hash if the transaction was successfully submitted | ||
| pub fn unready_error(reason: &str, hash: Option<B256>) -> ErrorObjectOwned { | ||
| ErrorObjectOwned::owned(UNREADY_ERROR_CODE, reason.to_string(), hash) | ||
| } | ||
|
|
||
| /// Calculates the effective timeout | ||
| /// | ||
| /// - If `None`, returns `DEFAULT_TIMEOUT_MS` | ||
| /// - If `Some(0)` returns `DEFAULT_TIMEOUT_MS` | ||
| /// - Otherwise, returns the minimum of the requested value and `MAX_TIMEOUT_MS` | ||
| /// | ||
| /// # Arguments | ||
| /// * `requested_ms` - Optional timeout duration in milliseconds | ||
| pub fn calculate_timeout_ms(requested_ms: Option<u64>) -> u64 { | ||
| match requested_ms { | ||
| Some(ms) if ms > 0 => std::cmp::min(ms, MAX_TIMEOUT_MS), | ||
| _ => DEFAULT_TIMEOUT_MS, | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn test_timeout_error() { | ||
| let hash = B256::ZERO; | ||
| let err = timeout_error(hash, 5000); | ||
|
|
||
| assert_eq!(err.code(), TIMEOUT_ERROR_CODE); | ||
| assert!(err.message().contains("5s")); | ||
| assert!(err.message().contains("wasn't processed")); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_unready_error_with_hash() { | ||
| let hash = B256::ZERO; | ||
| let err = unready_error("Test reason", Some(hash)); | ||
|
|
||
| assert_eq!(err.code(), UNREADY_ERROR_CODE); | ||
| assert_eq!(err.message(), "Test reason"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_unready_error_without_hash() { | ||
| let err = unready_error("No hash provided", None); | ||
|
|
||
| assert_eq!(err.code(), UNREADY_ERROR_CODE); | ||
| assert_eq!(err.message(), "No hash provided"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_calculate_timeout_default() { | ||
| assert_eq!(calculate_timeout_ms(None), DEFAULT_TIMEOUT_MS); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_calculate_timeout_zero() { | ||
| assert_eq!(calculate_timeout_ms(Some(0)), DEFAULT_TIMEOUT_MS); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_calculate_timeout_normal() { | ||
| assert_eq!(calculate_timeout_ms(Some(5000)), 5000); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_calculate_timeout_exceeds_max() { | ||
| assert_eq!(calculate_timeout_ms(Some(100_000)), MAX_TIMEOUT_MS); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.