Standard / EIP·EVM
EIP-1271 — Standard Signature Validation for Contracts
Interface for smart contracts to validate signatures: `isValidSignature(bytes32 hash, bytes signature) returns (bytes4)` MUST return the magic value `0x1626ba7e` on success. Status: Final (Standards Track / ERC). Required for any protocol that wants to accept signatures from multisigs, smart accounts (ERC-4337), or AA wallets — `ecrecover` alone breaks for them.
- 01accepting signatures from Safe / multisigs
- 02ERC-4337 smart-account login flows
- 03off-chain orders signed by contract wallets
- 04Permit2 / EIP-2612 with smart accounts
- 05SIWE for AA wallets
- pnpm add viem # verifyMessage / verifyTypedData auto-detect 1271 (EIP-6492 wrapped too)
- pnpm add ethers # provider.verifyMessage / Wallet utilities
- # Reference: OpenZeppelin SignatureChecker.isValidSignatureNow
Use EIP-1271 to verify signatures from contract wallets. Implementer side: `function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4) { return _check(hash, signature) ? 0x1626ba7e : 0xffffffff; }`. Verifier side, ALWAYS use the `(hash, sig) -> bytes4` form (the legacy `(bytes, bytes)` 0x20c13b0b variant is deprecated — most modern code only ships the bytes32 version). Use OpenZeppelin's `SignatureChecker.isValidSignatureNow(signer, hash, sig)` which transparently falls back from `ecrecover` to a 1271 staticcall when `signer.code.length > 0`. With viem, `verifyMessage` and `verifyTypedData` automatically perform the 1271 staticcall and additionally unwrap EIP-6492 counterfactual signatures. For ERC-4337 accounts, `validateUserOp` returns a packed validation result; the EOA-facing `isValidSignature` must hash the message per EIP-712 / EIP-191 rules expected by callers (Permit2, Seaport, etc.).
- ⚑Magic value is `0x1626ba7e` (selector of `isValidSignature(bytes32,bytes)`). Returning `true`/`false` or any other bytes4 fails verification — and many implementers accidentally return `bytes4(0)` on failure which is a valid wallet response per spec but breaks naive `== 0x1626ba7e ? ok : revert` checks if you also need a no-revert path.
- ⚑The legacy `isValidSignature(bytes, bytes)` selector `0x20c13b0b` is deprecated; some old contracts (early Argent, early Gnosis) only implement the legacy form. Use `SignatureChecker` which tries both, or feature-detect.
- ⚑Counterfactual contract wallets (not yet deployed) cannot answer the staticcall — the call reverts. Wrap the signature with EIP-6492 so the verifier deploys-then-checks via the universal validator.
- ⚑1271 is `view` but the verifier executes the wallet's logic — a malicious wallet can read calldata, gas-bomb, or return inconsistent results across blocks. Cap gas on the staticcall and treat the result as advisory (not authoritative across time).
- ⚑The `hash` passed in must be the FINAL digest the wallet expects — for EIP-712 callers the digest is `0x1901 || domain || hashStruct`, for `personal_sign` it is `hashMessage(msg)`. Mismatched hashing is the most common 1271 bug.