ERC-7744 Code Index
Last Call · Other · created 2024-07-16
A global index standard that registers and looks up deployed smart contracts by the bytecode hash, the fingerprint of the code, rather than by address. Anyone can look up, verify, and reuse code without anyone's permission.
Why it is needed
Today contracts are looked up by address. But an address is a nondeterministic value that depends on when and how a contract was deployed, and inserting a proxy (a relay contract that forwards calls to another contract) can change the code actually executed while the address stays the same. So even when an auditor vouches that some code is safe, it is hard to say that guarantee continues to hold when keyed by address, and a developer who references someone else's code by address bears the risk of the code behind the proxy being maliciously swapped. Redeploying the same code for every app that wants to use it also wastes gas. This standard provides a global index that registers and looks up contracts by the bytecode hash, the code's own fingerprint, enabling trust and reuse at the level of code rather than address.
What to implement
function register(address container) external: Registers a given contract in the index keyed by its bytecode hash (codehash). Reverts if the container is invalid or a valid address is already registered.function get(bytes32 id) external view returns (address): Returns the contract address registered under a bytecode hash. Returns the zero address if nothing is registered.function isEIP7702(address account) public view returns (bool): Determines whether an account is a delegated EOA by checking whether the first 3 bytes of the account's code match the EIP-7702 delegation designator 0xef0100.
In depth
A single mapping(bytes32 => address) store
The implementation is nothing more than a single mapping from bytecode hash to address (mapping(bytes32 => address) private index). register(address container) stores the address keyed by container.codehash and emits Indexed(container, container.codehash). get(bytes32 id) returns the address registered under that hash, or 0 if none is registered. The Rationale states that keeping the interface minimal is meant to maximize composability and ease of implementation.
register's Validation and Re-registration Rules
register first checks the target's validity via isValidContainer. There are three conditions: code.length must be greater than 0, codehash must not be bytes32(0), and the account must not be an EIP-7702 delegated account. If an address (etalon) is already registered under the same hash, isValidContainer is run again on that address; if it is still valid, the call reverts with alreadyExists(container.codehash, container); if it is no longer valid (for example, because its code has disappeared), overwriting with the new address is allowed.
Excluding EIP-7702 Delegated Accounts
The isEIP7702 function uses assembly's extcodecopy to read the first 3 bytes of the account's code and compares them against EIP-7702's reserved delegation designator 0xef0100. If they match, the account is treated as a delegated EOA and register reverts. Delegated accounts are excluded from registration because what they point to can change, which conflicts with the immutability premise of a bytecode-hash-based index.
Why Re-registration Is Banned, and the Selfdestruct Exception
The Rationale explains that although the probability of a hash collision attack is small, it is not zero, so re-registration is banned to prevent overwriting an already indexed entry in response to that risk. However, the situation differs if a registered contract's code becomes empty, for example via selfdestruct. When register is called, isValidContainer is run again on the existing registered address, and if it fails, re-indexing under the new address is allowed.
CREATE2 Deterministic Deployment
The CodeIndex contract is deployed at address 0xC0De1D1126b6D698a0073A4e66520111cEe22F62. It is deployed via CREATE2 through the deterministic deployer 0x4e59b44847b379578588920ca78fbf26c0b4956c with salt 0x9425035d50edcd7504fe5eeb5df841cc74fe6cccd82dca6ee75bcdf774bd88d9. This salt was obtained by searching for a vanity address starting with c0de1d, meaning Code ID, and the bytecode is compiled with solc 0.8.28 using the options --optimize --optimize-runs 2000 --metadata-hash none --via-ir --optimize-yul. This approach lets the same address be expected on any chain.
Use Cases Cited in Motivation
The source lists four use cases. First, auditors can leave an integrity guarantee on the bytecode itself, independent of deployment parameters or storage contents. Second, a project can get whitelisting or pre-approval before deployment using just the hash, without disclosing the code. Third, it prevents malicious changes that could arise via proxies in address-based references, creating a chain of trust that extends to the end user. Fourth, it can save redeployment gas by referencing code that already exists. Bytecode signing, whitelisting, and decentralized deployment mechanisms are mentioned as representative examples.
Standard status: Last Call
This proposal is a Standards Track ERC, currently in Last Call, the final comment period, with a stated deadline of 2025-07-29. Since it has not yet reached the Final stage, details may still change. The Rationale explains that standardizing as an ERC rather than a library creates a single official reference point, driving code reuse and broad adoption.
Things to watch out for
- Does not filter out malicious code: The registry does not guarantee the safety or functionality of registered contracts. Users must exercise their own caution and due diligence before interacting with a registered contract.
- Storage state is not indexed: The registry only points to a contract's bytecode and does not cover storage contents. Contract state is not indexed and may change over time.
- Blocks registration of EIP-7702 delegated accounts: Accounts delegated via EIP-7702 are not indexed. On a registration attempt, it checks whether the code begins with the reserved delegation designator 0xef0100 and reverts if so.
- Re-indexing a self-destructed contract: If a registered contract's code becomes empty, re-indexing becomes possible. On a register call, it runs an isValidContainer check against the existing registered address, and if that fails, it allows re-indexing to the newly specified address.