Skip to main content

ContractSignature

EIP-1271 contract signature verification primitive. Enables verification of signatures from smart contract wallets using the isValidSignature standard interface.

Overview

EIP-1271 allows smart contracts to validate signatures, enabling:
  • Smart contract wallets (Gnosis Safe, Argent, etc.)
  • Multi-signature schemes
  • Custom signature validation logic
  • Account abstraction

Installation

npm install @tevm/primitives

Basic Usage

Verify Contract Signature

import * as ContractSignature from '@tevm/primitives/ContractSignature';

// Check if a contract signature is valid
const isValid = await ContractSignature.isValidSignature(
  provider,
  contractAddress,
  messageHash,
  signature
);

Unified EOA + Contract Verification

import * as ContractSignature from '@tevm/primitives/ContractSignature';
import { hash as keccak256 } from '@tevm/crypto/keccak256';
import { recoverPublicKey } from '@tevm/crypto/Secp256k1';
import { fromPublicKey } from '@tevm/primitives/Address';

// Create unified verifier
const verifySignature = ContractSignature.VerifySignature({
  keccak256,
  recoverPublicKey,
  addressFromPublicKey: fromPublicKey
});

// Automatically detects EOA vs contract and uses correct method
const isValid = await verifySignature(
  provider,
  signerAddress,  // Can be EOA or contract
  messageHash,
  signature
);

API Reference

Functions

isValidSignature(provider, contractAddress, hash, signature)

Verify signature using EIP-1271 contract method. Parameters:
  • provider: JSON-RPC provider with request method
  • contractAddress: Contract address (string or Uint8Array)
  • hash: Message hash (32 bytes)
  • signature: Signature bytes
Returns: Promise<boolean> - True if signature is valid Example:
const isValid = await ContractSignature.isValidSignature(
  provider,
  '0x742d35cc6634c0532925a3b844bc9e7595f251e3',
  messageHash,
  signatureBytes
);

VerifySignature({ keccak256, recoverPublicKey, addressFromPublicKey })

Factory function that creates a unified signature verifier for both EOA and contract accounts. Parameters:
  • keccak256: Keccak256 hash function
  • recoverPublicKey: secp256k1 public key recovery function
  • addressFromPublicKey: Address derivation function
Returns: Async function that verifies signatures Example:
const verifySignature = ContractSignature.VerifySignature({
  keccak256,
  recoverPublicKey,
  addressFromPublicKey: fromPublicKey
});

// Works for both EOA and contracts
const isValidEOA = await verifySignature(
  provider,
  eoaAddress,
  messageHash,
  signature
);

const isValidContract = await verifySignature(
  provider,
  contractAddress,
  messageHash,
  signature
);

Constants

EIP1271_MAGIC_VALUE = "0x1626ba7e";
IS_VALID_SIGNATURE_SELECTOR = "0x1626ba7e";
The magic value 0x1626ba7e is bytes4(keccak256("isValidSignature(bytes32,bytes)")).

How It Works

Detection Logic

VerifySignature automatically detects the account type:
  1. Calls eth_getCode to check if address has contract code
  2. If no code (0x): Uses EOA verification (ecrecover)
  3. If has code: Uses EIP-1271 contract verification

Contract Interface

EIP-1271 contracts must implement:
interface IERC1271 {
  function isValidSignature(
    bytes32 _hash,
    bytes memory _signature
  ) external view returns (bytes4 magicValue);
}
Valid signatures return 0x1626ba7e, invalid signatures return anything else.

Usage Examples

With Gnosis Safe

import * as ContractSignature from '@tevm/primitives/ContractSignature';

const safeAddress = '0x...'; // Gnosis Safe contract
const messageHash = new Uint8Array(32); // Message hash
const signature = new Uint8Array(65); // Signature from Safe

const isValid = await ContractSignature.isValidSignature(
  provider,
  safeAddress,
  messageHash,
  signature
);

With Personal Message

import * as SignedData from '@tevm/primitives/SignedData';
import * as ContractSignature from '@tevm/primitives/ContractSignature';

// Hash personal message (EIP-191)
const hash = SignedData.Hash({ keccak256 });
const messageHash = hash("Hello, Ethereum!");

// Verify with automatic EOA/contract detection
const verifySignature = ContractSignature.VerifySignature({
  keccak256,
  recoverPublicKey,
  addressFromPublicKey: fromPublicKey
});

const isValid = await verifySignature(
  provider,
  signerAddress,
  messageHash,
  signature
);

Custom Provider

const customProvider = {
  request: async (method, params) => {
    // Custom JSON-RPC implementation
    const response = await fetch('https://eth-mainnet.g.alchemy.com/v2/...', {
      method: 'POST',
      body: JSON.stringify({ method, params })
    });
    return response.json();
  }
};

const isValid = await ContractSignature.isValidSignature(
  customProvider,
  contractAddress,
  messageHash,
  signature
);

Error Handling

import {
  ContractSignatureError,
  ContractCallError
} from '@tevm/primitives/ContractSignature';

try {
  const isValid = await ContractSignature.isValidSignature(
    provider,
    contractAddress,
    messageHash,
    signature
  );
} catch (error) {
  if (error instanceof ContractCallError) {
    console.error("Failed to call contract:", error.message);
  }
}

Smart Contract Example

Example EIP-1271 implementation:
contract MyWallet {
  address public owner;

  function isValidSignature(
    bytes32 _hash,
    bytes memory _signature
  ) external view returns (bytes4) {
    // Recover signer from signature
    address signer = ECDSA.recover(_hash, _signature);

    // Check if signer is owner
    if (signer == owner) {
      return 0x1626ba7e; // Magic value for valid signature
    }

    return 0xffffffff; // Invalid signature
  }
}

See Also

  • Address - Ethereum address handling
  • Abi - ABI encoding for contract calls