Signature & Nonce Management
Core.sol provides centralized signature verification and nonce management for all EVVM services. This unified system ensures security consistency across the entire ecosystem and prevents replay attacks in multi-service transactions.
validateAndConsumeNonce
Function Type: external
Function Signature: validateAndConsumeNonce(address,address,bytes32,address,uint256,bool,bytes)
The primary function used by all EVVM services (NameService, Staking, P2PSwap, Treasury) to validate user signatures and consume nonces atomically. This is the foundation of the centralized signature verification system.
Key Features
- Centralized Validation: Single point of signature verification for entire ecosystem
- Atomic Operation: Signature validation and nonce consumption happen together
- Service Authorization: Only smart contracts can call this function
- Flexible Executor Model: Supports both restrictive (specific executor) and permissive (any executor) modes
- User Filtering: Integrates with optional UserValidator for transaction filtering
- Cross-Service Security: Prevents replay attacks between different services
Parameters
| Field | Type | Description |
|---|---|---|
user | address | Address of the transaction signer |
senderExecutor | address | Service address authorized to execute (msg.sender). Use address(0) for flexible multi-service execution - any service with correct signature can execute and consume the nonce. Use specific address to restrict nonce consumption to that service only. |
hashPayload | bytes32 | Hash of the transaction parameters (generated by service-specific hash functions) |
originExecutor | address | Optional tx.origin restriction. Use address(0) to allow any origin. Provides additional security for multi-service transaction flows. |
nonce | uint256 | Nonce to validate and consume |
isAsyncExec | bool | true for asynchronous (parallel) nonces, false for synchronous (sequential) |
signature | bytes | User's EIP-191 authorization signature |
How Services Use This Function
All EVVM services follow this pattern:
// 1. Service generates hash of its specific operation parameters
bytes32 hashPayload = NameServiceHashUtils.hashDataForRegister(
username,
lockNumber,
amountToPay
);
// 2. Service calls Core.validateAndConsumeNonce
Core(coreAddress).validateAndConsumeNonce(
user,
address(this), // senderExecutor: restrict to this service only
hashPayload,
originExecutor,
nonce,
isAsyncExec,
signature
);
// Alternative: Use address(0) for flexible execution
Core(coreAddress).validateAndConsumeNonce(
user,
address(0), // senderExecutor: any service can execute
hashPayload,
address(0), // originExecutor: any origin allowed
nonce,
isAsyncExec,
signature
);
// 3. If no revert, signature is valid and nonce is consumed
// Service can now safely execute its logic
Workflow
-
Sender Executor Validation: If
senderExecutoris notaddress(0), validates thatmsg.sendermatches the specified address. Reverts withSenderMismatchon mismatch. Whenaddress(0), any service can execute. -
Origin Executor Validation: If
originExecutoris notaddress(0), validates thattx.originmatches the specified address. Reverts withOriginMismatchon mismatch. Provides additional security layer. -
Signature Verification: Reconstructs and validates the signature:
- Builds signature payload:
buildSignaturePayload(evvmId, senderExecutor, hashPayload, originExecutor, nonce, isAsyncExec) - Recovers signer using
SignatureRecover.recoverSigner() - Compares recovered address with
userparameter - Reverts with
InvalidSignatureon mismatch
- Builds signature payload:
-
User Validation: Checks if user is allowed to execute transactions via
canExecuteUserTransaction(user)(integrates with UserValidator if configured). Reverts withUserCannotExecuteTransactionif blocked. -
Nonce Management: Validates and consumes the nonce based on
isAsyncExec:- Async (isAsyncExec = true):
- Checks nonce status via
asyncNonceStatus(user, nonce) - If
senderExecutorwasaddress(0)in signature: any service can consume the nonce - If
senderExecutorwas specific address in signature: only that service can consume the nonce - Reverts with
AsyncNonceAlreadyUsedif already used - Reverts with
AsyncNonceIsReservedByAnotherServiceif reserved by different service - Marks nonce as used:
asyncNonce[user][nonce] = true
- Checks nonce status via
- Sync (isAsyncExec = false):
- Verifies nonce matches
nextSyncNonce[user] - Reverts with
SyncNonceMismatchon mismatch - Increments sync nonce:
++nextSyncNonce[user]
- Verifies nonce matches
- Async (isAsyncExec = true):
Security Features
Dual-Executor Model:
senderExecutor = address(0): Enables flexible multi-service execution. Any service with the correct signature structure can consume the nonce. Useful for competitive fisher markets and cross-service transactions.senderExecutor = specific address: Restricts nonce consumption to a single service. Provides deterministic execution guarantees and prevents race conditions.originExecutor: Additionaltx.originrestriction for enhanced security in multi-service flows.- Service payments: When services dispatch payments on behalf of users, both
senderExecutorandoriginExecutorshould be set to the service address for clear payment origin tracking.
Replay Attack Prevention:
- Atomic signature verification and nonce consumption
- Service-specific validation prevents cross-service replay
- Reserved nonces prevent service interference
- Nonce flexibility controlled by
senderExecutorparameter
Service Isolation:
- Each service provides unique
hashPayload senderExecutor(when specified) is part of signature payload- Signatures can be restricted per-service or shared across services
- Enables both competitive and deterministic execution models
User Protection:
- Optional UserValidator integration for compliance filtering
- Origin executor restrictions for additional security
- Consistent validation rules across all services
When building a custom EVVM service, always call validateAndConsumeNonce BEFORE executing any state changes. This ensures signatures are valid and prevents replay attacks.
Nonce Reservation System
Core.sol provides a nonce reservation system allowing users to pre-allocate async nonces to specific services. This prevents race conditions and ensures deterministic execution ordering.
reserveAsyncNonce
Function Type: external
Function Signature: reserveAsyncNonce(uint256,address)
Reserves an async nonce exclusively for a specific service address.
Parameters
| Field | Type | Description |
|---|---|---|
nonce | uint256 | The async nonce value to reserve |
serviceAddress | address | Service contract that can use this nonce |
Use Cases
- Cross-chain operations: Reserve nonces for multi-step cross-chain transactions
- Multi-signature workflows: Ensure specific executors process transactions
- Service-specific queues: Create dedicated transaction queues per service
- Front-running prevention: Block other services from using specific nonces
Workflow
- Service Validation: Checks that
serviceAddressis notaddress(0). Reverts withInvalidServiceAddressif zero. - Usage Check: Verifies nonce hasn't been used. Reverts with
AsyncNonceAlreadyUsedif already consumed. - Reservation Check: Ensures nonce isn't already reserved. Reverts with
AsyncNonceAlreadyReservedif reserved. - Reserve: Sets
asyncNonceReservedPointers[msg.sender][nonce] = serviceAddress
Example
// Reserve nonces 100-105 for NameService operations
for (uint256 i = 100; i <= 105; i++) {
core.reserveAsyncNonce(i, nameServiceAddress);
}
// Now only NameService can consume these nonces for this user
revokeAsyncNonce
Function Type: external
Function Signature: revokeAsyncNonce(address,uint256)
Revokes a previously reserved async nonce, making it available for any service.
Parameters
| Field | Type | Description |
|---|---|---|
user | address | Address that reserved the nonce |
nonce | uint256 | The async nonce to revoke reservation for |
Use Cases
- Canceling operations: Free nonces from canceled transactions
- Correcting mistakes: Fix accidental reservations
- Reallocation: Reassign nonces to different services
Workflow
- Usage Check: Verifies nonce hasn't been used. Reverts with
AsyncNonceAlreadyUsedif consumed. - Reservation Check: Ensures nonce is currently reserved. Reverts with
AsyncNonceNotReservedif not. - Clear: Sets
asyncNonceReservedPointers[user][nonce] = address(0)
Currently, revokeAsyncNonce can be called by anyone. In production, consider implementing authorization checks to ensure only the user or authorized addresses can revoke reservations.
UserValidator System
Core.sol supports an optional UserValidator contract for transaction filtering and compliance requirements. This system allows blocking specific users from executing transactions without modifying Core.sol.
Overview
The UserValidator system provides:
- Optional Filtering: Can be enabled/disabled without contract upgrades
- Time-Delayed Governance: 1-day waiting period for validator changes
- Flexible Implementation: Validator contract defines filtering logic
- Ecosystem-Wide Effect: Affects all services using
validateAndConsumeNonce
proposeUserValidator
Function Type: external (Admin only)
Function Signature: proposeUserValidator(address)
Proposes a new UserValidator contract address with a 1-day time-lock.
Parameters
| Field | Type | Description |
|---|---|---|
newValidator | address | Address of proposed UserValidator contract |
cancelUserValidatorProposal
Function Type: external (Admin only)
Function Signature: cancelUserValidatorProposal()
Cancels a pending UserValidator proposal before the time-lock expires.
acceptUserValidatorProposal
Function Type: external (Admin only)
Function Signature: acceptUserValidatorProposal()
Finalizes the UserValidator change after the 1-day time-lock period.
Requirements:
- Time-lock period must have passed
- Reverts with
ProposalForUserValidatorNotReadyif called too early
IUserValidator Interface
Custom UserValidator contracts must implement:
interface IUserValidator {
function canExecute(address user) external view returns (bool);
}
Implementation Examples:
// Whitelist-based validator
contract WhitelistValidator is IUserValidator {
mapping(address => bool) public whitelisted;
function canExecute(address user) external view returns (bool) {
return whitelisted[user];
}
}
// Blacklist-based validator
contract BlacklistValidator is IUserValidator {
mapping(address => bool) public blacklisted;
function canExecute(address user) external view returns (bool) {
return !blacklisted[user];
}
}
// KYC-based validator
contract KYCValidator is IUserValidator {
mapping(address => bool) public kycVerified;
function canExecute(address user) external view returns (bool) {
return kycVerified[user];
}
}
When a UserValidator is active, validateAndConsumeNonce calls validator.canExecute(user) before processing any transaction. If it returns false, the transaction reverts with UserCannotExecuteTransaction.
Related Functions
For information on nonce getter functions, see: