A Solidity implementation of EIP-7913, enabling signature verification for address-less cryptographic keys.
Externally Owned Accounts (EOA) can sign messages with their associated private keys. Additionally ERC-1271 defines a method for signature verification by smart accounts such as multisig. In both cases the identity of the signer is an ethereum address.
This standard extends the concept of signer description and signature verification to keys that do not have an ethereum identity of their own - they don't have their own address to represent them. This new mechanism can be used to integrate new signers such as:
- Non-ethereum cryptographic curves (P256/secp256r1, RSA, Ed25519, etc.)
- Hardware devices
- Email addresses
- ZK-based authentication
This is particularly relevant when dealing with account abstraction and social recovery of smart accounts.
With the development of account abstraction, there is an increasing need for non-ethereum signature verification. Cryptographic algorithms besides the natively supported secp256k1 are being used for controlling smart accounts. In particular:
- secp256r1/P-256: Supported by many mobile devices (like Apple's Secure Enclave and Android's StrongBox)
- RSA keys: Distributed by traditional institutions
- ZK solutions: For signing with emails or JWT from Web2 services
All these signature mechanisms have one thing in common: they do not have a canonical ethereum address to represent them onchain. While users could deploy ERC-1271 compatible contracts for each key individually, this would be cumbersome and expensive.
As account abstraction tries to separate account addresses (that hold assets) from the keys that control them, giving fixed on-chain addresses to keys is not the right approach. Instead, using a small number of verifier contracts that can process signatures in a standard way, and having accounts rely on these verifiers, is the correct approach.
Once the verifier is deployed, any key can be represented using a (verifier, key) pair without requiring any setup cost. The (verifier, key) pairs can be given permission to control a smart account, perform social recovery, or do any other operation without ever having a dedicated on-chain address.
This definition is backward compatible with EOA and ERC-1271 contracts: in that case, we use the address of the identity (EOA or contract) as the verifier and the key is empty.
- ERC-7913 Interface: Core interface for signature verifiers
- P256 Verifier: secp256r1/P-256 signature verification using RIP-7212 precompile
- RSA Verifier: RSA signature verification with PKCS#1 v1.5 padding
- Extended Signature Checker: Unified library supporting EOA, ERC-1271, and ERC-7913 signatures
- Smart Account Example: Reference implementation of a smart account using ERC-7913 signers
- Foundry installed
Clone the repository and install dependencies:
cd ERC-7913
forge installBuild all smart contracts:
forge buildExecute the complete test suite:
forge testExpected output:
- 6 tests in
P256VerifierTest - 13 tests in
SignatureCheckerExtendedTest - Total: 19 tests passed
For verbose output showing logs and traces:
forge test -vvvUse different verbosity levels:
-v: Show failed test details-vv: Show test names-vvv: Show test execution traces-vvvv: Show execution traces and setup-vvvvv: Show execution and setup with Stack traces
Test only the P256 verifier:
forge test --match-path test/P256Verifier.t.solTest only the SignatureChecker library:
forge test --match-path test/SignatureCheckerExtended.t.solRun a single test by name:
forge test --match-test testERC7913ValidSignatureRun tests matching a pattern:
forge test --match-test "testERC7913*"Generate a coverage report:
forge coverageERC-7913/
├── src/
│ ├── interfaces/
│ │ └── IERC7913SignatureVerifier.sol # EIP-7913 interface
│ ├── verifiers/
│ │ ├── P256Verifier.sol # P256/secp256r1 verifier
│ │ └── RSAVerifier.sol # RSA verifier
│ ├── libraries/
│ │ └── SignatureCheckerExtended.sol # Extended signature checker
│ └── examples/
│ └── SmartAccountWithERC7913.sol # Smart account example
├── test/
│ ├── P256Verifier.t.sol # P256 verifier tests
│ └── SignatureCheckerExtended.t.sol # Library tests
├── lib/ # Dependencies (OpenZeppelin, Forge)
├── foundry.toml # Foundry configuration
└── README.md
import {P256Verifier} from "./verifiers/P256Verifier.sol";
import {IERC7913SignatureVerifier} from "./interfaces/IERC7913SignatureVerifier.sol";
// Deploy verifier
P256Verifier verifier = new P256Verifier();
// Prepare inputs
bytes memory publicKey = ...; // 64 bytes: x || y coordinates
bytes32 hash = keccak256("message");
bytes memory signature = ...; // 64 bytes: r || s values
// Verify signature
bytes4 result = verifier.verify(publicKey, hash, signature);
bool isValid = (result == IERC7913SignatureVerifier.verify.selector);import {SignatureCheckerExtended} from "./libraries/SignatureCheckerExtended.sol";
// For EOA (20 bytes)
bytes memory signer = abi.encodePacked(address(0x123...));
// For ERC-7913 verifier (>20 bytes: verifier address || key)
bytes memory signer = abi.encodePacked(verifierAddress, publicKey);
// Verify signature
bool isValid = SignatureCheckerExtended.isValidSignatureNow(
signer,
hash,
signature
);The SignatureCheckerExtended library supports three formats:
| Format | Length | Structure | Description |
|---|---|---|---|
| EOA | 20 bytes | address |
Standard Ethereum address |
| ERC-1271 | 20 bytes | address |
Contract implementing ERC-1271 |
| ERC-7913 | >20 bytes | verifier_address (20) || key (variable) |
Verifier contract + public key |
The verifier interface follows EIP-7913:
interface IERC7913SignatureVerifier {
/**
* @dev Verifies `signature` as a valid signature of `hash` by `key`.
*
* MUST return the bytes4 magic value 0x024ad318 (IERC7913SignatureVerifier.verify.selector)
* if the signature is valid.
*
* SHOULD return 0xffffffff or revert if the signature is not valid.
* SHOULD return 0xffffffff or revert if the key is empty.
*/
function verify(
bytes calldata key,
bytes32 hash,
bytes calldata signature
) external view returns (bytes4);
}Return values:
0x024ad318- Signature is valid (equalsverify.selector)0xffffffff- Signature is invalid- Reverts - Invalid input or verification failure
| Scheme | Status | Precompile | Key Format | Signature Format |
|---|---|---|---|---|
| P256 (secp256r1) | Implemented | RIP-7212 (0x100) | 64 bytes (x||y) | 64 bytes (r||s) |
| RSA | Implemented | ModExp (0x05) | Variable | Variable |
| Ed25519 | Planned | - | - | - |
| BLS12-381 | Planned | - | - | - |
-
P256 Precompile Dependency: The P256Verifier requires the RIP-7212 precompile at address
0x100. It will revert if the precompile is not available in your environment. -
RSA Gas Costs: RSA verification is gas-intensive due to modular exponentiation. Always consider gas limits for on-chain usage.
-
Input Validation: Always validate key formats and signature lengths before verification to avoid unexpected behavior.
-
Production Readiness: This implementation is for demonstration and testing purposes. Conduct a thorough security audit before using in production.
-
Key Management: Ensure proper key generation and storage practices for all cryptographic schemes.
Approximate gas costs for signature verification:
| Operation | Gas Cost |
|---|---|
| P256 verification (with precompile) | ~3,000-4,000 |
| P256 verification (without precompile) | Not implemented |
| RSA verification (2048-bit) | ~200,000-300,000 |
| EOA signature check | ~3,000-4,000 |
┌─────────────────────────┐
│ Signature + Signer │
└───────────┬─────────────┘
│
▼
┌───────────────┐
│ Signer Length?│
└───┬───────┬───┘
│ │
20 bytes >20 bytes
│ │
▼ ▼
┌─────┐ ┌──────────────────┐
│ EOA │ │ Extract verifier │
│ or │ │ & key from │
│1271 │ │ signer bytes │
└──┬──┘ └────────┬─────────┘
│ │
▼ ▼
┌──────────┐ ┌────────────────┐
│SignChecker│ │ Call verifier │
│.isValid.. │ │ .verify() │
└─────┬────┘ └────────┬───────┘
│ │
▼ ▼
┌──────────────────────┐
│ Return true/false │
└──────────────────────┘
Error: P256 precompile not available
Solution: The RIP-7212 precompile may not be deployed in your test environment. This is expected in standard Foundry tests. The precompile is available on certain networks that support RIP-7212.
Function state mutability warnings can be safely ignored - they suggest optimization opportunities but don't affect functionality.
Contributions are welcome! Areas for improvement:
- Additional cryptographic scheme implementations (Ed25519, BLS, etc.)
- Gas optimization for existing verifiers
- Comprehensive real-world test vectors
- Library fallback implementations for missing precompiles
- Enhanced documentation and examples
MIT License
- EIP-7913: Signature Verifiers
- RIP-7212: Precompile for secp256r1 Curve Support
- ERC-1271: Standard Signature Validation Method for Contracts
- OpenZeppelin Contracts
Implementation based on EIP-7913 by: