ethereum / ethereum/execution-apis

JSON-RPC edge-cases, nonce management and `eth_getTransactionBySenderAndNonce`

Open
#494 18 comments 3 reactions 0 assignees View on GitHub
Dominant language
Io
Stars
1.1k
Forks
530
Avg merge
5d 8h
Merged PRs (30d)
9

Description

During the JSON-RPC meeting in Istanbul, I made a case that `eth_getTransactionBySenderAndNonce` would be useful for dapp and tool developers. I'm opening this issue to continue the discussion here, and to go into more detail.

## Problems with the JSON-RPC

These are the problems that I think could be solved with `eth_getTransactionBySenderAndNonce`.

### Problem 1: Losing track of a transaction

I created this pictures of how a the normal workflow of sending a transaction from a dapp or tool that interacts with a wallet looks like:

![image](https://github.com/ethereum/execution-apis/assets/176499/ce4f3e1f-b07e-4480-8560-ac6fb92d5d1d)

As shown above, the flow would be something like this:

1. The dapp/tool wants to send a transaction, so it calls into a library like ethers/web3/viem.
2. The library is connected to a wallet, which exposes the same JSON-RPC interface as a client. Note that the wallet doesn't just forward things to a client, it has its own implementation of some methods, like `eth_sendTransaction`.
3. To send a transaction, the library sends an `eth_sendTransaction` to the wallet.
4. The wallet receives that request and creates and signs a transaction locally.
5. At this point, the wallet is already aware of the full tx body and hence its hash, but it hasn't communicated it to the the library yet.
6. The wallet will then do an `eth_sendRawTransaction`, sending the full tx body to an EL client, which will broadcast it.
7. The wallet returns a hash as the response of `eth_sendTransaction`, which the library receives and passes to the dapp.

If after sending the tx to the EL client anything goes wrong with the wallet, library or dapp, a transaction will be broadcasted as requested by the dapp, but the dapp will never be able to access it.

Note that the order of 6 and 7 are unspecified. The wallet could return the hash before sending the raw transaction. As it's unclear, we are forced to assume the worst situation is possible.

If we had `eth_getTransactionBySenderAndNonce` a dapp could keep track of the nonce and transaction params, and resume from errors if needed, by checking if a transaction with that sender and nonce exists, and if it has the same params as the one that it intended to send.

### Problem 2: Replaced and dropped transactions

When a dapp/tool sends a transaction, the tx can be replaced by another one with the same nonce and sender, or dropped from the mempool. What's more, replacement transactions can be equivalent to the one you sent (e.g. just higher fees), or a completely different one (e.g. to a different account).

All of these situations manifest as a `eth_getTransactionByHash` returning `null`.

The way to distinguish between some of them (replaced vs dropped) is by using `eth_getTransactionCount` with different blocktags and analyzing a pretty complex set of cases, including making tons of assumptions about how `"pending"` works in this case.

If we had `eth_getTransactionBySenderAndNonce` a dapp could use it to understand what happened if `eth_getTransactionByHash` returns `null`.

## Proposal

Add a method `eth_getTransactionBySenderAndNonce` with two parameters:

1. An address: the sender of the transaction.
2. A quantity: the nonce of the transaction.

The JSON-RPC server should return the ~hash of a transaction (or the full tx?)~ transaction object sent from the provided address using that nonce. If no such transaction exists, it should return `null`.

If multiple transactions from a sender and nonce were received, I assume there's one that can be considered the best candidate for inclusion (e.g. pays more fees), and that one should be used.

## Things to consider

1. Does this method have other use cases?
4. Are there any alternative solution?
1. How many resources would this method require for transactions that are still in the mempool?
5. How many resources would this method require for transactions that were included in a block?
6. How many blocks are really important to solve the problems listed above?
7. How can RPC/Node providers (e.g. Infura) support this?

_Tagging people present on the in-person discussion, or who I've discussed this in the past: @s1na @lightclient @sambacha @kanej_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.