iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Interfaces & Libraries

Solidity interfaces: contract APIs without implementation. The standards (ERC-20, ERC-721, ERC-1155) and how to design your own.

Web3 — Solidity interfaces

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

// ===== Interface basics =====
interface IGreeter {
    function greet(address who) external view returns (string memory);
    event Greeted(address indexed who);
}

contract Greeter is IGreeter {
    function greet(address who) external pure returns (string memory) {
        // pure: no state read or write
        return "hello";
    }
}

// ===== Why interfaces =====
// 1. Standards: ERC-20 / ERC-721 / ERC-1155 are interfaces
// 2. Composition: import only the interface; call any deployed contract that implements it
// 3. Testing: mock contracts conform to the interface
// 4. Smaller bytecode: interfaces have no implementation

// ===== ERC-20 (fungible tokens) =====
interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

// Implement using OpenZeppelin:
// import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
// contract MyToken is ERC20 { constructor() ERC20("My", "MY") {} }

// ===== ERC-721 (non-fungible tokens) =====
interface IERC721 {
    function balanceOf(address owner) external view returns (uint256);
    function ownerOf(uint256 tokenId) external view returns (address);
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
    function safeTransferFrom(address from, address to, uint256 tokenId) external;
    function transferFrom(address from, address to, uint256 tokenId) external;
    function approve(address to, uint256 tokenId) external;
    function setApprovalForAll(address operator, bool approved) external;
    function getApproved(uint256 tokenId) external view returns (address);
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

// ===== ERC-1155 (multi-token) =====
// Supports both fungible + non-fungible IDs in one contract; gas-efficient batch ops.

// ===== ERC-165 (interface detection) =====
interface IERC165 {
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// Use to check if a contract implements a given interface BEFORE calling it.

// ===== Designing your own =====
interface ISubscription {
    function isActive(address user) external view returns (bool);
    function subscribe(uint256 tierId) external payable;
    function cancel() external;
    event Subscribed(address indexed user, uint256 indexed tierId);
    event Cancelled(address indexed user);
}

// Implementers can vary internals; consumers depend on the interface.

// ===== Visibility rules =====
// In interfaces, all functions MUST be external (callable from outside).
// They cannot have state variables, modifiers, or constructors.
// Events ARE allowed.

// ===== Calling external contracts via interface =====
contract Vault {
    function depositTo(IERC20 token, address to, uint256 amount) external {
        // Pull tokens from the caller (must have approved this contract first)
        token.transferFrom(msg.sender, to, amount);
    }
}

// ===== Patterns to internalise =====
// - Standard interfaces (ERC-20/721/1155) wherever applicable
// - Small custom interfaces for your contracts; consumers depend on them
// - ERC-165 supportsInterface for runtime checks
// - OpenZeppelin implementations as the default (audited + battle-tested)

// ===== Pitfalls =====
// - Reinventing ERC-20 / 721 instead of using OpenZeppelin
// - Forgetting that interface functions are external (cannot be called internally without 'this.')
// - Calling unverified contracts via interface assumes they behave; check supportsInterface
// - Skipping events on state-changing functions (no indexers can catch up)

Why it matters

Solidity interfaces are the API contracts of Web3. Use the standards (ERC-20, ERC-721, ERC-1155) where you can, OpenZeppelin implementations for audited code, and your own small interfaces for composition. Interfaces enable mocking, indexing, and cross-contract calls — and keep the bytecode small.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
interface IERC20 {
    function transfer(address to, uint256 amount) external returns (bool);
    function balanceOf(address owner) external view returns (uint256);
}

library Math {
    function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; }
}
Try it Yourself »

Discussion

Loading…