ERC-5269 Querying supported standards by ERC number (ERC Detection and Discovery)
Review · Other · created 2022-07-15
A detection interface that lets a contract's supported ERC standards be queried using human-readable ERC numbers, and per caller address.
Why it is needed
Until now, ERC-165 has been used to check which standards a contract supports. However, because ERC-165 identifies interfaces by collecting hashes of function signatures, it requires at least one function to exist, and cannot represent standards that have no functions, such as data formats or signature schemes, or soul-bound style standards that simply reject transfer calls. Also, because bytes4 hash values are hard for humans to read, a conversion table must be kept inside the contract to know which ERC number a given hash corresponds to. Finally, since ERC-165 gives the same answer regardless of who asks, there is no way to query support on a per-caller basis for contracts that treat admin accounts differently from regular accounts, such as transparent upgradeable proxies. This standard aims to solve these limitations by defining a query function that takes an ERC number and a caller address and returns the support status.
In depth
Two identifiers: majorERCIdentifier and minorERCIdentifier
majorERCIdentifier is the value commonly called the ERC number. For ERC-721 it is 721, and for this standard itself it is 5269. In a supportERC call, this value SHOULD be in the range (0, 2^32-1], and values of 2^32 or above are reserved in case of supporting standard collections other than ERCs. The name majorERCIdentifier was used instead of ERCNumber to allow for the possibility of future standards that are not number-based. minorERCIdentifier is a bytes32 value left for each ERC's authors to define. For example, the authors of ERC-721 could specify keccak256("ERC721Metadata") or keccak256("ERC721Metadata.tokenURI"), and it can also be used to distinguish versions, as with V1 through V4 of ERC-712. The value 0 is reserved to refer to the entire main behavior of the ERC in question.
Rules for the return value ercStatus
supportERC returns the support status as a bytes32. If the queried ERC is in FINAL status, it MUST return keccak256("FINAL"), and for ERCs that are not FINAL, it SHOULD return keccak256("DRAFT"). ERC authors MAY define their own status values, such as keccak256("DRAFTv1") or keccak256("DRAFT-option1"), but such values must be documented in the body of that ERC. When querying an ERC-XXX that is not Final, it MUST NOT return keccak256("FINAL"), and returning 0 in that case is recommended. Callers must treat any value other than keccak256("FINAL") as not final, and strictly treat 0 as unsupported. supportERC must be a view function and must not change the EVM's global state.
Why caller is taken as an input
Unlike ERC-165, this standard takes an address caller as input, because the same contract may behave differently depending on the calling account. A representative case is the Transparent Upgradable Pattern, where a proxy contract handles calls from an admin account differently from calls from a regular account. Taking it as an argument instead of using msg.sender allows querying in advance whether a given interface would be provided to a particular sender, without sending a transaction. This design accounts for the widespread use of proxy patterns such as ERC-1967.
Declaration via the OnSupportERC event
A conforming contract SHOULD emit an OnSupportERC(address(0), 5269, 0, ERC_5269_STATUS, []) event at construction or upgrade time. Here, if caller is address(0), it means the declaration applies to all callers. The main behavior or sub-behaviors of other supported ERCs MAY also be declared with the same event, and an event MAY also be emitted when support status changes for a specific caller or for all callers. The reference implementation's ERC-721 example declares the entirety of 721, ERC721Metadata, and ERC721Enumerable as FINAL status at deployment.
Difference from and competing relationship with ERC-165
This standard was created as a competing standard to ERC-165. ERC-165 declares interfaces via function signature hashes, so at least one function must exist, but this standard can declare support even for standards that have no functions, using their number. Also, using the ERC number as-is is human-readable and easy to handle with name-based contracts such as ENS. In fact, cases of specifying standards by ERC number are increasing, such as the extensions list in ERC-5267, the derivation paths in ERC-600 and ERC-601, the instruction_id in ERC-5568, and the uint eip field in ERC-6120. Having a number-based detection interface eliminates the need for a lookup table converting bytes4 identifiers to ERC numbers. During the design discussion, alternatives such as using a hash of the number or an ERC-165 identifier were also considered, but the original number was chosen as-is because a hash is hard to read back.
Querying this standard itself and extraData
As of this document, ERC_5269_STATUS is set to keccak256("DRAFTv1"). Based on the current snapshot, a call to supportERC((any caller), 5269, 0, []) must return ERC_5269_STATUS, and once this standard becomes FINAL, the same call must return ERC_5269_STATUS. The final argument extraData is a bytes parameter for future extension per ERC-5750. The reference implementation has a simple structure that returns ERC_STATUS when majorERCIdentifier is 5269 and minorERCIdentifier is 0, and returns 0 otherwise.
A proposal still in Review stage
The status of this standard is Review. It has not yet been finalized, so its content may change, and the status value definitions within the specification (such as DRAFTv1) may also change as the standard progresses.
Things to watch out for
- Not taking declarations of support at face value: As with ERC-165, even if a contract declares that it supports a given ERC interface, there is no guarantee that it has actually implemented it correctly. The querying party should assume that the declaration may not be accurate.