Skip to content

Add nonce registry contract for gasless replay protection - #1078

Open
shinorh247 wants to merge 3 commits into
accesslayerorg:mainfrom
shinorh247:feat/1070-nonce-registry
Open

shinorh247 wants to merge 3 commits into
accesslayerorg:mainfrom
shinorh247:feat/1070-nonce-registry

Conversation

@shinorh247

Copy link
Copy Markdown
Contributor

Off-chain signed meta-transactions had no on-chain record of which nonces were already submitted, so a relayer could submit the same signed message twice. Track consumed nonces per wallet in a dedicated contract: consume_nonce burns a nonce and reverts with NonceAlreadyUsed on a replay, is_nonce_used exposes the consumed state, and bulk_invalidate lets a wallet pre-emptively invalidate its own pending nonces. Nonces are independent uint64 values with no ordering, and NonceConsumed carries wallet and nonce so relayers can index them.

🤖 Generated with Codebuff

Summary

Testing

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo test --workspace

Checklist

  • Linked issue or backlog item
  • Added or updated creator-keys unit/integration tests for every changed contract behavior, including failure paths for new or reachable ContractError variants
  • Ran cargo fmt --all -- --check, cargo clippy --workspace --all-targets -- -D warnings, and cargo test --workspace, or explained exactly why a command was not run
  • Reviewed persistent storage changes against docs/storage-key-invariants.md; any storage layout change includes a migration/backward-compatibility note
  • Confirmed event names, topic order, payload field order, and field meanings remain compatible with docs/contract-event-conventions.md, or documented the breaking Summary

Implements a nonce registry contract to prevent replay attacks in the gasless signed transaction flow by tracking consumed nonces for individual wallets.

Changes
• Implement consume_nonce(wallet, nonce) to mark a nonce as consumed and revert with NonceAlreadyUsed if it has already been used.
• Implement is_nonce_used(wallet, nonce) to check whether a wallet’s nonce has been consumed.
• Implement bulk_invalidate(wallet, nonces[]) to allow wallets to pre-emptively invalidate multiple nonces.
• Restrict bulk nonce invalidation to the wallet owner.
• Support uint64 nonces without enforcing sequential ordering.
• Emit the NonceConsumed event with the wallet address and nonce when a nonce is consumed for the first time.

Security
• Prevents replay attacks by rejecting duplicate nonce consumption.
• Keeps nonce tracking scoped to individual wallets.
• Prevents unauthorized bulk invalidation of another wallet’s nonces.

Acceptance Criteria
• Duplicate nonce consumption reverts with NonceAlreadyUsed.
• is_nonce_used correctly reports used and unused nonces.
• bulk_invalidate marks all supplied nonces as consumed.
• Bulk invalidation is restricted to the wallet owner.
• NonceConsumed is emitted correctly on first consumption.

Testing
• Tests should cover duplicate nonce rejection, nonce state queries, bulk invalidation, access control, and event emission.

Closes #1070

Off-chain signed meta-transactions had no on-chain record of which nonces were
already submitted, so a relayer could submit the same signed message twice.
Track consumed nonces per wallet in a dedicated contract: consume_nonce burns a
nonce and reverts with NonceAlreadyUsed on a replay, is_nonce_used exposes the
consumed state, and bulk_invalidate lets a wallet pre-emptively invalidate its
own pending nonces. Nonces are independent uint64 values with no ordering, and
NonceConsumed carries wallet and nonce so relayers can index them.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@drips-wave

drips-wave Bot commented Oct 18, 2026

Copy link
Copy Markdown

@shinorh247 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

shinorh247 and others added 2 commits October 18, 2026 01:46
The merge kept both sides of the Cargo.toml members list and folded the
multisig-admin and nonce-registry package blocks in Cargo.lock into a single
block with two name keys. cargo metadata then aborted with a duplicate key, so
CI failed at the format check before clippy or the tests could run. Unify the
members list and restore one valid lock block per crate.
@shinorh247

Copy link
Copy Markdown
Contributor Author

Pls review and merge

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.

Build nonce registry contract for gasless signed transaction replay protection

1 participant