Skip to content

Creating an ERC-20 contract

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
  • Solution

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.13;
    import {Test, console} from "forge-std/Test.sol";
    import { MyToken } from "../src/Token.sol";
    contract CounterTest is Test {
    MyToken public token;
    function setUp() public {
    token = new MyToken();
    }
    function test_Increment() public {
    token.mint(address(this), 100);
    assertEq(token.balanceOf(address(this)), 100);
    token.mint(0x075c299cf3b9FCF7C9fD5272cd2ed21A4688bEeD, 100);
    assertEq(token.balanceOf(0x075c299cf3b9FCF7C9fD5272cd2ed21A4688bEeD), 100);
    }
    function test_Transfer() public {
    token.mint(address(this), 100);
    token.transfer(0x075c299cf3b9FCF7C9fD5272cd2ed21A4688bEeD, 100);
    assertEq(token.balanceOf(0x075c299cf3b9FCF7C9fD5272cd2ed21A4688bEeD), 100);
    assertEq(token.balanceOf(address(this)), 0);
    }
    function test_Approve() public {
    token.mint(address(this), 100);
    token.approve(0x075c299cf3b9FCF7C9fD5272cd2ed21A4688bEeD, 100);
    uint amount = token.allowance(address(this), 0x075c299cf3b9FCF7C9fD5272cd2ed21A4688bEeD);
    assertEq(amount, 100);
    vm.prank(0x075c299cf3b9FCF7C9fD5272cd2ed21A4688bEeD);
    token.transferFrom(address(this), 0x075c299cf3b9FCF7C9fD5272cd2ed21A4688bEeD, 100);
    assertEq(token.balanceOf(address(this)), 0);
    assertEq(token.balanceOf(0x075c299cf3b9FCF7C9fD5272cd2ed21A4688bEeD), 100);
    }
    }

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
  • Solution

    function testFail_Mint() public {
    assertEq(token.balanceOf(address(this)), 200);
    token.mint(0x075c299cf3b9FCF7C9fD5272cd2ed21A4688bEeD, 100);
    assertEq(token.balanceOf(0x075c299cf3b9FCF7C9fD5272cd2ed21A4688bEeD), 200);
    }
    function testFail_Transfer() public {
    token.transfer(0x075c299cf3b9FCF7C9fD5272cd2ed21A4688bEeD, 100);
    }
    function testFail_Allowance() public {
    token.mint(address(this), 100);
    vm.prank(0x075c299cf3b9FCF7C9fD5272cd2ed21A4688bEeD);
    token.transferFrom(address(this), 0x075c299cf3b9FCF7C9fD5272cd2ed21A4688bEeD, 100);
    assertEq(token.balanceOf(address(this)), 0);
    assertEq(token.balanceOf(0x075c299cf3b9FCF7C9fD5272cd2ed21A4688bEeD), 100);
    }

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);
}
}