Skip to content

relayer nonce reuse#98

Closed
aa-eyup wants to merge 3 commits into
joshstevens19:masterfrom
aave:ATEAM-1506-relayer_nonce_reuse
Closed

relayer nonce reuse#98
aa-eyup wants to merge 3 commits into
joshstevens19:masterfrom
aave:ATEAM-1506-relayer_nonce_reuse

Conversation

@aa-eyup

@aa-eyup aa-eyup commented May 11, 2026

Copy link
Copy Markdown
Contributor

Motivation

POST /transactions/relayers/:relayer_id/send could reserve and advance a relayer nonce before gas estimation/simulation had fully succeeded. If simulation reverted, the API returned a server error and could leave a PENDING transaction row for a transaction that was never broadcast. Client retries could then consume future nonces due to a lack of idempotent handling.

This PR makes sends safer by moving nonce reservation later (while holding a lock from the beginning of the send lifecycle), classifying deterministic simulation failures as client errors, and adding optional per-relayer external_id idempotency so clients can safely retry accepted or failed sends without consuming additional nonces.

Changes

  • Added optional per-relayer external_id idempotency with a partial unique index.
  • Moved nonce reservation after successful gas estimation/simulation, using a scoped rollback guard.
  • Persist deterministic pre-broadcast simulation failures as FAILED, not PENDING.
  • Map deterministic simulation reverts to 400 Bad Request; keep RPC/transport failures retryable.
  • Initialize nonce cursor from max(chain pending nonce, highest open local nonce + 1). Previously nonces were only initialized from onchain data which ignored in-flight transactions.
  • Preflight cancel/replace duplicate external_ids before broadcasting.

Flow Diagram

POST /transactions/relayers/:relayer_id/send
  |
  |  API auth / permissions / rate limit
  v
find relayer queue
  |
  |  if external_id is present:
  v
lookup existing tx by (relayer_id, external_id)
  |
  +-- found + payload mismatch
  |     -> 409 Conflict
  |
  +-- found + hash present
  |     -> return existing id/hash
  |
  +-- found + no hash
  |     -> 400 Bad Request
  |
  +-- not found
        |
        v
lock relayer queue
  |
  +-- relayer paused
  |     -> 403 Forbidden
  |
  +-- unsupported blob tx
  |     -> error
  |
  v
build transaction with placeholder nonce
  |
  v
gas price + gas estimation / simulation
  |
  +-- deterministic simulation revert
  |     |
  |     v
  |   insert FAILED row
  |     |
  |     +-- insert wins
  |     |     -> 400 Bad Request
  |     |
  |     +-- external_id conflict
  |           -> resolve existing tx
  |
  +-- RPC / transport failure
  |     -> server/upstream error, no nonce reserved
  |
  v
sync nonce cursor with on-chain nonce
  |
  v
reserve next nonce
  |
  |  reservation increments cursor and holds guard
  v
assign nonce + compute transaction hash
  |
  +-- build/hash failure
  |     -> drop reservation, rollback cursor
  |
  v
insert PENDING tx row
  |
  +-- external_id conflict
  |     -> drop reservation, rollback cursor
  |     -> resolve existing tx
  |
  +-- other DB failure
  |     -> drop reservation, rollback cursor
  |
  v
add tx to in-memory pending queue
  |
  v
commit nonce reservation
  |
  v
return id/hash

@vercel

vercel Bot commented May 11, 2026

Copy link
Copy Markdown

@aa-eyup is attempting to deploy a commit to the joshaavecom's projects Team on Vercel.

A member of the Team first needs to authorize it.

@aa-eyup aa-eyup closed this May 11, 2026
@aa-eyup aa-eyup deleted the ATEAM-1506-relayer_nonce_reuse branch May 11, 2026 23:49
@aa-eyup aa-eyup restored the ATEAM-1506-relayer_nonce_reuse branch May 11, 2026 23:50
@aa-eyup aa-eyup changed the title Ateam 1506 relayer nonce reuse relayer nonce reuse May 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants