interface
An interface is a pure description of how to talk to a contract: a list of function signatures with no code behind them. It is the contract-to-contract equivalent of a plug shape — it tells your code which functions exist and what they take and return, so you can call a deployed contract without ever importing its full source. The familiar IERC20 is an interface.
Declared with the interface keyword, it may only contain external function signatures, plus events, custom errors, structs, and enums. It cannot have implemented functions, a constructor, or state variables, and all its functions are implicitly virtual and external. An interface defines a type you can cast an address to: writing IERC20(token).transfer(to, amount) tells the compiler to ABI-encode a call with the transfer selector and send it to that address. Interfaces may inherit other interfaces but never regular contracts.
Calling through an interface is just a normal external message call dispatched by the 4-byte function selector — the interface is a compile-time convenience, not a runtime guarantee. If the target address does not actually implement that selector, the call may revert or hit the target's fallback function and silently do something unexpected. The compiler derives the contract's ABI directly from its interface, which is why interfaces are the lingua franca for composing protocols out of independently deployed contracts.
interface IERC20 {
function transfer(address to, uint256 amount) external returns (bool);
function balanceOf(address who) external view returns (uint256);
}
IERC20(token).transfer(to, amount);A shape to call against, no implementation included.
An interface is a promise you make to the compiler, not one the target keeps. Casting an arbitrary address to IERC20 does not verify it is really a token — the call may hit a fallback and quietly succeed.