Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,4 @@

This repository contains a collection of Solidity examples that students can use to learn about smart contracts and blockchain development.

## Examples

This repository contains the following Solidity examples:

- [Storage.sol](./001_Storage.sol) - A simple smart contract that demonstrates various Solidity data types and modifiers.

- [Mapping.sol](./002_Mapping.sol) - A simple smart contract that demonstrates the use of the mapping data structure.
58 changes: 58 additions & 0 deletions contracts/CohortBank.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;

// Uncomment this line to use console.log
// import "hardhat/console.sol";

contract CohortBank {
// The keyword "public" makes variables
// accessible from other contracts
uint256 public unlockTime;
mapping(address => uint256) public balances;
uint256 public totalCohortBalance;

address payable public owner;
// Events allow clients to react to specific
// contract changes you declare
event Deposit(uint256 amount, uint256 when, address caller);
event Withdrawal(uint256 amount, uint256 when);

// Constructor code is only run when the contract
// is created
constructor(uint256 _unlockTime) payable {
require(
block.timestamp < _unlockTime,
"Unlock time should be in the future"
);

unlockTime = _unlockTime;
owner = payable(msg.sender);
}


// spot the error here
function deposit(uint256 amount) public payable {
// Uncomment this line, and the import of "hardhat/console.sol", to print a log in your terminal
// console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp);
require(amount > 0, "cannot deposit 0 amount");
balances[msg.sender] += amount;
totalCohortBalance += amount;

emit Deposit(amount, block.timestamp, msg.sender);
}

// buggy withdraw function
// spot this bug
// add your findings to findings.md
function withdraw() public {
// Uncomment this line, and the import of "hardhat/console.sol", to print a log in your terminal
// console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp);

require(block.timestamp >= unlockTime, "You can't withdraw yet");
require(msg.sender == owner, "You aren't the owner");
balances[msg.sender] = 0;

emit Withdrawal(address(this).balance, block.timestamp);
owner.transfer(address(this).balance);
}
}
4 changes: 4 additions & 0 deletions findings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# My Findings

- I added a uint amount to the deposit function
- I changed the withdraw logic to set thee balancee of msg.sender to equal 0.
144 changes: 144 additions & 0 deletions test/CohortBank.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
const {
time,
loadFixture,
} = require("@nomicfoundation/hardhat-network-helpers");
const { anyValue } = require("@nomicfoundation/hardhat-chai-matchers/withArgs");
const { expect } = require("chai");
const {ethers} = require("hardhat");
const { parseEther } = ethers.utils

const TEST_ETH = parseEther("2");

describe("CohortBank Test Suite", function(){
async function runEveryTime(){
const ONE_YEAR_IN_SECS = 365 * 24 * 60 * 60;
const ONE_GWEI = 1000000000;

const lockedAmount = ONE_GWEI;
const unlockTime = (await time.latest()) + ONE_YEAR_IN_SECS;

const [owner, addr1] = await ethers.getSigners();

const CohortBank = await ethers.getContractFactory("CohortBank");
const cohortBank = await CohortBank.deploy(unlockTime, {value: lockedAmount});

return {cohortBank, unlockTime, lockedAmount, owner, addr1};

}
// Deployment
describe("Deployment", function(){
it("Should check unlock time", async function(){
const {cohortBank, unlockTime} = await loadFixture(runEveryTime);

expect(await cohortBank.unlockTime()).to.eq(unlockTime)
});

it("Should set the right owner", async function(){
const {cohortBank, owner} = await loadFixture(runEveryTime);

expect(await cohortBank.owner()).to.eq(owner.address);
});

it("should correctly retrieve deployed ETH to CohortBank", async function(){
const { cohortBank, lockedAmount} = await loadFixture(runEveryTime);

expect(await ethers.provider.getBalance(cohortBank.address)).to.eq(lockedAmount)

});

it("should fail if unlock time is not in the future", async function(){
const latestTime = await time.latest();

const CohortBank = await ethers.getContractFactory("CohortBank");

await expect(CohortBank.deploy(latestTime, {value: 1})).to.be.revertedWith("Unlock time should be in the future")
});
});

// Deposit function
describe("Deposit", async()=>{
it("should deposit an amount that is greater than 0", async () =>{
const {cohortBank, owner} = await loadFixture(runEveryTime);

const ownerBalanceBeforeDeposit = await cohortBank.balances(owner.address);
expect (ownerBalanceBeforeDeposit).to.eq(0);
expect(cohortBank.connect(owner).deposit(0)).to.be.revertedWith("cannot deposit 0 amount")
});
});


// Deposit Event
describe("Deposit events", async ()=>{
it("should emit event on deposit", async()=>{

const{cohortBank, owner} = await loadFixture(runEveryTime)
await cohortBank.connect(owner).deposit(8);

const ownerBalanceAfterDeposit = await cohortBank.balances(owner.address);
expect(ownerBalanceAfterDeposit).to.eq(8);

const ownerDeposit = await cohortBank.connect(owner).deposit(8);
await expect(ownerDeposit).to.emit(cohortBank, "Deposit").withArgs(8, anyValue, owner.address);
});
});

// Withdrawal Function
describe("Withdrawal", function(){
describe("Validations", function(){
it("should not be able to withdraw if called too soon", async function(){
const {cohortBank} = await loadFixture(runEveryTime);

await expect(cohortBank.withdraw()).to.be.revertedWith("You can't withdraw yet");
});

it("should revert the message with right owner", async function(){
const {cohortBank, unlockTime, addr1} = await loadFixture(
runEveryTime
);

await time.increaseTo(unlockTime);
await expect(cohortBank.connect(addr1).withdraw()).to.be.revertedWith("You aren't the owner");
});

it("should not fail if unlock time has arrived and is called by the owner", async function(){
const {cohortBank, unlockTime} = await loadFixture(runEveryTime);

await time.increaseTo(unlockTime);
await expect(cohortBank.withdraw()).not.to.be.reverted;
});
});
});

// Witdrawal Event
describe("Withdrawal events", function(){
it("should emit the event on withdrawals", async function (){
const { cohortBank, unlockTime, lockedAmount} = await loadFixture(
runEveryTime
);
await time.increaseTo(unlockTime);
await expect(cohortBank.withdraw()).to.emit(cohortBank, "Withdrawal").withArgs(lockedAmount,anyValue);
});

});

// Transfer
describe("Transfer", function () {
it("Should transfer the funds to the owner", async () => {
const { cohortBank, unlockTime, owner } = await loadFixture(runEveryTime);

await cohortBank.deposit(3, {value: TEST_ETH});

const ownerBalanceBeforeWithdrawal = await cohortBank.balances(owner.address);
await time.increaseTo(unlockTime);


//withdraw balance
await cohortBank.withdraw();
const ownerBalanceAfterWithdrawal = await cohortBank.balances(owner.address);
expect(ownerBalanceAfterWithdrawal).to.be.lt(
ownerBalanceBeforeWithdrawal
);
});
});
runEveryTime()
});
Loading