EIP-1344 Chain ID opcode (ChainID opcode)
Final · EVM & execution · EIP status Final · created 2018-08-22
Adds a new EVM instruction, CHAINID, that lets a smart contract directly read the chain ID of the network it is running on.
Why it is needed
Signed transactions on Ethereum carry the chain ID defined by EIP-155 to prevent replay attacks, where a signature valid on one network is reused on another. But smart contracts themselves had no way to know which chain they were currently executing on. As a result, off-chain signing schemes such as EIP-712 had to hardcode the chain ID as a fixed value at compile time. This approach can cause loss of funds or signature replay attacks if a hard fork splits the chain or a human enters the wrong value. This EIP adds a single instruction to the EVM that reads the current chain ID, letting contracts verify signatures against the actual chain ID at execution time.
What changes
Today
- Contracts have no way to know the ID of the chain they are running on
- The chain ID used for EIP-712 signature verification has to be hardcoded at compile time
- After a hard fork, the hardcoded chain ID can diverge from the actual chain
After EIP-1344
- A single CHAINID opcode reads the chain ID directly at execution time
- Using the current chain ID in signature verification prevents reuse on other chains
- If the chain ID changes, contracts can follow the new value without being redeployed
Who is affected
- Smart contract developers: EIP-712 signature verification can read the chain ID via CHAINID instead of hardcoding it
- Layer 2 and meta-transaction services: Can safely use the chain ID to prevent off-chain signatures from being reused across chains
- Client teams: A new opcode must be implemented at slot 0x46
- Ordinary users: Even if the chain splits, the risk that a previously signed message gets reused on another chain is reduced
In depth
Opcode specification: 0x46 CHAINID
Adds the new opcode CHAINID at 0x46. It takes no stack arguments, and executing it pushes the current chain ID onto the stack. The chain ID is treated as a 256-bit value. The execution cost is G_base. From the contract's perspective, this is like reading the block number or the caller's address: a single instruction yields the identifier of the network it is running on, with no separate storage access or external call needed.
The return value comes from the client's chain ID configuration
The value the opcode returns comes from the client's chain ID configuration, and this value must match the EIP-155 unique identifier the client accepts in incoming transactions. Under EIP-155, a transaction is not required to include an EIP-155 identifier, but even when executed within such a transaction, this opcode still returns the configured chain ID rather than a default value. In other words, the result follows the network configuration regardless of how an individual transaction was signed.
Why it is needed: the compile-time chain ID problem in EIP-712
The existing EIP-712 approach specifies the chain ID at compile time. This causes problems after a hard fork, and human error can lead to loss of funds or replay attacks on signed messages. Adding the proposed opcode lets a contract access the current chain ID and verify signatures against that value.
Contentious split scenarios and developer responsibility
There is no specification for how a chain ID is assigned to a particular network; it depends on manual choices by client implementers and the community. If a contentious split occurs, it is not safe for both chains to keep the same chain ID, so one or both sides will change their value. The original text therefore states explicitly that users of CHAINID must make sure their application can handle situations where the chain ID value changes. It suggests implementing a trustless oracle that records when the chain ID changed, either as an internal application feature or by referencing a global standard contract. Without such provisions, the validity of an already-signed off-chain message could suddenly disappear, which is a problem for long verification windows such as settlement periods. Not every application needs a mitigation, and developers should judge the need case by case.
Why a plain opcode instead of a precompile: the Plasma example
The original text cites Plasma L2 as an example where a global oracle does not fit. In Plasma, an operator submits blocks summarizing L2 transactions to the base chain, and the timing of these submissions may not align precisely with major events on the main chain, such as a chain ID change. If the operator cannot control the timing of a chain ID update, block submission and the update cannot be perfectly synchronized, and some past transactions could be rejected because they fall out of sync with the update. Over-specifying behavior during a split at the protocol level creates unintended side effects like this, so the original text concludes that a simple opcode for access is better than a complex precompile or contract. It leaves how to handle chain ID changes, whether globally or locally, to be implemented flexibly by developers.
Status, compatibility, and implementation resources
This EIP is a finalized specification with status Final. It is fully backward compatible with every chain that has implemented the EIP-155 chain ID domain separator in transaction signing. This feature was previously proposed in part as EIP-901. Test cases have been added to the ethereum/tests repository, and a reference implementation for the Trinity Python client and an example trustless chain ID oracle implementation (ChainIdOracle.vy) have been published.