// SPDX-License-Identifier: MIT pragma solidity ^0.8.30; import {Love} from "../src/Love.sol"; import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Test} from "forge-std/Test.sol"; contract LoveTest is Test { Love public love; address alice = makeAddr("alice"); address bob = makeAddr("bob"); address stranger = makeAddr("stranger"); function setUp() public { love = new Love(); } /*////////////////////////////////////////////////////////////// METADATA //////////////////////////////////////////////////////////////*/ function test_Metadata() public view { assertEq(love.name(), "Love"); assertEq(love.symbol(), "LOVE"); assertEq(love.decimals(), 18); } function test_InitialSupplyIsZero() public view { assertEq(love.totalSupply(), 0); } /*////////////////////////////////////////////////////////////// MINT //////////////////////////////////////////////////////////////*/ function test_Mint() public { vm.prank(alice); love.mint(alice, 100e18); assertEq(love.balanceOf(alice), 100e18); assertEq(love.totalSupply(), 100e18); } /// @dev The point of this contract: `mint` has no access control. function test_AnyoneCanMintToAnyone() public { vm.prank(stranger); love.mint(bob, 1000e18); assertEq(love.balanceOf(bob), 1000e18); assertEq(love.balanceOf(stranger), 0); assertEq(love.totalSupply(), 1000e18); } function test_MintEmitsTransferFromZero() public { vm.expectEmit(true, true, false, true); emit IERC20.Transfer(address(0), alice, 5e18); vm.prank(alice); love.mint(alice, 5e18); } function test_MintAccumulates() public { vm.startPrank(alice); love.mint(alice, 1e18); love.mint(alice, 2e18); vm.stopPrank(); assertEq(love.balanceOf(alice), 3e18); assertEq(love.totalSupply(), 3e18); } function test_RevertWhen_MintingToZeroAddress() public { vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InvalidReceiver.selector, address(0))); love.mint(address(0), 1e18); } function test_RevertWhen_MintOverflowsTotalSupply() public { love.mint(alice, type(uint256).max); vm.expectRevert(); love.mint(bob, 1); } /*////////////////////////////////////////////////////////////// TRANSFER //////////////////////////////////////////////////////////////*/ function test_Transfer() public { love.mint(alice, 10e18); vm.prank(alice); assertTrue(love.transfer(bob, 4e18)); assertEq(love.balanceOf(alice), 6e18); assertEq(love.balanceOf(bob), 4e18); } function test_RevertWhen_TransferExceedsBalance() public { love.mint(alice, 1e18); vm.prank(alice); vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InsufficientBalance.selector, alice, 1e18, 2e18)); // forge-lint: disable-next-line(erc20-unchecked-transfer) love.transfer(bob, 2e18); } /*////////////////////////////////////////////////////////////// APPROVE / TRANSFERFROM //////////////////////////////////////////////////////////////*/ function test_ApproveAndTransferFrom() public { love.mint(alice, 10e18); vm.prank(alice); assertTrue(love.approve(bob, 6e18)); assertEq(love.allowance(alice, bob), 6e18); vm.prank(bob); assertTrue(love.transferFrom(alice, stranger, 6e18)); assertEq(love.balanceOf(alice), 4e18); assertEq(love.balanceOf(stranger), 6e18); assertEq(love.allowance(alice, bob), 0); } function test_RevertWhen_TransferFromExceedsAllowance() public { love.mint(alice, 10e18); vm.prank(alice); love.approve(bob, 1e18); vm.prank(bob); vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InsufficientAllowance.selector, bob, 1e18, 2e18)); // forge-lint: disable-next-line(erc20-unchecked-transfer) love.transferFrom(alice, stranger, 2e18); } /*////////////////////////////////////////////////////////////// FUZZ //////////////////////////////////////////////////////////////*/ function testFuzz_MintByAnyCaller(address caller, address to, uint256 amount) public { vm.assume(to != address(0)); vm.prank(caller); love.mint(to, amount); assertEq(love.balanceOf(to), amount); assertEq(love.totalSupply(), amount); } function testFuzz_Transfer(uint256 minted, uint256 amount) public { amount = bound(amount, 0, minted); love.mint(alice, minted); vm.prank(alice); assertTrue(love.transfer(bob, amount)); assertEq(love.balanceOf(alice), minted - amount); assertEq(love.balanceOf(bob), amount); } }