# Functions, Modifiers, Events and Errors — Blockchain & Smart Contracts (Solidity)

Source: https://www.geekswithgeeks.com/en/solidity/s-functions

> Write view, pure and payable functions, modifiers, events and custom errors.

## Function building blocks

Functions declare their **state mutability**: **`view`** reads but does not modify state, **`pure`** neither reads nor modifies, **`payable`** can receive ether, and the default can modify state. Functions can **return multiple values**, named or unnamed. **Modifiers** wrap functions with reusable checks, such as `onlyOwner`, where `_;` marks where the function body runs. **Events** (`event Transfer(address indexed from, address indexed to, uint256 value)`) write **logs** that off-chain apps index and subscribe to; up to three parameters can be **`indexed`** for efficient filtering, and logs are much cheaper than storage but cannot be read by contracts. For errors, `require(condition, "message")` reverts with a reason string, while **custom errors** (`error InsufficientBalance(uint256 available, uint256 needed);` used with `revert InsufficientBalance(bal, amount)`) are cheaper and carry structured data; recent compilers also accept `require(condition, CustomError(...))`. **`assert`** is for invariants that should never fail. A **revert** undoes all state changes in the call. Use **`try/catch`** to handle failures from external calls.

## An owned, pausable piggy bank

Modifiers, payable functions, events and custom errors.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract PiggyBank {
    address public immutable owner;
    bool public paused;
    uint256 public immutable unlockTime;

    event Deposited(address indexed from, uint256 amount);
    event Withdrawn(uint256 amount);

    error NotOwner(address caller);
    error Locked(uint256 unlockTime);
    error Paused();
    error TransferFailed();

    modifier onlyOwner() {
        if (msg.sender != owner) revert NotOwner(msg.sender);
        _;                                   // the function body runs here
    }

    modifier whenNotPaused() {
        if (paused) revert Paused();
        _;
    }

    constructor(uint256 lockSeconds) {
        owner = msg.sender;
        unlockTime = block.timestamp + lockSeconds;
    }

    function deposit() external payable whenNotPaused {
        require(msg.value > 0, "no ether sent");
        emit Deposited(msg.sender, msg.value);
    }

    function setPaused(bool value) external onlyOwner {
        paused = value;
    }

    function withdrawAll() external onlyOwner {
        if (block.timestamp < unlockTime) revert Locked(unlockTime);
        uint256 amount = address(this).balance;
        emit Withdrawn(amount);
        (bool ok, ) = owner.call{value: amount}("");
        if (!ok) revert TransferFailed();
    }
}
```

## Prefer custom errors

Custom errors cost less gas than long revert strings, both at deployment and when they trigger, and they give front ends structured data such as the unlock time instead of a vague message.

**Quiz:** What does the `_;` inside a modifier represent?

- [x] The place where the modified function's body is executed
- [ ] The end of the contract
- [ ] A comment
- [ ] A revert

*Answer:* The place where the modified function's body is executed. Code before `_;` runs first, then the function body, then any code after it.
