3. Verifier
3.1 Purpose and invariants
The Verifier is a single shared contract responsible for signature verification and scheme dispatch only. It has no records, no roles, no policy state, and no per-name configuration.
A conformant Verifier:
- MUST be permissionlessly callable. Any contract or EOA can invoke
verify(...)without prior registration. - MUST NOT hold per-call state.
verifyisview; no replay protection lives at this layer. - MUST NOT revert on an unsupported
schemeId. It MUST returnvalid = falseinstead, so the caller (typically the AuthResolverImpl'sverifyAction) can map the outcome to a structuredDenyReason(per §5.1). - MUST be deployed once per chain. The AuthResolverImpl's deployment salt embeds a
versionId(§4.3); the Verifier's address is captured by that versioning.
3.2 Registered signature schemes (v1)
v1 ships exactly three registered schemes, chosen to cover the EOA, smart-contract-account, and passkey signing models present across named Wave-1 MARP candidates from day zero:
| schemeId | Scheme | Verification primitive | Covers |
|---|---|---|---|
keccak256("WebAuthn-ES256") | ECDSA over P-256 with SHA-256 challenge hashing | EIP-7951 P-256 precompile | Passkey-backed signing (WebAuthn authenticators) |
keccak256("ECDSA-secp256k1") | ECDSA over secp256k1 with keccak256 challenge hashing | ecrecover | EOA signing, including standard agent-runtime signing endpoints (e.g., Bankr Agents' /agent/sign) |
keccak256("EIP-1271") | Contract-account signature | staticcall to pubKey-encoded contract's isValidSignature(bytes32, bytes) returning the EIP-1271 magic value | Smart-contract-account signing (EIP-7702-delegated wallets, ZeroDev/Kernel, generic ERC-4337 accounts) |
A conformant Verifier:
- MUST register all three schemes at deployment. Returning
falsefromisSchemeSupportedfor any of the three is a conformance violation. - MUST dispatch by
schemeId. The mapping above is the source of truth for v1. - For
WebAuthn-ES256, themessageparameter MUST be passed to the EIP-7951 precompile as the pre-hashed challenge per the WebAuthn assertion verification procedure. The Verifier MUST NOT re-hash the message before precompile invocation. - For
ECDSA-secp256k1, thepubKeyparameter MUST be 64 bytes (uncompressed, no0x04prefix); the Verifier MUST recover the address viaecrecoverand compare againstkeccak256(pubKey)[12:]. EIP-712 typed-data hashing is the caller's responsibility — the Verifier sees the final 32-byte digest inmessage. - For
EIP-1271, thepubKeyparameter MUST be a 20-byte contract address (left-padded tobytesif the caller passes a longer encoding; the Verifier MUST take the rightmost 20 bytes). The Verifier MUST staticcallisValidSignature(bytes32 hash, bytes signature)and returntrueif and only if the returned 4 bytes equal0x1626ba7e.
3.3 IVerifier interface (NORMATIVE)
interface IVerifier {
/// @notice Verify a signature using a registered signature scheme.
/// @param schemeId keccak256 identifier (see §3.2).
/// @param message Scheme-specific message bytes. For WebAuthn-ES256 this is the
/// pre-hashed challenge; for ECDSA-secp256k1 this is a 32-byte digest;
/// for EIP-1271 this is a 32-byte hash passed to the contract.
/// @param signature Scheme-specific signature blob.
/// @param pubKey Scheme-specific public key (uncompressed P-256 point for WebAuthn-ES256;
/// 64-byte uncompressed secp256k1 key; 20-byte address for EIP-1271).
/// @return valid True iff the signature verifies under the scheme.
function verify(bytes32 schemeId,
bytes calldata message,
bytes calldata signature,
bytes calldata pubKey) external view returns (bool valid);
/// @notice Whether the Verifier dispatches the given scheme.
function isSchemeSupported(bytes32 schemeId) external view returns (bool);
}3.4 Dispatch surface and extensibility
The schemeId parameter is the extensibility surface. Future cycles MAY register additional schemes (BLS12-381 aggregation, post-quantum candidates) by deploying a successor Verifier and bumping the versionId embedded in the AuthResolverImpl deployment salt (§4.3). Adding a scheme is a new Verifier deployment, not an upgrade of the v1 Verifier — v1 is immutable at the dispatch layer.
A conformant Verifier MUST NOT expose a setter that adds, removes, or modifies scheme handlers post-deployment. Scheme registration is fixed at construction.
3.5 Security considerations
- No batch entry point. v1 does not expose a batched
verifyMany. Relying parties needing to verify N signatures callverifyN times. Deferred to v1.1 if pilot integrations request it. - No replay protection. Replay binding is the caller's responsibility (typically via
stateHashinVerificationResultper §5.1, or via EIP-712 typed-data nonces in the application-layer message). - Scheme handler trust. The EIP-1271 path executes a staticcall to a contract address the caller controls (via
pubKey). The Verifier MUST treat the call as untrusted (gas-bounded, revert-safe). Implementations SHOULD cap the staticcall gas to a documented limit (e.g., 100,000 gas) and treat any revert asvalid = false. - Precompile availability. The EIP-7951 precompile MUST be live on the target chain at the AuthResolverImpl deployment block. Per ENS documentation (search "EIP-7951"), the precompile is available on Ethereum mainnet after the Fusaka hardfork. Per-L2 availability is not uniformly enumerated; deployment targets MUST confirm precompile presence on each chain before the AuthResolverImpl proxy is deployed there.
3.6 Conformance criteria (NORMATIVE)
A conformant Verifier satisfies every MUST/MUST NOT and SHOULD/MAY in this table. This restates the normative content in §3.1–§3.5; the source sections remain authoritative for any discrepancy.
| # | Requirement | Type | Source |
|---|---|---|---|
| V1 | Permissionlessly callable; no per-call state | MUST | §3.1 |
| V2 | verify is view; no replay protection at this layer | MUST | §3.1, §3.5 |
| V3 | Return false (not revert) for unsupported schemeId | MUST | §3.1 |
| V4 | Deployed once per chain | MUST | §3.1 |
| V5 | Register all three v1 schemes (WebAuthn-ES256, ECDSA-secp256k1, EIP-1271) at construction | MUST | §3.2 |
| V6 | Dispatch by schemeId per the §3.2 table | MUST | §3.2 |
| V7 | For WebAuthn-ES256: pass message as the pre-hashed challenge to the EIP-7951 precompile | MUST | §3.2 |
| V8 | For WebAuthn-ES256: NOT re-hash the message before precompile invocation | MUST NOT | §3.2 |
| V9 | For ECDSA-secp256k1: pubKey is 64 bytes uncompressed (no 0x04 prefix); recover via ecrecover and compare against keccak256(pubKey)[12:] | MUST | §3.2 |
| V10 | For EIP-1271: take rightmost 20 bytes of pubKey as the contract address | MUST | §3.2 |
| V11 | For EIP-1271: staticcall isValidSignature(bytes32, bytes); return true iff returned 4 bytes equal 0x1626ba7e | MUST | §3.2 |
| V12 | NOT expose any setter that adds, removes, or modifies scheme handlers post-deployment | MUST NOT | §3.4 |
| V13 | EIP-7951 precompile MUST be live on target chain at deployment block (per Fusaka hardfork status for mainnet; per-L2 confirmation required) | MUST | §3.5 |
| V14 | Treat EIP-1271 staticcall as untrusted (gas-bounded, revert-safe) | MUST | §3.5 |
| V15 | Cap EIP-1271 staticcall gas to a documented limit (e.g., 100K) and treat any revert as valid = false | SHOULD | §3.5 |
| V16 | Expose a batched verifyMany entry point | MAY (deferred to v1.1) | §3.5 |