-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
167 additions
and
0 deletions.
There are no files selected for viewing
11 changes: 11 additions & 0 deletions
11
Week 4 - Smart Contract Basics/Unit Test a Smart Contract/.gitignore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
node_modules | ||
.env | ||
coverage | ||
coverage.json | ||
typechain | ||
typechain-types | ||
|
||
# Hardhat files | ||
cache | ||
artifacts | ||
|
28 changes: 28 additions & 0 deletions
28
Week 4 - Smart Contract Basics/Unit Test a Smart Contract/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Smart Contract Unit Testing 🧪 | ||
|
||
This repository contains a Solidity smart contract that represents a Faucet. The Faucet contract is designed to allow users to withdraw a small amount of ETH at a time. The contract can be tested using the following tests: | ||
|
||
1. Deployment and Ownership: The first test checks whether the contract is deployed correctly and the owner is set to the correct address. | ||
2. Withdrawal Limit: The second test ensures that the contract does not allow withdrawals above 0.1 ETH at a time. This test checks the functionality of the contract's withdrawal feature. | ||
3. Faucet Destruction: The third test checks the faucet's ability to destroy the Faucet contract when the destroyFaucet function is called by the owner. | ||
4. Owner Withdrawal: The fourth and final test checks whether the contract only allows the owner to withdraw all funds. This test checks the security of the contract and ensures that only the owner can access the funds. | ||
|
||
## Getting Started 🚀 | ||
|
||
To run the tests for the Faucet contract, you will need to set up a local environment with the following: | ||
|
||
1. Solidity Compiler: Solidity is a contract-oriented programming language for writing smart contracts. You will need a Solidity compiler to compile the smart contract. | ||
2. Chai: Chai is a JavaScript assertion library that allows you to write expressive and readable tests. | ||
|
||
Once you have installed these dependencies, you can run the tests using the following steps: | ||
|
||
1. Clone the repository. | ||
2. Change into the project directory: cd... | ||
3. Install the dependencies: npm install. | ||
4. Finally, run the tests: npx hardhat test. | ||
|
||
## Conclusion 📚 | ||
|
||
The tests outlined above cover the essential functionalities of the Faucet smart contract, including deployment, withdrawal limits, destruction, and owner withdrawal. By running these tests, you can ensure that the contract behaves as expected and that there are no critical security vulnerabilities. Using Chai, you can write expressive and readable tests to verify the contract's functionality. | ||
|
||
There are many more cases that you can test for to create really iron-clad and comprehensive unit tests - and thus create iron-clad smart contracts! 💪 The testing rabbit hole is particularly great for anyone looking to get a solid foundation in smart contract security, lots of testing there for sure! Good luck, smart contract tester! 🫡 |
31 changes: 31 additions & 0 deletions
31
Week 4 - Smart Contract Basics/Unit Test a Smart Contract/contracts/Faucet.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.19; | ||
|
||
contract Faucet { | ||
address payable public owner; | ||
|
||
constructor() payable { | ||
owner = payable(msg.sender); | ||
} | ||
|
||
function withdraw(uint _amount) public payable { | ||
// users can only withdraw .1 ETH at a time, feel free to change this! | ||
require(_amount <= 100000000000000000); | ||
(bool sent, ) = payable(msg.sender).call{value: _amount}(""); | ||
require(sent, "Failed to send Ether"); | ||
} | ||
|
||
function withdrawAll() public onlyOwner { | ||
(bool sent, ) = owner.call{value: address(this).balance}(""); | ||
require(sent, "Failed to send Ether"); | ||
} | ||
|
||
function destroyFaucet() public onlyOwner { | ||
selfdestruct(owner); | ||
} | ||
|
||
modifier onlyOwner() { | ||
require(msg.sender == owner); | ||
_; | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
Week 4 - Smart Contract Basics/Unit Test a Smart Contract/hardhat.config.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
require("@nomicfoundation/hardhat-toolbox"); | ||
|
||
/** @type import('hardhat/config').HardhatUserConfig */ | ||
module.exports = { | ||
solidity: "0.8.19", | ||
}; |
16 changes: 16 additions & 0 deletions
16
Week 4 - Smart Contract Basics/Unit Test a Smart Contract/package.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"name": "unit-test-a-smart-contract", | ||
"version": "1.0.0", | ||
"description": "", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"keywords": [], | ||
"author": "", | ||
"license": "ISC", | ||
"devDependencies": { | ||
"@nomicfoundation/hardhat-toolbox": "^2.0.2", | ||
"hardhat": "^2.13.0" | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
Week 4 - Smart Contract Basics/Unit Test a Smart Contract/scripts/deploy.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// We require the Hardhat Runtime Environment explicitly here. This is optional | ||
// but useful for running the script in a standalone fashion through `node <script>`. | ||
// | ||
// You can also run a script with `npx hardhat run <script>`. If you do that, Hardhat | ||
// will compile your contracts, add the Hardhat Runtime Environment's members to the | ||
// global scope, and execute the script. | ||
const hre = require("hardhat"); | ||
|
||
async function main() { | ||
const currentTimestampInSeconds = Math.round(Date.now() / 1000); | ||
const unlockTime = currentTimestampInSeconds + 60; | ||
|
||
const lockedAmount = hre.ethers.utils.parseEther("0.001"); | ||
|
||
const Lock = await hre.ethers.getContractFactory("Lock"); | ||
const lock = await Lock.deploy(unlockTime, { value: lockedAmount }); | ||
|
||
await lock.deployed(); | ||
|
||
console.log( | ||
`Lock with ${ethers.utils.formatEther( | ||
lockedAmount | ||
)}ETH and unlock timestamp ${unlockTime} deployed to ${lock.address}` | ||
); | ||
} | ||
|
||
// We recommend this pattern to be able to use async/await everywhere | ||
// and properly handle errors. | ||
main().catch((error) => { | ||
console.error(error); | ||
process.exitCode = 1; | ||
}); |
43 changes: 43 additions & 0 deletions
43
Week 4 - Smart Contract Basics/Unit Test a Smart Contract/test/faucetTests.js.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); | ||
const { expect } = require('chai'); | ||
|
||
describe('Faucet', function () { | ||
// We define a fixture to reuse the same setup in every test. | ||
// We use loadFixture to run this setup once, snapshot that state, | ||
// and reset Hardhat Network to that snapshot in every test. | ||
async function deployContractAndSetVariables() { | ||
const Faucet = await ethers.getContractFactory('Faucet'); | ||
const faucet = await Faucet.deploy(); | ||
|
||
const [owner, alice] = await ethers.getSigners(); | ||
|
||
let withdrawAmount = ethers.utils.parseUnits('1', 'ether'); | ||
|
||
return { faucet, owner, alice, withdrawAmount }; | ||
} | ||
|
||
it('should deploy and set the owner correctly', async function () { | ||
const { faucet, owner } = await loadFixture(deployContractAndSetVariables); | ||
|
||
expect(await faucet.owner()).to.equal(owner.address); | ||
}); | ||
|
||
it('should not allow withdrawals above .1 ETH at a time', async function () { | ||
const { faucet, withdrawAmount } = await loadFixture( | ||
deployContractAndSetVariables | ||
); | ||
await expect(faucet.withdraw(withdrawAmount)).to.be.reverted; | ||
}); | ||
|
||
it('should destroy the faucet when the destroyFaucet function is called by the owner', async function () { | ||
const { faucet, owner } = await loadFixture(deployContractAndSetVariables); | ||
await faucet.destroyFaucet(); | ||
expect(await ethers.provider.getCode(faucet.address)).to.equal('0x'); | ||
}); | ||
|
||
it('should only allow the owner to withdraw all funds', async function () { | ||
const { faucet, owner, alice } = await loadFixture(deployContractAndSetVariables); | ||
await expect(faucet.connect(alice).withdrawAll()).to.be.reverted; | ||
await expect(faucet.connect(owner).withdrawAll()).to.be.ok; | ||
}); | ||
}); |