Intermediate

Testing Smart Contracts — Kudos build — tests

Build confidence before deploying.

Developer Intermediate
4/6 — Testing Smart Contracts — Kudos build — tests

Objective Lock down the upgradeable Kudos stack with deterministic tests and gas baselines.

Talk is cheap. Show me the code.
— Linus Torvalds

Flow Cover mint gates, VRF callbacks, permit staking, and upgrade authorization. Snapshot gas for hot paths.

What to write

  1. Permit + stake test
    Prove signature + transfer path works.
    function testPermitStake() public {
        vm.prank(user);
        (uint8 v, bytes32 r, bytes32 s) = signPermit(user, address(staking), 1 ether, block.timestamp + 1 days);
        vm.prank(user);
        staking.stakeWithPermit(PermitData({value: 1 ether, deadline: block.timestamp + 1 days, v: v, r: r, s: s}));
        assertEq(staking.stakes(user), 1 ether);
    }

    Asserts signature validation and transfer path in one test.

  2. Upgrade guard test
    Ensure only the upgrader can change logic.
    function testUpgradeGuarded() public {
        vm.expectRevert();
        proxy.upgradeTo(address(newImpl));
        vm.prank(admin);
        proxy.upgradeTo(address(newImpl));
    }

    Proves unauthorized upgrades fail; admins succeed.

  3. Gas baseline
    Snapshot hot paths.

    Compare before/after optimizations to catch regressions.

Why Tests prove the upgradeable stack holds together; gas snapshots catch regressions before users do.

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

import "forge-std/Test.sol";
import "../src/KudosToken.sol";
import "../src/KudosStaking.sol";

contract KudosStackTest is Test {
    KudosToken token;
    KudosStaking staking;
    address user = address(0xBEEF);

    function setUp() public {
        token = new KudosToken();
        staking = new KudosStaking(address(token));
        token.mint(user, 100 ether);
    }

    function testPermitStake() public {
        vm.startPrank(user);
        (uint8 v, bytes32 r, bytes32 s) = _signPermit(user, address(staking), 1 ether, block.timestamp + 1 days);
        staking.stakeWithPermit(KudosStaking.PermitData({
            value: 1 ether,
            deadline: block.timestamp + 1 days,
            v: v, r: r, s: s
        }));
        vm.stopPrank();
        assertEq(staking.stakes(user), 1 ether);
    }

    function _signPermit(address owner, address spender, uint256 value, uint256 deadline) internal view returns (uint8, bytes32, bytes32) {
        bytes32 digest = token.getDigest(owner, spender, value, token.nonces(owner), deadline);
        return vm.sign(owner, digest);
    }
}

Next Trim gas and storage costs to keep the stack lean.

← Previous section
Next section →