Skip to content

refactor(recovery-queue): scan calldata with per-payload windows#63

Open
mithatakbulut wants to merge 2 commits intoreview/remove-wallet-wdkfrom
review/recovery-queue-per-payload-window
Open

refactor(recovery-queue): scan calldata with per-payload windows#63
mithatakbulut wants to merge 2 commits intoreview/remove-wallet-wdkfrom
review/recovery-queue-per-payload-window

Conversation

@mithatakbulut
Copy link
Copy Markdown
Contributor

Summary

  • Replaces the recovery queue payload-body lookup with a per-payload timestamp window scan over chain calldata.
  • Fixes a hash-domain mismatch that caused every decoded payload to be silently rejected.
  • Drops the Arweave reader path for queue payload bodies (dead — owner whitelist always misses).
  • Refreshes the supported chain / RPC list (independent housekeeping; included to keep the stack moving).

Why

Queue payloads were being listed (metadata is on-chain) but payload.payload was always undefined, leaving the Execute button permanently disabled. On Polygon, the listing itself took 6+ minutes (HAR: ~437s wall time, 95% in publicnode RPC).

Five issues stacked on top of each other:

  1. Hash-domain mismatch. decodeQueuedPayloadInput validated against Extensions.Recovery.hashRecoveryPayload(...) (recovery-flag domain — used for signing), but the contract stores queued payloads keyed by Payload.hash(wallet, chainId, payload) (no recovery flag — storage key). Every candidate tx was rejected even when the calldata matched.
  2. Topic-filtered eth_getLogs timed out on publicnode Polygon outside very small windows (~10s timeout × 3 viem retries = 30s of dead time per chunk).
  3. Single mega min..max queuedAt window, then sequential chunking on top of publicnode's 10000-block cap → 21 sequential chunks × ~10s.
  4. Binary-search block discovery (~27 iterations × 2 boundaries = ~54 sequential getBlock calls per chain).
  5. Spread queuedAt scenario (e.g. payloads queued 9 / 5 / 3 months ago) blows up the single window into millions of blocks even after the per-chain block estimator lands. Per-payload windows are the only structurally correct fix.

Architecture context

The recovery extension contract stores only the payload hash + timestamp on-chain (gas optimization). The actual call body (to, value, data, …) is never written to storage — it only exists in the calldata of the original queuePayload tx.

Sequence's hosted UI presumably side-channels the body to its own state service when queueing. This app explicitly does not use Sequence services (State.Sequence.Provider, keymachine.sequence.app, hosted state APIs are out of scope by design — the app must work with Sequence services down). Arweave reads are still allowed for wallet config / topology, but Arweave does not contain queued payload bodies, and the reader filters by Sequence's owner key, so any "fallback to Arweave" was always going to miss.

That leaves chain calldata as the only source of truth for queued payload bodies.

@mithatakbulut mithatakbulut requested a review from a team as a code owner May 3, 2026 19:45
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.

1 participant