diff --git a/contracts/BlockToken.sol b/contracts/BlockToken.sol new file mode 100644 index 0000000..a6b045a --- /dev/null +++ b/contracts/BlockToken.sol @@ -0,0 +1,51 @@ +// 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) onlyOwner notAmount0(_amount) external { + _mint(_recepient, _amount); + } + + function burn(uint256 _amount) notAmount0(_amount) external { + _burn(msg.sender, _amount); + } + + function burnFrom(address _user, uint256 _amount)onlyOwner notAmount0(_amount) external { + _burn(_user, _amount); + } + + function transfer(address _owner, uint256 _amount) public notAmount0(_amount) override returns (bool){ + require(_owner != address(0), "BlockToken:: Zero address not supported"); + _transfer(msg.sender, _owner, _amount); + } + + // function transferFrom(address _owner, address _recepient, uint256 _amount) public notAmount0(_amount) override returns (bool){ + // require(_recepient != address(0), "BlockToken:: Zero address not supported"); + // _transfer(_owner, _recepient, _amount); + // uint256 currentAllowance = allowance(_owner, msg.sender); + // require(currentAllowance >= _amount, "BlockToken:: Transfer amount exceeds allowance"); + // _approve(_owner, msg.sender, currentAllowance - _amount); + // return true; + // } + + +} \ No newline at end of file diff --git a/contracts/Counter.sol b/contracts/Counter.sol index fa8560c..decb138 100644 --- a/contracts/Counter.sol +++ b/contracts/Counter.sol @@ -1,48 +1,30 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.28; +// // SPDX-License-Identifier: MIT -interface ICounter { - function setCount(uint256 _count) external; - function increaseCountByOne() external; - function getCount() external view returns(uint256); +// pragma solidity ^0.8.28; -} +// interface ICounter { +// function increaseCountByOne() external; +// function setCount(uint256 _count) external; -contract Counter is ICounter { - uint256 public count; +// function getCount() external view returns(uint256); +// //function resetCount() external +// } - function setCount(uint256 _count) external { - count = _count; - } - function increaseCountByOne() public { - count += 1; - } +// contract Counter is ICounter { +// uint256 public count; - function getCount() public view returns(uint256) { - return count; - } -} - - -// contract F { -// // Initializing interface IC -// IC public _ic; -// // Initializing the contract address -// address public contractCAddress; - -// constructor(address _contractCAddress) { -// // Set the contract address to the state variable contract address -// contractCAddress = _contractCAddress; -// // Passing the contract address into interface using the address instance of another contract -// _ic = IC(_contractCAddress); -// } +// function increaseCountByOne() public { +// count += 1; +// } // function setCount(uint256 _count) public { -// _ic.setCount(_count); +// count = _count; // } // function getCount() public view returns(uint256) { -// return _ic.getCount(); +// return count; // } + + // } \ No newline at end of file diff --git a/contracts/CounterV2.sol b/contracts/CounterV2.sol new file mode 100644 index 0000000..3de0b9d --- /dev/null +++ b/contracts/CounterV2.sol @@ -0,0 +1,54 @@ +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.19; + +// interface ICounterV2{ +// function setCount(uint256 _count) external; + +// function getCount() external view returns(uint256); + +// function increaseCountByOne() external; + +// function resetCount() external; + +// function decreaseCountByOne() external; +// } + +// contract CounterV2 is ICounterV2 { +// uint256 public count; +// address owner; + +// constructor(){ +// owner = msg.sender; +// } + +// function setCount(uint256 _count) public { +// require(_count > 0, "Count must be greater than 0"); +// require(msg.sender == owner, "You are unauthorised"); +// count = _count; +// } + +// function getCount() public view returns(uint256) { +// return count; +// } + +// function getOwner() public view returns(address) { +// return owner; +// } + +// function increaseCountByOne() public { +// require(msg.sender == owner, "You are unauthorised"); +// count+=1; +// } + +// function resetCount() public { +// require(count > 0,"Cannot reset value , It's already at default"); +// require(msg.sender == owner, "You are Unauthorised"); +// count = 0; +// } + +// function decreaseCountByOne() public { +// require(msg.sender == owner, "You are unauthorised"); +// count-=1; +// } +// } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 656a34f..cc2bf83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,9 @@ "packages": { "": { "name": "hardhat-project", + "dependencies": { + "@openzeppelin/contracts": "^5.4.0" + }, "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^5.0.0", "hardhat": "^2.26.1" @@ -1305,6 +1308,12 @@ "node": ">= 12" } }, + "node_modules/@openzeppelin/contracts": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.4.0.tgz", + "integrity": "sha512-eCYgWnLg6WO+X52I16TZt8uEjbtdkgLC0SUX/xnAksjjrQI4Xfn4iBRoI5j55dmlOhDv1Y7BoR3cU7e3WWhC6A==", + "license": "MIT" + }, "node_modules/@scure/base": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", diff --git a/package.json b/package.json index 0586079..a7c3ef4 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,13 @@ "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^5.0.0", "hardhat": "^2.26.1" - }, + }, "scripts": { - "test": "npx hardhat test", - "compile": "npx hardhat compile", + "test": "npx hardhat test", + "compile": "npx hardhat compile", "node": "npx hardhat node" + }, + "dependencies": { + "@openzeppelin/contracts": "^5.4.0" } } diff --git a/test/BlockToken.js b/test/BlockToken.js new file mode 100644 index 0000000..c098bae --- /dev/null +++ b/test/BlockToken.js @@ -0,0 +1,218 @@ +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", () => { + 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 allow owner to burnFrom Token Successfully ", async () => { + const { BlockToken, owner_, addr1, addr2 } = await loadFixture( + deployBlockToken + ); + await BlockToken.connect(owner_).mint(1000, addr1); + const balance = await BlockToken.balanceOf(addr1); + expect(await BlockToken.balanceOf(addr1)).to.eq(1000); + // allow owner to burn from aadr + await expect( + BlockToken.connect(addr1) + .burnFrom(addr1, 500)) + .to.be.revertedWith("BlockToken:: Unauthorized User" + ); + + await BlockToken.connect(owner_).mint(1000, addr2); + expect(await BlockToken.balanceOf(addr2)).to.eq(1000); + //Check if addr1 can burn from addr2 + await expect( + BlockToken.connect(addr1).burnFrom(addr2, 600) + ).to.be.revertedWith( + "BlockToken:: Unauthorized User" + ); + it("Should not burn zero amount", async () => { + const { BlockToken, owner_, addr1 } = await loadFixture(deployBlockToken); + expect(await BlockToken.balanceOf(owner_)).to.eq(1000); + + await expect( + BlockToken.connect(owner_).burnFrom(addr1, 0) + ).to.be.revertedWith("BlockToken:: Zero amount not supported"); + + }); + }); + }); + + + describe("Transactions", () => { + it("Should allow owner to send token from to address", async () => { + const { BlockToken, owner_, addr1, addr2 } = await loadFixture(deployBlockToken); + + const zeroAddress = "0x0000000000000000000000000000000000000000"; + //transfer to zero address + await expect( + BlockToken.connect(owner_).transfer(zeroAddress, 100)).to.be.reverted; + await BlockToken.connect(owner_).mint(1000, owner_); + expect(await BlockToken.balanceOf(owner_)).to.eq(1000); + + // transfer from owner to addr1 + await BlockToken.connect(owner_).transfer(addr1, 900); + + expect (await BlockToken.balanceOf(owner_)).to.eq(100); + expect(await BlockToken.balanceOf(addr1)).to.eq(900); + + // //transfer from addr1 to addr2 + await BlockToken.connect(addr1).transfer(addr2, 200); + // //check balance of addr1 + expect (await BlockToken.balanceOf(addr1)).to.eq(700); + // //check balance of addr2 + expect(await BlockToken.balanceOf(addr2)).to.eq(200); + }); + it("should transfer tokens less than or equal to balance", 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_).transfer(addr1, 1000); + expect (await + BlockToken.balanceOf(owner_)).to.eq(0); + expect(await BlockToken.balanceOf(addr1)).to.eq(1000); + + await expect(BlockToken.connect(owner_).transfer(addr1, 100) + ).to.be.revertedWithCustomError(BlockToken, "ERC20InsufficientBalance"); + expect(await BlockToken.balanceOf(owner_)).to.eq(0); + expect (await BlockToken.balanceOf(addr1)).to.eq(1000); + }) + + it("should approve for transferFrom Function", 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_).approve(addr1, 500); + expect(await BlockToken.allowance(owner_, addr1)).to.eq(500); + }); + + it("should transferFrom based on approve and allowance", async() => { + const {BlockToken, owner_, addr1, addr2} = await loadFixture(deployBlockToken); + await BlockToken.connect(owner_).mint(1000, addr1); + expect(await BlockToken.balanceOf(addr1)).to.eq(1000); + + await BlockToken.connect(addr1).approve(owner_, 500); + expect (await BlockToken.connect(addr1).allowance(addr1, owner_)).to.eq(500); + await BlockToken.connect(owner_).transferFrom(addr1, addr2, 400); + expect(await BlockToken.connect(addr2).balanceOf(addr2)).to.eq(400); + }); + + it("should revert if balance of 'From' is insufficient", async() => { + const {BlockToken, owner_, addr1, addr2} = await loadFixture(deployBlockToken); + await BlockToken.connect(owner_).mint(1000, addr1); + expect(await BlockToken.balanceOf(addr1)).to.eq(1000); + + await BlockToken.connect(addr1).approve(owner_, 500); + expect(await BlockToken.connect(addr1).allowance(addr1, owner_)).to.eq(500); + await expect(BlockToken.connect(owner_).transferFrom(addr1, addr2, 700) + ).to.be.revertedWithCustomError( BlockToken, "ERC20InsufficientAllowance"); + }); + + it("should revert if 'to' is address zero", async() => { + const {BlockToken, owner_, addr1, addr2} = await loadFixture(deployBlockToken); + const zeroAddress = "0x0000000000000000000000000000000000000000"; + await BlockToken.connect(owner_).mint(1000, addr1); + expect(await BlockToken.balanceOf(addr1)).to.eq(1000); + + await BlockToken.connect(addr1).approve(owner_, 500); + expect(await BlockToken.connect(addr1).allowance(addr1, owner_)).to.eq(500); + await expect(BlockToken.connect(owner_).transferFrom(addr1, zeroAddress,400) + ).to.be.revertedWithCustomError( BlockToken, "ERC20InvalidReceiver"); + }); + + it("should revert if allowance is incremented and not replaced", async () => { + const {BlockToken, owner_, addr1, addr2} = await loadFixture(deployBlockToken); + await BlockToken.connect(owner_).mint(1000, addr1); + expect(await BlockToken.balanceOf(addr1)).to.eq(1000); + + await BlockToken.connect(addr1).approve(owner_, 500); + await BlockToken.connect(addr1).approve(owner_, 700); + expect(await BlockToken.connect(addr1).allowance(addr1, owner_)).to.eq(700); + + + }); + + + + + + }); + +}); \ No newline at end of file diff --git a/test/Counter.js b/test/Counter.js index 99b2931..82cc133 100644 --- a/test/Counter.js +++ b/test/Counter.js @@ -1,49 +1,70 @@ -const {loadFixture } = require("@nomicfoundation/hardhat-toolbox/network-helpers"); -// const { anyValue } = require("@nomicfoundation/hardhat-chai-matchers/withArgs"); -const { expect } = require("chai"); - -// util functon -const deployCounter = async () => { - // target the Counter contract within our contract folder - const CounterContract = await ethers.getContractFactory("Counter"); // target Counter.sol - const counter = await CounterContract.deploy(); // deploy the Counter contract - return counter ; // return the deployed instance of our counter contract -} - -// Counter Test Suite -describe("Counter Test Suite", () => { - describe("Deployment", () => { - it("Should return default values upon deployment", async () => { - const counter = await loadFixture(deployCounter); - expect(await counter.count()).to.eq(0); // assert that count = 0 upon deployment - }) - }) - - describe("Transactions", () => { - describe("SetCount", () => { - it("Should set appropriate count values", async () => { - const counter = await loadFixture(deployCounter); // extract deployed counter instace - let count1 = await counter.getCount(); // check initial count value before txn - expect(count1).to.eq(0); - await counter.setCount(10) // assert that count = 0 upon deployment +// const {loadFixture} = require("@nomicfoundation/hardhat-toolbox/network-helpers"); +// //const {anyValue} = require("@nomicfoundation/hardhat-chai-matchers/withArgs"), +// const { expect } = require("chai"); + +// //util function +// //target the counter contract within our contract folder + +// const deployCounter = async () => { +// //contracts are deployed using first signer/account by default +// //const [owner, otherAccount] = await ethers.getSigners(); + +// const CounterContract = await ethers.getContractFactory("Counter"); //target counter.sol +// const counter = await CounterContract.deploy(); // deply the Counter contract + +// return counter ; // return the deplyed instance of our counter contract +// } +// //Counter Test Suite +// describe("Counter Test, Suite", () => { +// describe("Deployment", () => { +// it("Should return default values upon deployment", async () => { +// const counter = await loadFixture(deployCounter); +// expect(await counter.getCount()).to.eq(0); //assert that count =0 +// //console.log("counter here: ____", counter); +// }) +// }) - let count2 = await counter.getCount(); // check initial count value before txn - expect(count2).to.eq(10) // check final count = 10 - }) - - it("Should set appropriate values for multiple setCount txns", async () => { - - }) - }) - - describe("IncreaseCountByOne", () => { - it("Should set appropriate increaseCountByOne value", async () => { - - }) - - it("Should set appropriate values for multiple increaseCountByOne txns", async () => { - - }) - }) - }) -}) \ No newline at end of file +// describe("Transactions", () => { +// it("setCount", async () => { +// const counter = await loadFixture(deployCounter); //extract deployed counter insatnce + +// let count1 = await counter.getCount(); //check initial count value before txn +// expect(count1).to.eq(0); +// await counter.setCount(10); + +// let count2 = await counter.getCount(); //check initial count value before txn +// expect(count2).to.eq(10); +// //expect(await counter.setCount()).to.eq(0); +// }); +// it("Should set appropriate values for multiple setCount txns", async () => { +// const counter = await loadFixture(deployCounter); //extract deployed counter insatnce + +// let count2 = await counter.getCount(); // check initial count value before txn +// expect(count2).to.eq(0); +// await counter.setCount(20); + +// let count3 = await counter.getCount(); // check initial count value before txn +// expect(count3).to.eq(20); +// await counter.setCount(30); +// let count4 = await counter.getCount(); +// expect (count4).to.eq(30); +// await counter.setCount(50); +// }) +// }); + +// describe("IncreaseCountByOne", () => { +// it("Should set appropriate increaseCountByOne value", async () => { +// const counter = await loadFixture(deployCounter); //extract deployed counter insatnce +// await counter.increaseCountByOne(); +// expect(await counter.getCount()).to.eq(1); +// }); + +// it("Should set appropriate values for multiple increaseCountByOne txns", async () => { +// const counter = await loadFixture(deployCounter); //extract deployed counter insatnce +// await counter.increaseCountByOne(); +// await counter.increaseCountByOne(); +// await counter.increaseCountByOne(); +// expect(await counter.getCount()).to.eq(3); +// }); +// }); +// }); \ No newline at end of file diff --git a/test/CounterV2.js b/test/CounterV2.js new file mode 100644 index 0000000..f0a7df0 --- /dev/null +++ b/test/CounterV2.js @@ -0,0 +1,109 @@ +// const { +// loadFixture, +// } = require("@nomicfoundation/hardhat-toolbox/network-helpers"); +// const { expect } = require("chai"); + +// describe("CounterV2 Contract Test Suite", function () { +// async function deployCounterV2Fixture() { +// const [owner, otherAccount] = await ethers.getSigners(); +// const CounterV2 = await ethers.getContractFactory("CounterV2"); +// const counter = await CounterV2.deploy(); +// return { counter, owner, otherAccount }; +// } + +// describe("Deployment", function () { +// it("Should set initial count to 0", async function () { +// const { counter, owner } = await loadFixture(deployCounterV2Fixture); +// expect(await counter.getCount()).to.equal(0); +// expect(await counter.getOwner()).to.equal(owner.address); +// }); +// }); + +// describe("setCount()", function () { +// it("Should allow owner to set count > 0", async function () { +// const { counter, owner } = await loadFixture(deployCounterV2Fixture); +// await counter.setCount(5); +// expect(await counter.getCount()).to.equal(5); +// }); + +// it("Should revert if count is 0", async function () { +// const { counter } = await loadFixture(deployCounterV2Fixture); +// await expect(counter.setCount(0)).to.be.revertedWith( +// "Count must be greater than 0" +// ); +// }); + +// it("Should revert if called by non-owner", async function () { +// const { counter, otherAccount } = await loadFixture( +// deployCounterV2Fixture +// ); +// await expect( +// counter.connect(otherAccount).setCount(10) +// ).to.be.revertedWith("You are unauthorised"); +// }); +// }); + +// describe("increaseCountByOne()", function () { +// it("Should increase count by 1 if called by owner", async function () { +// const { counter } = await loadFixture(deployCounterV2Fixture); +// await counter.setCount(2); +// await counter.increaseCountByOne(); +// expect(await counter.getCount()).to.equal(3); +// }); + +// it("Should revert if called by non-owner", async function () { +// const { counter, otherAccount } = await loadFixture( +// deployCounterV2Fixture +// ); +// await counter.setCount(1); +// await expect( +// counter.connect(otherAccount).increaseCountByOne() +// ).to.be.revertedWith("You are unauthorised"); +// }); +// }); + +// describe("resetCount()", function () { +// it("Should reset count to 0 if count > 0", async function () { +// const { counter } = await loadFixture(deployCounterV2Fixture); +// await counter.setCount(10); +// await counter.resetCount(); +// expect(await counter.getCount()).to.equal(0); +// }); + +// it("Should revert if count is already 0", async function () { +// const { counter } = await loadFixture(deployCounterV2Fixture); +// await expect(counter.resetCount()).to.be.revertedWith( +// "Cannot reset value , It's already at default" +// ); +// }); + +// it("Should revert if called by non-owner", async function () { +// const { counter, otherAccount } = await loadFixture( +// deployCounterV2Fixture +// ); +// await counter.setCount(5); +// await expect( +// counter.connect(otherAccount).resetCount() +// ).to.be.revertedWith("You are Unauthorised"); +// }); +// }); + +// describe("decreaseCountByOne()", function () { +// it("Should decrease count by 1 if called by owner", async function () { +// const { counter } = await loadFixture(deployCounterV2Fixture); +// await counter.setCount(10); +// await counter.decreaseCountByOne(); +// expect(await counter.getCount()).to.equal(9); +// }); + +// it("Should revert if called by non-owner", async function () { +// const { counter, otherAccount } = await loadFixture( +// deployCounterV2Fixture +// ); +// await counter.setCount(10); +// await expect( +// counter.connect(otherAccount).decreaseCountByOne() +// ).to.be.revertedWith("You are unauthorised"); +// }); +// }); +// });