DeFi Intel

EIP-712 Signature Replay: How Cross-Implementation Exploits Work

Quick answerEIP-712 signature replay occurs when a signature valid for one contract or chain is reused on another due to insufficient domain separation. Attackers exploit missing or mismatched `chainId`, `verifyingContract`, or `salt` in the domain separator, or nonce reuse across implementations, enabling unauthorized actions across forks, layers, or contracts.

EIP-712 signature replay is a class of vulnerability where a valid signed message—intended for a specific smart contract or blockchain—is maliciously reused on a different contract, chain, or implementation. This exploit undermines the core promise of typed structured data signing: that a signature binds to a precise context. In practice, attackers leverage incomplete domain separator definitions, shared nonce spaces, or omitted identifiers to replay approvals, orders, or authorizations across Ethereum mainnet, L2s, sidechains, or upgradeable proxy patterns. Understanding these cross-implementation exploits is critical for any developer building with EIP-712-based features like gasless permits, off-chain order books, or cross-chain relayers.

This guide dives deep into the mechanical roots of EIP-712 signature replay, the real-world protocols that fell victim, and the architectural choices that prevent it. We examine the domain separator—the keystone of EIP-712—and show how small omissions or inconsistencies in its construction trigger drastic security gaps. By the end, you will not only recognize replay vectors but also know precisely how to defend against them in your own contracts.

Key takeaways
  • EIP-712 signature replay arises from insufficient domain separation, not cryptographic weakness.
  • The domain separator must include chainId and verifyingContract to prevent cross-chain and cross-contract reuse.
  • OpenSea Wyvern's omission of chainId is the classic real-world example of a replay exploit.
  • Static domain separator hashes become stale after upgrades or chain-id changes; compute dynamically.
  • Permit2's shared domain is by design but forgives cross-implementation replay within the same ecosystem.
  • Always audit upgradeable contracts for replay across implementation versions using salt or version embeddings.

What Is EIP-712 Signature Replay? The Core Mechanism

EIP-712 defines a standard for hashing and signing typed structured data. Its security model relies on a domain separator—a unique identifier that scopes a signature to a particular contract and chain. A replay attack occurs when an attacker takes a signature produced for domain A and submits it to domain B, and domain B accepts it as valid. This is possible only if the two domains produce the same domain separator hash or if the verifying contract does not check domain fields it should.

The fundamental cause is insufficient domain separation. For example, if both contracts omit chainId from their domain separator, a signature made on Ethereum mainnet can be replayed on an L2 fork. Similarly, if the verifyingContract field is absent, a signature intended for one lending protocol can authorize actions in another unrelated protocol—so long as both use the same name and version in their domain. The exploit is not about breaking cryptography; it's about binding the cryptographic signature to an insufficiently unique context.

The Root Cause: Domain Separator Breakdown

The domain separator in EIP-712 is defined as:

EIP712Domain => [ name, version, chainId, verifyingContract, salt ]

Every field is optional, but omitting any of them dramatically increases the replay surface. The most critical fields are:

Many early implementations skipped these fields either for simplicity or to save gas. The OpenSea Wyvern exchange (pre-Seaport) famously omitted chainId early on, enabling cross-chain order replay on forks. Even today, some protocols rely solely on name and version, which are trivially replicated by an attacker deploying a contract with identical string values.

Real-World Exploit: OpenSea Wyvern and Cross-Fork Replay

Omitting chainId from an EIP-712 domain means a signature can be valid on every EVM chain where a contract with the same address exists.

The original OpenSea Wyvern exchange used a domain separator that included name ('Wyvern Exchange') and verifyingContract, but no chainId. This meant any signature made for the Wyvern contract on Ethereum mainnet could be legally replayed on any fork where a contract with the same address existed—such as an Ethereum Classic replica or a testnet. Attackers could take a user's order signed on mainnet and execute it on a lower-value fork, acquiring assets at the agreed price while the user's real order remained unexecuted. OpenSea later patched by adding chainId to the domain and forcing re-signing. This incident serves as the canonical example of why chainId is non-negotiable in production.

Similar vulnerabilities have been found in early versions of 0x and Rarible, where domain separators were not anchored to a unique chain identifier.

Cross-Chain Replay: Forks, L2s, and the ChainId Omission

Even when chainId is included, cross-chain replay is possible if the chainId changes after deployment. This is the scenario a hard fork creates: if a chain splits or otherwise changes its chainId, a contract that stored a cached domain separator hash at construction does not retroactively change it—signatures computed with the old hash remain valid after the chainId changes. EIP-712 recommends computing the domain separator dynamically on each call to avoid this stale-hash issue.

Additionally, sidechains that run EVM but have the same chainId as Ethereum (e.g., early L2 testnets) can cause copies. Always verify that the chainId you use is globally unique. The EIP-155 chainId registry is maintained by the Ethereum community, but custom rollups may accidentally collide.

Cross-Contract Replay: Shared Nonce and VerifyingContract Absence

Cross-contract replay happens when two different smart contracts accept the same typed signature message because they either:

Consider two ERC-20 permit contracts (EIP-2612) from different projects. If both use the same domain separator fields but omit verifyingContract, a permit signed for token A can be replayed on token B, granting the attacker the right to use the signature's allowance on token B. This is especially dangerous when both tokens are held by the same user. To mitigate, EIP-2612 mandates including the token's own address as the verifyingContract in the domain separator.

Nonce reuse amplifies this: if a permit contract uses a global nonce per user (not per domain), signing two permits for different versions of the same contract can cause one to invalidate the other. Worse, if the nonce is not hashed together with the domain, an attacker could craft a permit that uses the same nonce but a different domain, creating a collision.

Comparison Table: Domain Separator Fields Across Protocols

ProtocolnameversionchainIdverifyingContractsaltKnown Replay Fix
OpenSea Wyvern (old)Wyvern Exchange1.0Added chainId
Uniswap V3 Permit2Permit21Uses nonce per token + owner
OpenSea SeaportSeaport1.5Domain includes contract
0x V4 (old)0x Protocol2.0Later added chainId
EIP-2612 (standard)(token name)1✅ (token addr)verifyingContract = token
Gnosis Safe (relay)Gnosis Safe1
(optional)
Separator computed dynamically

The Permit2 Dilemma: Shared Typed Signatures Across Implementations

Uniswap Permit2 introduces a universal nonce and approval system that allows users to sign a single permit and use it across multiple routers. However, it also introduces a new replay risk: if two different protocols both adopt Permit2, they share the same domain separator (since Permit2's domain is fixed). An attacker could replay a permit meant for Uniswap on another DEX that also uses Permit2 if the recipient or spender addresses overlap. This is intentional by design—Permit2 is built to be reusable—but it means the signature's authorization window is wider than the user may expect.

More concerning is cross-chain Permit2 replay. Since Permit2 uses chainId, a different chain should prevent replay. But if a chain fork shares the same chainId (e.g., both call themselves chainId 1), permits are replayable. Always ensure your chainId is unique. Permit2 also uses a salt of zero, meaning two deployments with the same address on different chains (impossible by address derivation) would not be separated.

Mitigation Strategies: Building Replay-Proof EIP-712 Systems

Preventing EIP-712 signature replay requires following the standard's intent, not just its letter. Key strategies include:

Advanced: Replay Across Upgradeable Contracts and Beacon Proxies

Upgradeable contracts introduce a subtle replay vector: if the implementation changes but the domain separator points only to the proxy address (the verifyingContract), the domain remains the same even though the logic has changed. A signature that was valid for an old implementation may still be valid for the new one, unless the new implementation explicitly checks a version or salt. Use the salt field of the domain separator to embed an implementation version number. When the implementation is upgraded, change the salt so that all old signatures become invalid.

Beacon proxies add another layer: if the beacon's address is used as verifyingContract, the signature binds to the beacon, not to any specific implementation. This can be desirable for universal permits, but it also means a signature is valid for any future implementation. Balance between flexibility and security by hashing the implementation address inside the message rather than the domain.

Tools like OpenZeppelin Defender and EIP-2535 Diamonds also face similar challenges. Always audit the lifecycle of the verifyingContract and whether old signers can influence new logic.

Conclusion and Future Directions

EIP-712 signature replay, while preventable, remains a recurring vulnerability in the DeFi ecosystem. The root cause is always insufficient domain separation—whether through omission of chainId, verifyingContract, or stale implementation references. As cross-chain and cross-application interoperability grows (ERC-4337 account abstraction, CCIP, layer-zero), the replay surface expands. Future EIPs may standardize a universal domain separator that includes a global chain namespace, but until then, developers must be meticulous.

Defending against replay is not an optional optimization; it is a fundamental security requirement. Use the comparison table above as a checklist. Always think: 'If this signature were wrong but valid on a different chain or contract, what would happen?' The answer should be 'rejected'.

Common mistakes to avoid

Frequently asked questions

Can EIP-712 signature replay be prevented without increasing gas costs?

Yes: the domain separator is hashed once and cached, so including chainId and verifyingContract has negligible gas overhead. The replay protection is free after the initial hash.

How does EIP-2612 relate to EIP-712 replay vulnerabilities?

EIP-2612 (permit) is built on EIP-712. Early implementations sometimes omitted verifyingContract, leading to cross-token replay. The standard now mandates including the token address as verifyingContract to prevent that.

What is the difference between cross-chain and cross-contract replay?

Cross-chain replay reuses a signature on a different blockchain (e.g., Ethereum → Polygon) due to missing chainId. Cross-contract replay reuses it on a different contract on the same chain due to missing verifyingContract or identical domain identifiers.

Does EIP-712 still have replay issues if I include all domain fields?

Even with all fields, a change in any field (e.g., chainId after a hard fork) invalidates signatures only if the domain separator is recomputed dynamically. Also, upgradeable contracts require careful use of salt or version to prevent old signatures from working on new implementations.

How does ERC-4337 affect EIP-712 replay?

ERC-4337 user operations use EIP-712 signatures. Since account abstraction allows signature verification from different entry points, domain separation between entry points is critical. Similarly, cross-chain user ops via ERC-4337 require unique chainId and entry point address in the domain.

Track the entities behind the concepts

DeFi Intel maps 11,000+ protocols, tokens and companies to a typed knowledge graph — with live data, incidents and regulation.

Entities mentioned