Beginner

Understanding the ABI — Kudos board build (step 4/6)

Demystify the ABI.

Developer Beginner
7/7 — Understanding the ABI — Kudos board build (step 4/6)

GoalHarden the app: validate inputs and rate-limit spammers.

What to write

  1. Errors
    declare custom errors for empty, too long, too soon.
    error EmptyMessage();
    error TooLong();
    error TooSoon(uint256 readyAt);

    Custom errors save gas vs revert strings and surface clear causes.

  2. Limits
    set max length and cooldown constants plus lastSent tracking.
    uint256 private constant MAX_LEN = 140;
    uint256 private constant COOLDOWN = 60;
    mapping(address => uint256) private _lastSent;

    Constants make limits clear; _lastSent tracks rate limiting per sender.

  3. Guarded send
    validate message length and cooldown before writing.
    function sendKudos(address to, string calldata message) external {
        if (bytes(message).length == 0) revert EmptyMessage();
        if (bytes(message).length > MAX_LEN) revert TooLong();
        uint256 ready = _lastSent[msg.sender] + COOLDOWN;
        if (block.timestamp < ready) revert TooSoon(ready);
    
        _lastKudos[to] = message;
        kudosCount[to] += 1;
        totalKudos += 1;
        _lastSent[msg.sender] = block.timestamp;
        emit KudosSent(msg.sender, to, message);
    }

    Checks happen before state changes; cooldown uses block timestamps.

Why Guards block empty spam and throttle abuse; custom errors keep gas down.

Try it Deploy; test empty/too-long messages and send twice quickly to see cooldown reverts.

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

contract KudosBoard {
    error EmptyMessage();
    error TooLong();
    error TooSoon(uint256 readyAt);

    uint256 private constant MAX_LEN = 140;
    uint256 private constant COOLDOWN = 60;

    mapping(address => string) private _lastKudos;
    mapping(address => uint256) public kudosCount;
    mapping(address => uint256) private _lastSent;
    uint256 public totalKudos;

    event KudosSent(address indexed from, address indexed to, string message);

    function sendKudos(address to, string calldata message) external {
        if (bytes(message).length == 0) revert EmptyMessage();
        if (bytes(message).length > MAX_LEN) revert TooLong();
        uint256 ready = _lastSent[msg.sender] + COOLDOWN;
        if (block.timestamp < ready) revert TooSoon(ready);

        _lastKudos[to] = message;
        kudosCount[to] += 1;
        totalKudos += 1;
        _lastSent[msg.sender] = block.timestamp;
        emit KudosSent(msg.sender, to, message);
    }

    function lastKudos(address user) external view returns (string memory) {
        return _lastKudos[user];
    }
}

NextWe’ll keep extending this in the following lesson; this version already deploys and runs.

← Previous section
Next lesson →