├── README.md ├── .gitignore ├── hardhat.config.js ├── package.json ├── contracts ├── v2 │ ├── ColabBankV2.sol │ └── observations.md ├── Lock.sol └── ColabBank.sol ├── test ├── ColabBankV2.test.js ├── Lock.js └── ColabBank.test.js └── scripts └── deploy.js /README.md: -------------------------------------------------------------------------------- 1 | # Intro to Smart Contract Testing 2 | 3 | 4 | To test, run `npm run test` 5 | 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain 6 | typechain-types 7 | 8 | #Hardhat files 9 | cache 10 | artifacts 11 | 12 | -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("@nomicfoundation/hardhat-toolbox"); 2 | 3 | /** @type import('hardhat/config').HardhatUserConfig */ 4 | module.exports = { 5 | solidity: "0.8.17", 6 | }; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hardhat-project", 3 | "devDependencies": { 4 | "@ethersproject/providers": "^5.7.2", 5 | "@nomicfoundation/hardhat-chai-matchers": "^1.0.5", 6 | "@nomicfoundation/hardhat-network-helpers": "^1.0.7", 7 | "@nomicfoundation/hardhat-toolbox": "^2.0.0", 8 | "@nomiclabs/hardhat-ethers": "^2.2.1", 9 | "@nomiclabs/hardhat-etherscan": "^3.1.3", 10 | "@typechain/ethers-v5": "^10.1.1", 11 | "@typechain/hardhat": "^6.1.4", 12 | "@types/chai": "^4.3.4", 13 | "@types/mocha": "^9.1.1", 14 | "chai": "^4.3.7", 15 | "ethers": "^5.7.2", 16 | "hardhat": "^2.12.2", 17 | "hardhat-gas-reporter": "^1.0.9", 18 | "solidity-coverage": "^0.8.2", 19 | "ts-node": "^10.9.1", 20 | "typechain": "^8.1.1", 21 | "typescript": "^4.9.3" 22 | }, 23 | "scripts": { 24 | "test": "npx hardhat test" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/v2/ColabBankV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.9; 4 | 5 | contract ColabBankV2 { 6 | 7 | mapping(address => uint256) public balances; 8 | address payable public owner; 9 | 10 | event Deposit(uint256 amount, uint256 when, address caller); 11 | event Withdrawal(uint256 amount, uint256 when); 12 | 13 | constructor() payable { 14 | owner = payable(msg.sender); 15 | } 16 | 17 | // spot the vulnerability 18 | function deposit() public payable { 19 | require(msg.value != 0, "cannot deposit 0 ETH"); 20 | uint256 value = msg.value; 21 | balances[msg.sender] = value; 22 | emit Deposit(value, block.timestamp, msg.sender); 23 | } 24 | 25 | // spot the vulnerability in this function 26 | function withdraw() public { 27 | require(balances[msg.sender] != 0, "you have 0 ETH deposit"); 28 | uint256 amount = balances[msg.sender]; 29 | payable(msg.sender).transfer(address(this).balance); 30 | emit Withdrawal(amount, block.timestamp); 31 | } 32 | 33 | receive() external payable {} 34 | } 35 | -------------------------------------------------------------------------------- /test/ColabBankV2.test.js: -------------------------------------------------------------------------------- 1 | const { 2 | time, 3 | loadFixture, 4 | } = require("@nomicfoundation/hardhat-network-helpers"); 5 | const { anyValue } = require("@nomicfoundation/hardhat-chai-matchers/withArgs"); 6 | const { expect } = require("chai"); 7 | const { ethers } = require("hardhat"); 8 | 9 | 10 | describe("ColabBank Test Suite", async () => { 11 | 12 | 13 | async function deployOneYearLockFixture() { 14 | const [owner, addr1, addr2, addr3, addr4] = await ethers.getSigners(); 15 | const ColabBankV2 = await ethers.getContractFactory("ColabBankV2"); 16 | const colabBankV2 = await ColabBankV2.deploy(); 17 | 18 | 19 | return { owner, addr1, addr2, addr3, addr4, colabBankV2 }; 20 | } 21 | 22 | 23 | describe("Deposit", async () => { 24 | it.only("should successfully deposit", async () => { 25 | const { colabBankV2, addr1 } = await loadFixture(deployOneYearLockFixture); 26 | 27 | }) 28 | }) 29 | 30 | describe("Withdrawal", async () => { 31 | // write PoC for the vulnerability 32 | it("", async () => { 33 | 34 | }) 35 | }) 36 | 37 | }) -------------------------------------------------------------------------------- /contracts/Lock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.9; 3 | // pragma solidity >=0.4.0 <0.9.0; 4 | 5 | // Uncomment this line to use console.log 6 | // import "hardhat/console.sol"; 7 | 8 | contract Lock { 9 | uint public unlockTime; 10 | address payable public owner; 11 | 12 | event Withdrawal(uint amount, uint when); 13 | 14 | constructor(uint _unlockTime) payable { 15 | require( 16 | block.timestamp < _unlockTime, 17 | "Unlock time should be in the future" 18 | ); 19 | 20 | unlockTime = _unlockTime; 21 | owner = payable(msg.sender); 22 | } 23 | 24 | function withdraw() public { 25 | // Uncomment this line, and the import of "hardhat/console.sol", to print a log in your terminal 26 | // console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp); 27 | 28 | require(block.timestamp >= unlockTime, "You can't withdraw yet"); 29 | require(msg.sender == owner, "You aren't the owner"); 30 | 31 | emit Withdrawal(address(this).balance, block.timestamp); 32 | 33 | owner.transfer(address(this).balance); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /scripts/deploy.js: -------------------------------------------------------------------------------- 1 | // We require the Hardhat Runtime Environment explicitly here. This is optional 2 | // but useful for running the script in a standalone fashion through `node