Final ERC for publishing and detecting which interfaces a contract implements via `supportsInterface(bytes4)`. The interop primitive underpinning ERC-721, ERC-1155, ERC-2535, ERC-7540, and most modern token standards.
- 01interface introspection
- 02polymorphic contract integration
- 03safe receiver hooks (ERC-721/1155)
- 04registry / router compatibility checks
- pnpm add @openzeppelin/contracts
- forge install transmissions11/solmate
- forge install vectorized/solady
Implement `function supportsInterface(bytes4 interfaceID) external view returns (bool)` (selector `0x01ffc9a7`), returning `true` for `0x01ffc9a7`, `false` for `0xffffffff`, and the appropriate boolean for every interface the contract claims. Use OpenZeppelin's `ERC165` base or Solady's `ERC165` mixin and override `supportsInterface` with `super.supportsInterface(id) || id == type(IMyInterface).interfaceId`. Compute interface IDs at compile time with `type(IFoo).interfaceId`, never hard-code hex literals.
- ⚑The function MUST return within 30,000 gas — putting storage reads or external calls behind `supportsInterface` violates the spec and breaks integrators that gas-cap the call.
- ⚑`type(IFoo).interfaceId` excludes inherited functions in Solidity — when an interface extends another, the resulting ID is only the XOR of the new selectors, not the combined set; declare each parent ID separately.
- ⚑Returning `true` for `0xffffffff` is an explicit spec violation that integrators (e.g. ERC-721 receivers) use as a tamper-detection canary — it must always be `false`.
- ⚑Diamonds (ERC-2535) typically aggregate `supportsInterface` answers across facets via a shared mapping — forgetting to register a facet's interface IDs after `diamondCut` causes silent integration failures.
- ⚑ERC-721 / ERC-1155 safe-transfer flows revert when the receiver's `supportsInterface` lookup itself reverts (e.g. EOA, non-deployed contract) — wrap the lookup with `try/catch` when calling externally.