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.
Flow Cover mint gates, VRF callbacks, permit staking, and upgrade authorization. Snapshot gas for hot paths.
What to write
- 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.
- 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.
- 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.