Preventing Replay Attacks Post Ethereum Merge

March 1, 2024
Quantstamp Labs

Note: This advice is intended primarily for users interacting with both PoW and PoS chains. For those who intend to operate only on one chain and are not interested in assets on the other, replay attacks are not a concern.

The Importance of Chain ID

When we transact on an Ethereum network, we create transactions, sign them with our private keys to confirm the authenticity of the transaction, and broadcast the transaction to the network for inclusion into a block. The transaction we sign encodes several pieces of information, one of which is a so-called “chain ID”. 

The chain ID serves two important purposes: first, it indicates which chain the transaction is meant for. For example, the Ethereum mainnet has chain ID 1, Ropsten has chain ID 3, and Polygon has chain ID 137. The existence of the chain ID allows the network participants to recognize whether a broadcasted message (such as a signed transaction) is meant to be processed by them, or whether it can be dropped and ignored.

The other purpose of the chain ID, which is arguably more important, is to prevent so-called “replay attacks”. Many of the chains in the Ethereum ecosystem have exactly the same transaction format. Without chain ID being part of the transaction payload needing to be signed by the private key, a transaction—for example, a transfer of 1 ETH from Account A to Account B—would be identical on Ethereum mainnet, Ropsten, Polygon, and other networks. If Account A signed the transaction and broadcasted it on Ropsten, anyone could take it, broadcast it on mainnet, and it would appear valid. By adding chain ID into the payload, this is no longer possible; instead, mainnet miners would get a signed transaction with a chain ID that doesn’t match the network, and would then drop the transaction and not include it in the block. 

Two Chains, One Chain ID

So why do we care in the context of the merge? When the merge happens, some miners will update their nodes to the Ethereum 2.0 clients, and begin operating based on the PoS consensus. However, some miners will not, and will continue to operate the current Ethereum’s PoW chain. The PoS network will continue with chain ID 1. The miners who don’t update their nodes and continue operating will continue with chain ID 1 as well. Until these miners coordinate and choose a new chain ID, replay attacks are a possibility; transactions signed and submitted to the PoS and PoW chains will be identical, and can be executed on both chains. 

This can have many consequences that the networks’ users might not be aware of, or that they may not consider when signing transactions. This can include approving NFTs for trades, approving ERC20 tokens, executing trades on DEXes, and essentially any transaction. 

Consider a trade of $1M in ETH for DAI on Uniswap submitted on the PoS network. An attacker could observe the transaction, submit the same trade to the PoW network as a sandwiched trade, and extract all the available value. This value could be significant since ETH price differs between these chains and any slippage protection parameters built into Uniswap transactions may be too loose. Keep in mind that attackers cannot freely withdraw assets from user accounts following the merge without the users themselves creating suitable conditions for the attackers.

This issue exists at the protocol level, regardless of whether the account’s private keys are managed by a hot wallet (such as MetaMask), a hardware wallet, or a custody provider that uses a multi-party computation (MPC) scheme for signing transactions.

Avoiding Unintended Replays

So how can we protect against unintended replays? We must ensure that transactions signed on one chain (PoW or PoS) will naturally fail if replayed on the other chain. 

The most effective approach is to move all assets on both chains to new accounts dedicated to those chains. For example, if Account A transfers funds to Account B and C on the PoW and PoS chains respectively, a replay of a transaction signed by B on the PoS account will fail, because those assets are not present in Account B on the PoS chain. 

Note that the transfer needs to occur on both the PoW and PoS chains. If it occurred on only one chain, an attacker could replay the transfer on the other chain, and execute the attack exactly the same way. Also, while funds are actively being transferred to a new account on a specific chain—for example, PoW to Account B—an attacker could step in and transfer them using a replay attack to the same account on the other chain. 

If Account B is an externally owned account (EOA, i.e., not a smart contract such as a multisig wallet), this is not more than a temporary nuisance, as one would end up with the funds in the same account on both chains, and could simply try again. However, users should verify the transfers to B and C were successful before further use of these accounts. 

If B is a smart contract (such as a multisig wallet), one needs to exercise caution and ensure that B is present on both the PoW and PoS chains before transferring funds into it, and as always, we recommend further education on this topic before taking any action.

Why Nonce Divergence Doesn’t Fix the Problem

Some savvy users may suggest that achieving nonce divergence for an account on the two chains solves the problem and could be a sufficient fix, but we strongly urge that it is not. To understand why, it’s important to understand what a nonce is. The Ethereum protocol stipulates that transactions sent to a network by an account are sequentially numbered. This number in the sequence is called the nonce, and it is also part of the transaction payload that needs to be signed. The protocol stipulates that the very first transaction sent by an account has nonce 0, and then every transaction increments the nonce by 1, i.e., there can be no gaps.

The argument for nonce divergence assumes that if one chain advances the nonce for an account, the other chain will be behind in the transaction sequence, and therefore, the attempt to replay transactions would fail because of the gap in the nonces. While this is correct, it will hold only so long as the divergence in nonces exists. Specifically, if the attacker is able to execute transactions on the other chain and make the nonces of the account match, replays would be possible again. Similarly, if the account owner forgets that the divergence of nonces is needed and accidentally transacts on the other chain, replays may become feasible again.

Consequently, using separate wallets for networks having the same chain ID is the only effective technique for avoiding replay attacks.

Replay Attacks and Rollups

An interesting situation occurs for assets present on rollups, and bridged to other chains. Both rollups and bridges employ custodian smart contracts on the Ethereum network, which hold either the proof of state for the rollup, and/or the assets bridged to the rollup or the other chain. 

During the merge, these contracts will become duplicated on both the PoW and PoS chains as well. The implementation of the specific rollup and a bridge determines whether the updates to the state of these contracts can be performed by anyone, or only by privileged accounts, and what form these updates should have. In any case, these updates can be subjected to the same replay attacks just as any other transactions, which can result in unexpected situations. 

The safest approach to protect assets on other chains and rollups is to exit back to the Ethereum mainnet. In some cases, this may not be necessary, and as the implementations of these systems are complex and vary greatly, there is no one rule that fits every situation. For specific advice, we recommend reaching out to the projects’ official channels to ask about the post-merge situation and the potential for replay attacks.

Quantstamp Labs
September 12, 2022

Note: This advice is intended primarily for users interacting with both PoW and PoS chains. For those who intend to operate only on one chain and are not interested in assets on the other, replay attacks are not a concern.

The Importance of Chain ID

When we transact on an Ethereum network, we create transactions, sign them with our private keys to confirm the authenticity of the transaction, and broadcast the transaction to the network for inclusion into a block. The transaction we sign encodes several pieces of information, one of which is a so-called “chain ID”. 

The chain ID serves two important purposes: first, it indicates which chain the transaction is meant for. For example, the Ethereum mainnet has chain ID 1, Ropsten has chain ID 3, and Polygon has chain ID 137. The existence of the chain ID allows the network participants to recognize whether a broadcasted message (such as a signed transaction) is meant to be processed by them, or whether it can be dropped and ignored.

The other purpose of the chain ID, which is arguably more important, is to prevent so-called “replay attacks”. Many of the chains in the Ethereum ecosystem have exactly the same transaction format. Without chain ID being part of the transaction payload needing to be signed by the private key, a transaction—for example, a transfer of 1 ETH from Account A to Account B—would be identical on Ethereum mainnet, Ropsten, Polygon, and other networks. If Account A signed the transaction and broadcasted it on Ropsten, anyone could take it, broadcast it on mainnet, and it would appear valid. By adding chain ID into the payload, this is no longer possible; instead, mainnet miners would get a signed transaction with a chain ID that doesn’t match the network, and would then drop the transaction and not include it in the block. 

Two Chains, One Chain ID

So why do we care in the context of the merge? When the merge happens, some miners will update their nodes to the Ethereum 2.0 clients, and begin operating based on the PoS consensus. However, some miners will not, and will continue to operate the current Ethereum’s PoW chain. The PoS network will continue with chain ID 1. The miners who don’t update their nodes and continue operating will continue with chain ID 1 as well. Until these miners coordinate and choose a new chain ID, replay attacks are a possibility; transactions signed and submitted to the PoS and PoW chains will be identical, and can be executed on both chains. 

This can have many consequences that the networks’ users might not be aware of, or that they may not consider when signing transactions. This can include approving NFTs for trades, approving ERC20 tokens, executing trades on DEXes, and essentially any transaction. 

Consider a trade of $1M in ETH for DAI on Uniswap submitted on the PoS network. An attacker could observe the transaction, submit the same trade to the PoW network as a sandwiched trade, and extract all the available value. This value could be significant since ETH price differs between these chains and any slippage protection parameters built into Uniswap transactions may be too loose. Keep in mind that attackers cannot freely withdraw assets from user accounts following the merge without the users themselves creating suitable conditions for the attackers.

This issue exists at the protocol level, regardless of whether the account’s private keys are managed by a hot wallet (such as MetaMask), a hardware wallet, or a custody provider that uses a multi-party computation (MPC) scheme for signing transactions.

Avoiding Unintended Replays

So how can we protect against unintended replays? We must ensure that transactions signed on one chain (PoW or PoS) will naturally fail if replayed on the other chain. 

The most effective approach is to move all assets on both chains to new accounts dedicated to those chains. For example, if Account A transfers funds to Account B and C on the PoW and PoS chains respectively, a replay of a transaction signed by B on the PoS account will fail, because those assets are not present in Account B on the PoS chain. 

Note that the transfer needs to occur on both the PoW and PoS chains. If it occurred on only one chain, an attacker could replay the transfer on the other chain, and execute the attack exactly the same way. Also, while funds are actively being transferred to a new account on a specific chain—for example, PoW to Account B—an attacker could step in and transfer them using a replay attack to the same account on the other chain. 

If Account B is an externally owned account (EOA, i.e., not a smart contract such as a multisig wallet), this is not more than a temporary nuisance, as one would end up with the funds in the same account on both chains, and could simply try again. However, users should verify the transfers to B and C were successful before further use of these accounts. 

If B is a smart contract (such as a multisig wallet), one needs to exercise caution and ensure that B is present on both the PoW and PoS chains before transferring funds into it, and as always, we recommend further education on this topic before taking any action.

Why Nonce Divergence Doesn’t Fix the Problem

Some savvy users may suggest that achieving nonce divergence for an account on the two chains solves the problem and could be a sufficient fix, but we strongly urge that it is not. To understand why, it’s important to understand what a nonce is. The Ethereum protocol stipulates that transactions sent to a network by an account are sequentially numbered. This number in the sequence is called the nonce, and it is also part of the transaction payload that needs to be signed. The protocol stipulates that the very first transaction sent by an account has nonce 0, and then every transaction increments the nonce by 1, i.e., there can be no gaps.

The argument for nonce divergence assumes that if one chain advances the nonce for an account, the other chain will be behind in the transaction sequence, and therefore, the attempt to replay transactions would fail because of the gap in the nonces. While this is correct, it will hold only so long as the divergence in nonces exists. Specifically, if the attacker is able to execute transactions on the other chain and make the nonces of the account match, replays would be possible again. Similarly, if the account owner forgets that the divergence of nonces is needed and accidentally transacts on the other chain, replays may become feasible again.

Consequently, using separate wallets for networks having the same chain ID is the only effective technique for avoiding replay attacks.

Replay Attacks and Rollups

An interesting situation occurs for assets present on rollups, and bridged to other chains. Both rollups and bridges employ custodian smart contracts on the Ethereum network, which hold either the proof of state for the rollup, and/or the assets bridged to the rollup or the other chain. 

During the merge, these contracts will become duplicated on both the PoW and PoS chains as well. The implementation of the specific rollup and a bridge determines whether the updates to the state of these contracts can be performed by anyone, or only by privileged accounts, and what form these updates should have. In any case, these updates can be subjected to the same replay attacks just as any other transactions, which can result in unexpected situations. 

The safest approach to protect assets on other chains and rollups is to exit back to the Ethereum mainnet. In some cases, this may not be necessary, and as the implementations of these systems are complex and vary greatly, there is no one rule that fits every situation. For specific advice, we recommend reaching out to the projects’ official channels to ask about the post-merge situation and the potential for replay attacks.

Quantstamp Announcements

Modular Account: How Audits Can Help Shape Standards And Catalyze Mass Adoption

Quantstamp recently conducted a smart contract audit for Alchemy’s Modular Account, a wallet implementation designed from the ground up for ERC-4337 and ERC-6900 compatibility including two plugins

Read more
Quantstamp Announcements

Quantstamp 2023 Web3 Security Year In Review

As the year comes to a close, we wanted to take a moment to reflect on this year’s biggest hacks, root causes, and noteworthy trends.

Read more