Now that we have ERC-20 in our codebase, lets extend it to create our own ERC-20 contract

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Script.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyToken is ERC20 {
    uint256 public number;

    constructor() ERC20("MyToken", "MTK") {}
    function mint(address to, uint256 amount) public {
        _mint(to, amount);
    }
}

Positive tests

Add tests for the standard functions in an ERC-20 contract

  1. test minting
  2. Test transfers
  3. Test approvals

<aside> 💡

You need to learn about some cheat codes in forge before you attempt this assignment. Ref - https://book.getfoundry.sh/forge/cheatcodes (read about pranks)

</aside>

Negative tests

Add tests that expect failures

  1. User tries to spend tokens they dont have
  2. User tries to spend on someone elses behalf what they dont have

Class tests

// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.13;

import "forge-std/Test.sol";

import "src/KiratCoin.sol";

contract TestKiratCoin is Test {

    KiratCoin c;

    function setUp() public {
        c = new KiratCoin();
    }

    function testMint() public {
        c.mint(address(this), 100);
        assertEq(c.balanceOf(address(this)), 100 ,"ok");
        assertEq(c.balanceOf(0x587EFaEe4f308aB2795ca35A27Dff8c1dfAF9e3f), uint256(0) ,"ok");

        c.mint(0x587EFaEe4f308aB2795ca35A27Dff8c1dfAF9e3f, 100);
        assertEq(c.balanceOf(0x587EFaEe4f308aB2795ca35A27Dff8c1dfAF9e3f), 100, "ok");
    }

    function testTransfer() public {
        c.mint(address(this), 100);
        c.transfer(0x587EFaEe4f308aB2795ca35A27Dff8c1dfAF9e3f, 50);
        
        assertEq(c.balanceOf(address(this)), 50);
        assertEq(c.balanceOf(0x587EFaEe4f308aB2795ca35A27Dff8c1dfAF9e3f), 50);

        vm.prank(0x587EFaEe4f308aB2795ca35A27Dff8c1dfAF9e3f);
        c.transfer(address(this), 50);

        assertEq(c.balanceOf(address(this)), 100);
        assertEq(c.balanceOf(0x587EFaEe4f308aB2795ca35A27Dff8c1dfAF9e3f), 0);
    }

    function testApprovals() public {
        c.mint(address(this), 100);

        c.approve(0x587EFaEe4f308aB2795ca35A27Dff8c1dfAF9e3f, 10);

        assertEq(c.allowance(address(this), 0x587EFaEe4f308aB2795ca35A27Dff8c1dfAF9e3f), 10);
        assertEq(c.allowance(0x587EFaEe4f308aB2795ca35A27Dff8c1dfAF9e3f, address(this)), 0);
        
        vm.prank(0x587EFaEe4f308aB2795ca35A27Dff8c1dfAF9e3f);
        c.transferFrom(address(this), 0x587EFaEe4f308aB2795ca35A27Dff8c1dfAF9e3f, 5);

        assertEq(c.balanceOf(address(this)), 95, "ok");
        assertEq(c.balanceOf(0x587EFaEe4f308aB2795ca35A27Dff8c1dfAF9e3f), 5, "ok");
        assertEq(c.allowance(address(this), 0x587EFaEe4f308aB2795ca35A27Dff8c1dfAF9e3f), 5);
    }

    function testFailApprovals() public {
        c.mint(address(this), 100);
        c.approve(0x587EFaEe4f308aB2795ca35A27Dff8c1dfAF9e3f, 10);
        
        vm.prank(0x587EFaEe4f308aB2795ca35A27Dff8c1dfAF9e3f);
        c.transferFrom(address(this), 0x587EFaEe4f308aB2795ca35A27Dff8c1dfAF9e3f, 100);
    }

    function testFailTransfer() public {
        c.mint(address(this), 20);
        c.transfer(0x587EFaEe4f308aB2795ca35A27Dff8c1dfAF9e3f, 100);
    }

}