diff --git a/contracts/BlockToken.sol b/contracts/BlockToken.sol new file mode 100644 index 0000000..201c74e --- /dev/null +++ b/contracts/BlockToken.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract BlockToken is ERC20 { + address public owner; + + modifier onlyOwner() { + require(msg.sender == owner, "BlockToken:: Unauthorized User"); + _; + } + + modifier notAmount0(uint256 _amount) { + require(_amount != 0, "BlockToken:: Zero amount not supported"); + _; + } + constructor( + string memory _name, + string memory _symbol, + address _owner + ) ERC20(_name, _symbol) { + require( + _owner != address(0), + "BlockToken:: Zero address not supported" + ); + owner = _owner; + } + + function mint( + uint256 _amount, + address _recepient + ) external onlyOwner notAmount0(_amount) { + _mint(_recepient, _amount); + } + + function burn(uint256 _amount) external notAmount0(_amount) { + _burn(msg.sender, _amount); + } + + function burnFrom( + address _user, + uint256 _amount + ) external onlyOwner notAmount0(_amount) { + _burn(_user, _amount); + } + + function transfertk( + address _to, + uint256 _amount + ) external notAmount0(_amount) { + _transfer(msg.sender, _to, _amount); + } + + function transferFromtk( + address _from, + address _to, + uint256 _amount + ) external notAmount0(_amount) { + _transfer(_from, _to, _amount); + } + + function approveTxn( + address _spender, + uint256 _amount + ) external notAmount0(_amount) { + _approve(msg.sender, _spender, _amount); + } +} diff --git a/test/BlockToken.js b/test/BlockToken.js new file mode 100644 index 0000000..ed284fd --- /dev/null +++ b/test/BlockToken.js @@ -0,0 +1,235 @@ + +const { + loadFixture, +} = require("@nomicfoundation/hardhat-toolbox/network-helpers"); +// const { anyValue } = require("@nomicfoundation/hardhat-chai-matchers/withArgs"); +const { expect } = require("chai"); + +// util functon +const deployBlockToken = async () => { + // target the BlockToken contract within our contract folder + let name_ = "BlockToken"; + let symbol_ = "BCT"; + const [owner_, addr1, addr2] = await ethers.getSigners(); + const BlockTokenContract = await ethers.getContractFactory("BlockToken"); // target BlockToken.sol + const BlockToken = await BlockTokenContract.deploy( + name_, + symbol_, + owner_.address + ); // deploy the BlockToken contract + return { BlockToken, owner_, addr1, addr2, name_, symbol_ }; // return the deployed instance of our BlockToken contract +}; + +// BlockToken Test Suite +describe("BlockToken Test Suite", () => { + describe("Deployment", () => { + it("Should return set values upon deployment", async () => { + const { BlockToken, name_, symbol_, owner_ } = await loadFixture( + deployBlockToken + ); + expect(await BlockToken.name()).to.eq(name_); + expect(await BlockToken.symbol()).to.eq(symbol_); + expect(await BlockToken.owner()).to.eq(owner_); + }); + + it("Should revert if owner is zero address", async () => { + const BlockTokenContract = await ethers.getContractFactory("BlockToken"); + let ZeroAddress = "0x0000000000000000000000000000000000000000"; + await expect( + BlockTokenContract.deploy("hh", "tt", ZeroAddress) + ).to.be.revertedWith("BlockToken:: Zero address not supported"); + }); + }); + + describe("Minting", () => { + it("Should allow onlyOwner Mint", async () => { + const { BlockToken, owner_, addr1 } = await loadFixture(deployBlockToken); + // test owner mints successfully + await BlockToken.connect(owner_).mint(1000, addr1); + expect(await BlockToken.balanceOf(addr1)).to.eq(1000); + + // test that another user cant call successfully + let malicioustxn = BlockToken.connect(addr1).mint(1000, addr1); + await expect(malicioustxn).to.be.revertedWith( + "BlockToken:: Unauthorized User" + ); + }); + + it("Should revert if minting amount is zero", async () => { + const { BlockToken, owner_, addr1 } = await loadFixture(deployBlockToken); + await expect( + BlockToken.connect(owner_).mint(0, addr1) + ).to.be.revertedWith("BlockToken:: Zero amount not supported"); + }); + }); + + describe("Burning", () => { + describe("Burn Txn", () => { + it("Should not burn if user doesn't have tokens", async () => { + const { BlockToken, owner_, addr1 } = await loadFixture(deployBlockToken); + await expect( + BlockToken.connect(addr1).burn(1000)).to.be.revertedWithCustomError(BlockToken, "ERC20InsufficientBalance"); + }); + + it("Should Burn Tokens Successfully", async () => { + const { BlockToken, owner_, addr1 } = await loadFixture(deployBlockToken); + await BlockToken.connect(owner_).mint(1000, owner_); + expect(await BlockToken.balanceOf(owner_)).to.eq(1000); + + await BlockToken.connect(owner_).burn(100); + expect(await BlockToken.balanceOf(owner_)).to.eq(900); + }); + + it("Should revert if burning more than balance", async () => { + const { BlockToken, owner_ } = await loadFixture(deployBlockToken); + await BlockToken.connect(owner_).mint(1000, owner_); + expect(await BlockToken.connect(owner_).balanceOf(owner_)).to.eq(1000); + + await expect(BlockToken.connect(owner_).burn(2000)).to.be.revertedWithCustomError(BlockToken, "ERC20InsufficientBalance"); + }); + + it("Should revert if burning zero balance", async () => { + const { BlockToken, owner_ } = await loadFixture(deployBlockToken); + await expect(BlockToken.connect(owner_).burn(0)).to.be.revertedWith("BlockToken:: Zero amount not supported") + }) + }); + + describe("BurningFrom", () => { + it("Should Burn From User's Tokens Successfully", async () => { + const { BlockToken, owner_, addr1 } = await loadFixture(deployBlockToken); + await BlockToken.connect(owner_).mint(1000, owner_); + expect(await BlockToken.balanceOf(owner_)).to.eq(1000); + + await BlockToken.connect(owner_).transfertk(addr1, 200); + expect(await BlockToken.balanceOf(owner_)).to.eq(800); + + await BlockToken.connect(owner_).burnFrom(addr1, 50); + expect(await BlockToken.balanceOf(addr1)).to.eq(150); + }) + + it("Should revert if User burns more than balance", async () => { + const { BlockToken, owner_ } = await loadFixture(deployBlockToken); + await BlockToken.connect(owner_).mint(1000, owner_); + await expect(BlockToken.connect(owner_).burn(2000)).to.be.revertedWithCustomError(BlockToken, "ERC20InsufficientBalance") + }); + + it("Should revert if burning from address zero", async () => { + const { BlockToken, owner_ } = await loadFixture(deployBlockToken); + let ZeroAddress = "0x0000000000000000000000000000000000000000"; + await BlockToken.connect(owner_).mint(1000, owner_); + expect(await BlockToken.balanceOf(owner_)).to.eq(1000); + + await expect(BlockToken.connect(owner_).burnFrom(ZeroAddress, 50)).to.be.revertedWithCustomError(BlockToken, "ERC20InvalidSender"); + }); + + it("Should revert if burnining called by different address", async () => { + const { BlockToken, addr2, addr1 } = await loadFixture(deployBlockToken); + let malicioustxn = BlockToken.connect(addr1).burnFrom(addr2, 1000); + await expect(malicioustxn).to.be.revertedWith("BlockToken:: Unauthorized User"); + }); + }); + + }); + + describe("Transferring", () => { + describe("Transfer txn", () => { + it("Should not transfer if user doesn't have tokens", async () => { + const { BlockToken, addr1, addr2 } = await loadFixture(deployBlockToken); + await expect(BlockToken.connect(addr1).transfer(addr2, 100)).to.be.revertedWithCustomError(BlockToken, "ERC20InsufficientBalance"); + }) + + it("Should Transfer Successfully", async () => { + const { BlockToken, owner_, addr1, addr2 } = await loadFixture(deployBlockToken); + await BlockToken.connect(owner_).mint(1000, owner_); + await BlockToken.connect(owner_).transfertk(addr1, 500); + expect(await BlockToken.balanceOf(owner_)).to.eq(500); + expect(await BlockToken.balanceOf(addr1)).to.eq(500); + + await BlockToken.connect(addr1).transfertk(addr2, 300); + expect(await BlockToken.balanceOf(addr1)).to.eq(200); + expect(await BlockToken.balanceOf(addr2)).to.eq(300); + }); + + it("Should revert if tranferring to zero address", async () => { + const { BlockToken, owner_ } = await loadFixture(deployBlockToken); + let ZeroAddress = "0x0000000000000000000000000000000000000000"; + await BlockToken.connect(owner_).mint(1000, owner_); + + await expect(BlockToken.connect(owner_).transfertk(ZeroAddress, 300)).to.be.revertedWithCustomError(BlockToken, "ERC20InvalidReceiver"); + }); + + it("Should revert if tranferring more than balance", async () => { + const { BlockToken, owner_, addr1 } = await loadFixture(deployBlockToken); + await BlockToken.connect(owner_).mint(1000, owner_); + await expect(BlockToken.connect(owner_).transfertk(addr1, 2000)).to.be.revertedWithCustomError(BlockToken, "ERC20InsufficientBalance"); + }); + + }); + + describe("TransferFrom Txn", () => { + it("Should revert if owner address doesn't have tokens", async () => { + const { BlockToken, owner_, addr1, addr2 } = await loadFixture(deployBlockToken); + await BlockToken.connect(addr1).approveTxn(addr2, 1000); + await expect(BlockToken.connect(addr2).transferFromtk(addr1, owner_, 50)).to.be.revertedWithCustomError(BlockToken, "ERC20InsufficientBalance"); + }); + + it("Should revert when transfering zero tokens", async () => { + const { BlockToken, owner_, addr1, addr2 } = await loadFixture(deployBlockToken) + await BlockToken.connect(addr1).approveTxn(addr2, 1000) + await expect(BlockToken.connect(addr2).transferFromtk(addr1, owner_, 0)).to.be.revertedWith("BlockToken:: Zero amount not supported"); + }); + + it("Should revert when transfering more than balance", async () => { + const { BlockToken, owner_, addr1, addr2 } = await loadFixture(deployBlockToken); + await BlockToken.connect(owner_).mint(1000, addr1); + await BlockToken.connect(addr1).approveTxn(addr2, 3000) + await expect(BlockToken.connect(addr2).transferFromtk(addr1, owner_, 2000)).to.be.revertedWithCustomError(BlockToken, "ERC20InsufficientBalance"); + }); + + it("Should revert if transfering to address zero", async () => { + const { BlockToken, owner_, addr1, addr2 } = await loadFixture(deployBlockToken); + let ZeroAddress = "0x0000000000000000000000000000000000000000"; + await BlockToken.connect(owner_).mint(1000, addr1); + await BlockToken.connect(addr1).approveTxn(addr2, 1000) + await expect(BlockToken.connect(addr2).transferFromtk(addr1, ZeroAddress, 500)).to.be.revertedWithCustomError(BlockToken, "ERC20InvalidReceiver"); + }); + + it("Should Transfer Tokens Successfully Using TranasferFrom", async () => { + const { BlockToken, owner_, addr1, addr2 } = await loadFixture(deployBlockToken); + await BlockToken.connect(owner_).mint(1000, addr1); + await BlockToken.connect(addr1).approveTxn(addr2, 1000) + + await BlockToken.connect(addr2).transferFromtk(addr1, addr2, 400); + + expect(await BlockToken.balanceOf(addr1)).to.eq(600); + expect(await BlockToken.balanceOf(addr2)).to.eq(400); + + }); + }); + }); + + describe("Approvals", () => { + describe("Approve", () => { + it("Should revert when approving zero tokens", async () => { + const { BlockToken, owner_, addr1, addr2 } = await loadFixture(deployBlockToken); + await BlockToken.connect(owner_).mint(5000, addr1); + await expect(BlockToken.connect(addr1).approveTxn(addr2, 0)).to.be.revertedWith("BlockToken:: Zero amount not supported"); + }); + + it("Should revert if sending to address zero", async () => { + const { BlockToken, owner_, addr1, addr2 } = await loadFixture(deployBlockToken); + let ZeroAddress = "0x0000000000000000000000000000000000000000"; + + await BlockToken.connect(owner_).mint(500, addr1) + await expect(BlockToken.connect(addr1).approveTxn(ZeroAddress, 50)).to.be.revertedWithCustomError(BlockToken, "ERC20InvalidSpender"); + }); + + it("Should Approve Tokens Successfully", async () => { + const { BlockToken, owner_, addr1, addr2 } = await loadFixture(deployBlockToken); + + await BlockToken.connect(addr1).approveTxn(addr2, 300); + expect(await BlockToken.allowance(addr1, addr2)).to.eq(300); + }); + }); + }) +}); diff --git a/test/Counter.js b/test/Counter.js new file mode 100644 index 0000000..85e0f0f --- /dev/null +++ b/test/Counter.js @@ -0,0 +1,96 @@ +const {loadFixture} = require("@nomicfoundation/hardhat-toolbox/network-helpers"); +const { anyValue } = require("@nomicfoundation/hardhat-chai-matchers/withArgs"); + +const { expect } = require("chai"); + +// util deploy function + +const deployCounter = async ()=>{ + + // Contracts are deployed using the first signer/account by default + // const [owner, otherAccount] = await ethers.getSigners(); + + const CounterContract = await ethers.getContractFactory("Counter"); + const counter = await CounterContract.deploy(); //deploy the contract + + return counter; //return the deployed instances of our counter contract +} + +// +describe("counter test suit", ()=>{ + describe("Deployment", ()=>{ + it("Should return default values upon deployment", async()=>{ + const counter = await loadFixture(deployCounter); //load the fixture to deploy the contract + expect(await counter.getCount()).to.equals(0); // assert that counter = 0 upon deployment + // console.log("Counter here: _____", counter); + }) + }) + + describe("Transactions", ()=>{ + + describe("Set count", ()=>{ + it("Should set appropriate count value", async()=>{ + const counter = await loadFixture(deployCounter); //extract deployed contract instance + + let count1 = await counter.getCount(); + expect(await count1).to.eq(0);// assert that counter = 0 upon deployment + await counter.setCount(10); + + let count2 = await counter.getCount(); + expect(count2).to.eq(10); + + }) + + it("Should set appropriate values for multiples set count txns", async()=>{ + const counter = await loadFixture(deployCounter); + + let count3 = await counter.getCount(); + expect(count3).to.eq(0); //asset count value is 0 + await counter.setCount(5); //set count value = 5 + + expect(await counter.getCount()).to.eq(5); + await counter.setCount(10); + + expect(await counter.getCount()).to.eq(10); + await counter.setCount(30); + + expect(await counter.getCount()).to.eq(30); + + + }) + }) + describe("Increase count by one ", ()=>{ + it("Should set appropriately increase count value by one", async()=>{ + const counter = await loadFixture(deployCounter); //extract deployed contract instance + + let count = await counter.getCount(); + expect(count).to.eq(0); + + await counter.increaseCountByOne(); + expect(await counter.getCount()).to.eq(1); + }) + + it("Should set appropriately increase count value by one multiple txns", async()=>{ + const counter = await loadFixture(deployCounter); //extract deployed contract instance + + let count = await counter.getCount(); + expect(count).to.eq(0); + await counter.increaseCountByOne(); + + expect(await counter.getCount()).to.eq(1); + await counter.increaseCountByOne(); + + expect(await counter.getCount()).to.eq(2); + await counter.increaseCountByOne(); + + expect(await counter.getCount()).to.eq(3); + await counter.increaseCountByOne(); + + expect(await counter.getCount()).to.eq(4); + + + }) + }) + +}) +}) \ No newline at end of file