This EIP proposes a new JSON-RPC method, eth_sendRawTransactionSync, which submits a signed raw transaction and waits synchronously for the transaction receipt or a configurable timeout before returning. This method addresses the user experience gap in high-frequency applications by offering stronger delivery guarantees than eth_sendRawTransaction. Additionally, when a transaction cannot be immediately executed due to a nonce gap, it returns the expected nonce as a hex string in the error response, eliminating the need for additional RPC calls to query account state.
Motivation
Currently, Ethereum clients submit signed transactions asynchronously using eth_sendRawTransaction. Clients receive a transaction hash immediately but must poll repeatedly for the transaction receipt, which increases latency and complicates client-side logic.
This asynchronous approach is not efficient for high-frequency blockchains or Layer 2 solutions with fast block times and low latency, where rapid transaction throughput and quick confirmation feedback are critical. The need to separately poll for receipts results in increased network overhead, slower overall transaction confirmation feedback, and more complex client implementations.
Additionally, when transactions cannot be immediately executed (e.g., due to nonce gaps or insufficient funds), existing methods provide generic error messages that don’t help developers understand or fix the issue. Developers must make additional RPC calls to query account state, creating unnecessary round-trips and delays.
In a low-latency blockchain, transaction receipts are often available right after the transactions land in the block producer’s mempool. Requiring an additional RPC call introduces unnecessary latency.
eth_sendRawTransactionSync addresses these issues by combining transaction submission and receipt retrieval into a single RPC call. This helps:
reduce total transaction submission and confirmation latency by approximately 50%;
simplify client implementations by eliminating the need for separate polling loops;
improve user experience by enabling more responsive dApps and wallets;
align blockchain interactions closer to traditional Web2 request-response patterns;
maintain backward compatibility and optionality, preserving existing RPC methods and semantics.
Specification
Method Name
eth_sendRawTransactionSync
Parameters
Position
Type
Description
Required
1
DATA
The signed transaction data
Yes
2
INT
Maximum wait time in milliseconds
No
Parameter Validation Rules
Transaction Data. MUST be a valid hex-encoded, RLP-encoded signed transaction (same as in eth_sendRawTransaction).
Timeout. MUST be a positive integer not greater than the node-configured maximum timeout.
Returns
On success. Node implementations MUST return the transaction receipt object as defined by the eth_getTransactionReceipt method.
On timeout error. Node implementations MUST return an error code 4 with a timeout message.
On unreadiness error. Node implementations SHOULD return an error code 5 with an error message.
This happens when the processing node is not ready to accept a new transaction or the transaction is erroneous (DX improvement).
On standard error. Node implementations MUST return a JSON-RPC error object consistent with existing RPC error formats.
Error Codes and Response Structure
The following error codes are specific to eth_sendRawTransactionSync:
Code
Error Type
Description
Data Format
4
Timeout
Transaction was added to mempool but not processed within timeout
Transaction hash (hex)
5
Unknown/Queued
Transaction is NOT added to mempool (not ready for execution)
Transaction hash (hex)
6
Nonce Gap
Transaction is NOT added to mempool (nonce gap detected)
Expected nonce (hex)
When an error occurs, the response includes:
code: The error code indicating the error type
message: A human-readable error message
data: Error-specific data:
For error code 4 (Timeout): Contains the transaction hash as a hex string (e.g., "0x1234abcd...")
For error code 5 (Unknown/Queued): Contains the transaction hash as a hex string (e.g., "0x1234abcd...")
For error code 6 (Nonce Gap): Contains the expected nonce as a hex string (e.g., "0x5")
Node-Configured Timeouts
The handler function of this RPC SHOULD incorporate a configurable timeout when waiting for receipts (RECOMMENDED: 2 seconds).
Node implementations SHOULD provide a way to configure the timeout duration.
Node operators MAY implement dynamic timeout adjustment based on real-time network conditions.
Behavior
Upon receiving an eth_sendRawTransactionSync request, the handler function performs the following tasks.
If timeout parameter is provided, the handler function MUST validate its validity.
If the timeout is invalid, the handler function MUST use the default node-configure timeout.
The handler function MUST check if the transaction is ready for immediate execution BEFORE adding it to the mempool:
If the transaction has a nonce gap (transaction nonce is higher than the expected nonce), the handler function MUST NOT add the transaction to the mempool and MUST return error code 6 with the expected nonce as a hex string directly in the data field.
If the transaction is queued for any other reason (not ready for immediate execution), the handler function MUST NOT add the transaction to the mempool and MUST return error code 5.
If the transaction is ready for immediate execution, the handler function MUST submit the signed transaction to the mempool as per the existing eth_sendRawTransaction semantics.
The handler function MUST wait for the transaction receipt until the timeout elapses.
If the receipt is found within the specified timeout, the handler function MUST return it immediately.
If the timeout expires without obtaining a receipt, the handler function MUST return an error code 4 with a timeout message and the transaction hash.
If the transaction submission fails (e.g., due to invalid transaction data), the handler function MUST return an error (following the eth_sendRawTransaction definition) immediately.
{"jsonrpc":"2.0","id":1,"error":{"code":4,"message":"The transaction was added to the mempool but wasn't processed within the designated timeout interval.","data":"0x1234abcd..."}}
Note: The data field contains the transaction hash as a hex string.
Example Response (Nonce Gap - Error Code 6)
{"jsonrpc":"2.0","id":1,"error":{"code":6,"message":"The transaction was rejected due to a nonce gap. Please resubmit with the next on-chain nonce.","data":"0x5"}}
Note: The data field contains the expected nonce as a hex string. No transaction hash is returned because the transaction was never added to the mempool.
Example Response (Rejected Transaction - Error Code 5)
{"jsonrpc":"2.0","id":1,"error":{"code":5,"message":"The transaction was rejected for an unknown reason.","data":"0x1234abcd..."}}
Modifying eth_sendRawTransaction to support this behavior would risk compatibility issues and ambiguity. A separate method makes the semantics explicit and opt-in.
Node-Configured Timeouts
Node implementations SHOULD allow configuration of the timeout period, defaulting to 2 seconds (depending on the implementation). This balances responsiveness and propagation guarantees without creating excessive overhead in node clients.
User-Configured Timeouts
The optional timeout parameter allows clients to specify their preferred maximum wait time for transaction processing.
Applications can adjust timeouts based on their specific latency requirements.
The optional timeout prevents the RPC call from blocking indefinitely.
Optionality
This method is optional and does not replace or change existing asynchronous transaction submission methods. Nodes that do not implement this method will continue to operate normally using the standard asynchronous RPC methods.
This RPC method is particularly suitable for EVM-compatible blockchains or L2 solutions with fast block times and low network latency, where synchronous receipt retrieval can significantly improve responsiveness. On high-latency or slower blockchains (e.g., Ethereum mainnet pre-sharding), the synchronous wait may cause longer RPC call durations or timeouts, making the method less practical.
Improved UX
The synchronous receipt retrieval reduces the complexity of client applications by eliminating the need for separate polling logic.
Returned Nonces in Error Code 6
When a transaction is rejected due to a nonce gap, error code 6 returns the expected nonce as a hex string in the data field. This design provides several benefits:
Immediate Nonce Recovery: Applications receive the correct nonce directly in the error response, eliminating the need for a separate eth_getTransactionCount call.
Reduced Latency: By avoiding additional RPC round-trips to query account state, applications can immediately construct and resubmit the transaction with the correct nonce.
Backwards Compatibility
This EIP introduces a new RPC method and does not modify or deprecate any existing methods. Nodes that do not implement this method will continue operating normally. Existing applications using eth_sendRawTransaction are unaffected. Node implementations that do not support the method will simply return method not found.
Reference Implementation
A minimal reference implementation can be realized by wrapping existing eth_sendRawTransaction submission with logic that waits for the corresponding transaction receipt until a timeout elapses. Implementations MAY either rely on event-driven receipt-availability notifications or poll eth_getTransactionReceipt at short intervals until a receipt is found or a timeout occurs. Polling intervals or notification strategies and timeout values can be tuned by node implementations to optimize performance.
For example, in reth, we can implement the handler for eth_sendRawTransactionSync as follows.
asyncfnsend_raw_transaction_sync(&self,tx:Bytes,user_timeout_ms:Option<u64>,)->RpcResult<OpTransactionReceipt>{constMAX_TIMEOUT_MS:u64=2_000;constERROR_CODE_TIMEOUT:i32=4;constERROR_CODE_UNKNOWN:i32=5;constERROR_CODE_NONCE_GAP:i32=6;constERROR_MSG_TIMEOUT_RECEIPT:&str="The transaction was added to the mempool but wasn't processed within the designated timeout interval.";constERROR_MSG_UNKNOWN:&str="The transaction was rejected for an unknown reason.";constERROR_MSG_NONCE_GAP:&str="The transaction was rejected due to a nonce gap. Please resubmit with the next on-chain nonce.";letstart_time=Instant::now();lettimeout=Duration::from_millis(user_timeout_ms.map_or(MAX_TIMEOUT_MS,|ms|ms.min(MAX_TIMEOUT_MS)),);letpool_transaction=OpPooledTransaction::from_pooled(recover_raw_transaction(&tx)?);letsender=pool_transaction.sender();letoutcome=self.pool.add_transaction(TransactionOrigin::Local,pool_transaction).await.map_err(OpEthApiError::from_eth_err)?;// If transaction is queued (not ready for immediate execution), remove it and return errorifletAddedTransactionState::Queued(reason)=outcome.state{self.pool.remove_transaction(outcome.hash);matchreason{QueuedReason::NonceGap=>{letexpected_nonce=self.pending_state.basic_account(&sender).ok().flatten().map(|acc|format!("0x{:x}",acc.nonce));returnErr(ErrorObject::owned(ERROR_CODE_NONCE_GAP,ERROR_MSG_NONCE_GAP,expected_nonce,));}_=>{returnErr(ErrorObject::owned(ERROR_CODE_UNKNOWN,ERROR_MSG_UNKNOWN,Some(hash),));}}}lethash=outcome.hash;matchself.pending_state.get_receipt(hash,timeout.saturating_sub(start_time.elapsed())).await{Some(receipt)=>Ok(receipt),None=>Err(ErrorObject::owned(ERROR_CODE_TIMEOUT,ERROR_MSG_TIMEOUT_RECEIPT,Some(hash),)),}}
Other implementations such as go-ethereum can utilize a channel to signify receipt availability instead of polling.
Security Considerations
This method does not introduce new security risks beyond those inherent in transaction submission.
The node-configured timeout prevents indefinite blocking of RPC calls, protecting nodes from hanging requests.
Node implementations should handle timeout responses gracefully and continue monitoring transaction status as needed.
Nodes must ensure that the implementation does not degrade performance or cause denial-of-service.