A wallet address can belong to a person holding a private key, or to a smart contract that decides whether an action is authorized. Those two cases cannot be verified in exactly the same way. An externally owned account can produce an ECDSA signature that software checks with elliptic-curve recovery. A contract may instead require several owners, a session key, a spending policy or an upgradable validation module.

ERC-1271 gives applications one standard question to ask a contract: “Do you accept this signature for this message hash?” It does not prescribe the contract’s policy. It standardizes the call and the successful response so wallets, marketplaces and other applications do not need a different integration for every contract account.

The interface is deliberately small

The standard defines a function with this shape:

function isValidSignature(
    bytes32 hash,
    bytes memory signature
) external view returns (bytes4 magicValue);

A caller supplies the hash that was signed and the signature bytes. A valid result is the four-byte value 0x1626ba7e, which is the selector of isValidSignature(bytes32,bytes). Any other value, or a reverted call, means the caller should not treat the signature as valid.

That magic value matters. A simple Boolean could be returned accidentally by an unrelated function or decoded from unexpected data. The selector ties success to the exact validation interface. The contract still decides what the signature bytes mean: one owner’s ECDSA signature, a bundle of approvals, a delegated key or another scheme.

The function is marked view. Verification should inspect the contract’s current state without changing it. A caller also has to allow an external contract call; ecrecover alone cannot validate a contract wallet.

Why ordinary signature recovery is not enough

ECDSA recovery answers a narrow question: which public-key address produced a signature over a particular digest? That works for an externally owned account because control of the private key defines the account’s signing authority.

A contract address has no private key of its own. Its authority lives in code and state. A multisignature wallet may require two of three owners. A modular account may accept a temporary session key only for a limited action. A recovery process may replace one signer. None of those rules can be inferred from the contract address by recovering a single key.

ERC-1271 moves the final decision to the contract that owns the policy. The application prepares the same message hash it expects the user to authorize, then asks the account contract to validate the supplied proof.

This distinction also affects products that support both account types. A robust verifier first determines whether the claimed signer has deployed code. It can use ordinary ECDSA recovery for an externally owned account and the ERC-1271 call for a contract account. OpenZeppelin’s SignatureChecker utility packages that split into one interface.

A valid contract signature can change later

An ECDSA signature from a fixed key remains mathematically recoverable. Contract validation depends on live state. A multisig owner can be removed. A session key can expire. A module can be disabled. An upgradable account may replace its validation logic.

OpenZeppelin therefore warns that a contract signature can be valid at one block and invalid at another. This is not a flaw in ERC-1271; it follows from giving a programmable account control over its authorization policy.

The practical consequence is to define when validity matters. An on-chain transaction checks the contract at execution. An off-chain order may be accepted now and filled later, so the protocol needs rules for expiry, cancellation and revalidation. A historical audit should record the chain, contract address, block reference, message and validation result rather than assuming a fresh call reproduces an earlier decision.

The hash must carry the real intent

ERC-1271 does not define the message format. It only receives a bytes32 hash and signature bytes. The application remains responsible for hashing the correct data with an unambiguous domain.

ERC-191 defines a family of signed-data formats that begin with 0x19, followed by a version byte and version-specific data. Its purpose is to distinguish signed data from ordinary Ethereum transactions and to support formats that bind a message to a particular validator.

Many modern applications use typed structured data so a signature can include fields such as the chain ID, verifying contract, action, amount, nonce and deadline. Whatever format is chosen, the verifier and signer must build the same digest. If the interface displays “approve 10 units” but the hash also authorizes another contract, chain or recipient, a technically valid signature may approve the wrong intent.

A useful review starts with the full message schema:

  • Domain: which chain and verifying contract can use the signature?
  • Action: what function or operation is authorized?
  • Scope: which asset, amount, recipient or resource is covered?
  • Freshness: what nonce, sequence number or deadline prevents replay?
  • Signer: is the claimed authority an externally owned account or a contract account?

The signature check belongs after those fields are reconstructed and validated, not before.

Validation belongs close to execution

An application can call isValidSignature off-chain through an RPC node, on-chain from another contract or both. Each placement answers a slightly different operational need.

An off-chain check can reject an invalid order before it enters a queue. It is fast and saves fees, but it is only a snapshot of the state at the block the node used. If the order will later execute on-chain, the executing contract should validate the signature again under the state and domain rules that actually govern settlement.

An on-chain verifier should handle failure conservatively. A revert, an unexpected return length or a value other than the magic value is not approval. The caller also needs a gas strategy because it is invoking external contract code. Validation should not be allowed to re-enter sensitive execution paths or turn an unbounded call into a denial-of-service route.

Common integration mistakes

The first mistake is applying ecrecover to every signer. That silently excludes smart contract wallets or encourages a special, weaker bypass for them.

The second is treating the contract call as timeless. If fulfillment is delayed, recheck at the point where authorization becomes consequential. Store enough context to explain which block and contract state produced an earlier result.

The third is hashing a message without a domain, nonce or deadline. ERC-1271 can correctly approve that hash while the surrounding protocol still permits replay in another place or at another time.

The fourth is accepting any successful call. The contract must return the exact ERC-1271 magic value. “The call did not revert” is not the same as “the signature is valid.”

The fifth is assuming the standard makes every contract wallet safe. ERC-1271 only supplies interoperability. The account’s own code, upgrade controls, owner management and modules determine the quality of its authorization policy.

For related authorization boundaries, see ERC-20 token allowances and signed software updates. Both show the same design lesson: a cryptographic proof is useful only when the verifier binds it to the right identity, scope and moment.

Documentation checked: ERC-1271, ERC-191 and OpenZeppelin Contracts 5.x SignatureChecker documentation, October 31, 2026.