Introspection
This document is better viewed at https://docs.openzeppelin.com/contracts/api/introspection |
This set of interfaces and contracts deal with type introspection of contracts, that is, examining which functions can be called on them. This is usually referred to as a contract’s interface.
Ethereum contracts have no native concept of an interface, so applications must usually simply trust they are not making an incorrect call. For trusted setups this is a non-issue, but often unknown and untrusted third-party addresses need to be interacted with. There may even not be any direct calls to them! (e.g. ERC20
tokens may be sent to a contract that lacks a way to transfer them out of it, locking them forever). In these cases, a contract declaring its interface can be very helpful in preventing errors.
There are two main ways to approach this.
-
Locally, where a contract implements
IERC165
and declares an interface, and a second one queries it directly viaERC165Checker
. -
Globally, where a global and unique registry (
IERC1820Registry
) is used to register implementers of a certain interface (IERC1820Implementer
). It is then the registry that is queried, which allows for more complex setups, like contracts implementing interfaces for externally-owned accounts.
Note that, in all cases, accounts simply declare their interfaces, but they are not required to actually implement them. This mechanism can therefore be used to both prevent errors and allow for complex interactions (see ERC777
), but it must not be relied on for security.
Local
IERC165
Interface of the ERC165 standard, as defined in the EIP.
Implementers can declare support of contract interfaces, which can then be
queried by others (ERC165Checker
).
For an implementation, see ERC165
.
supportsInterface(bytes4 interfaceId) → bool
external
Returns true if this contract implements the interface defined by
interfaceId
. See the corresponding
EIP section
to learn more about how these ids are created.
This function call must use less than 30 000 gas.
ERC165
Implementation of the IERC165
interface.
Contracts may inherit from this and call _registerInterface
to declare
their support of an interface.
supportsInterface(bytes4 interfaceId) → bool
public
Time complexity O(1), guaranteed to always use less than 30 000 gas.
_registerInterface(bytes4 interfaceId)
internal
Registers the contract as an implementer of the interface defined by
interfaceId
. Support of the actual ERC165 interface is automatic and
registering its interface id is not required.
Requirements:
-
interfaceId
cannot be the ERC165 invalid interface (0xffffffff
).
ERC165Checker
Library used to query support of an interface declared via IERC165
.
Note that these functions return the actual result of the query: they do not
revert
if an interface is not supported. It is up to the caller to decide
what to do in these cases.
supportsERC165(address account) → bool
internal
Returns true if account
supports the IERC165
interface,
supportsInterface(address account, bytes4 interfaceId) → bool
internal
Returns true if account
supports the interface defined by
interfaceId
. Support for IERC165
itself is queried automatically.
getSupportedInterfaces(address account, bytes4[] interfaceIds) → bool[]
internal
Returns a boolean array where each value corresponds to the interfaces passed in and whether they’re supported or not. This allows you to batch check interfaces for a contract where your expectation is that some interfaces may not be supported.
Available since v3.4.
Global
IERC1820Registry
Interface of the global ERC1820 Registry, as defined in the EIP. Accounts may register implementers for interfaces in this registry, as well as query support.
Implementers may be shared by multiple accounts, and can also implement more than a single interface for each account. Contracts can implement interfaces for themselves, but externally-owned accounts (EOA) must delegate this to a contract.
IERC165
interfaces can also be queried via the registry.
For an in-depth explanation and source code analysis, see the EIP text.
setManager(address account, address newManager)
external
Sets newManager
as the manager for account
. A manager of an
account is able to set interface implementers for it.
By default, each account is its own manager. Passing a value of 0x0
in
newManager
will reset the manager to this initial state.
Emits a ManagerChanged
event.
Requirements:
-
the caller must be the current manager for
account
.
setInterfaceImplementer(address account, bytes32 _interfaceHash, address implementer)
external
Sets the implementer
contract as account
's implementer for
interfaceHash
.
account
being the zero address is an alias for the caller’s address.
The zero address can also be used in implementer
to remove an old one.
See interfaceHash
to learn how these are created.
Emits an InterfaceImplementerSet
event.
Requirements:
-
the caller must be the current manager for
account
. -
interfaceHash
must not be anIERC165
interface id (i.e. it must not end in 28 zeroes). -
implementer
must implementIERC1820Implementer
and return true when queried for support, unlessimplementer
is the caller. SeeIERC1820Implementer.canImplementInterfaceForAddress
.
getInterfaceImplementer(address account, bytes32 _interfaceHash) → address
external
Returns the implementer of interfaceHash
for account
. If no such
implementer is registered, returns the zero address.
If interfaceHash
is an IERC165
interface id (i.e. it ends with 28
zeroes), account
will be queried for support of it.
account
being the zero address is an alias for the caller’s address.
interfaceHash(string interfaceName) → bytes32
external
Returns the interface hash for an interfaceName
, as defined in the
corresponding
section of the EIP.
IERC1820Implementer
Interface for an ERC1820 implementer, as defined in the
EIP.
Used by contracts that will be registered as implementers in the
IERC1820Registry
.
ERC1820Implementer
Implementation of the IERC1820Implementer
interface.
Contracts may inherit from this and call _registerInterfaceForAddress
to
declare their willingness to be implementers.
IERC1820Registry.setInterfaceImplementer
should then be called for the
registration to be complete.