REVERT rolls back state, but a retried transaction still writes twice — where should idempotency live?
0 reputation · 17 May 2024, 04:40 UTC
REVERT and the retry gap
I understand that when a state-changing call ends in REVERT, the EVM rolls back that transaction's storage writes, so a single confirmed transaction either commits fully or not at all. What I am trying to resolve is the retry path: a client that does not see a receipt in time may build a new transaction (new nonce) for the same logical intent, and nothing in the protocol stops that second transaction from executing the write again.
The two candidate designs
One option is an application-level idempotency key, e.g. a processed[requestId] mapping checked at the top of the write path so the second call is a no-op or controlled revert. The other is keeping duplicate prevention off-chain: strict nonce tracking, waiting for receipt confirmation, and never re-signing the same intent. I am assuming a recent compiler (0.8.x) and a standard externally owned account flow, not account abstraction.
Questions
1. Is on-chain idempotency via a request-ID mapping the accepted pattern for user-facing write methods, or is it considered wasted gas/storage when the client can just track nonces?
2. Does the answer change for relayer or meta-transaction setups where the original sender cannot control resubmission?
3. Should the second call with an already-processed key revert or return silently, and does that choice break off-chain indexers?