Abstract Contracts & Interfaces

A contract with at least one unimplemented function must be abstract; a contract may also be marked abstract deliberately so it cannot be deployed.

20.1 Abstract contracts

A contract with at least one unimplemented function must be abstract; a contract may also be marked abstract deliberately so it cannot be deployed.

Abstract contracts are used for the template method pattern: the base defines a flow and calls internal virtual "hooks" that children implement.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

abstract contract Shape {
    function area() public view virtual returns (uint256);       // no body
    function describe() public view returns (string memory) {
        return area() > 100 ? "big" : "small";
    }
}

contract Square is Shape {
    uint256 public side;
    constructor(uint256 s) { side = s; }
    function area() public view override returns (uint256) { return side * side; }
}

20.2 Interfaces

Interface restrictions: no implemented functions, no state variables, no constructors, all functions external, can inherit other interfaces, can declare events, errors, structs and enums.

external only shared partial implementation API definition between contracts

type(ICounter).interfaceId is the XOR of all function selectors in the interface (excluding inherited ones) — used by ERC-165.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface ICounter {
    event Incremented(uint256 newValue);
    error Overflow();

    function count() external view returns (uint256);
    function increment() external;
}

contract Counter is ICounter {
    uint256 public count;                   // public getter implements count()

    function increment() external {         // `override` optional for interface functions (0.8.8+)
        count += 1;
        emit Incremented(count);
    }
}

contract User {
    function bump(address counter) external returns (uint256) {
        ICounter c = ICounter(counter);     // wrap an address in an interface type
        c.increment();
        return c.count();
    }
}