-
Notifications
You must be signed in to change notification settings - Fork 28
/
05-readOnly.sol
152 lines (125 loc) · 4.75 KB
/
05-readOnly.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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol";
interface IERC20BurnableMintable {
function mint(address to, uint256 amount) external;
function burn(address from, uint256 amount) external;
function balanceOf(address account) external view returns (uint256);
function totalSupply() external view returns (uint256);
}
/*
* @notice I quickly simplified an implementation from a random GH repo, do not trust this implementation
*/
contract VulnerableLiquidityPool is ReentrancyGuard {
IERC20 public immutable stEth;
IERC20BurnableMintable public immutable lpToken;
constructor(address steth_token, address lp_token) {
stEth = IERC20(steth_token);
lpToken = IERC20BurnableMintable(lp_token);
}
function addLiquidity(uint256 stEth_amount, uint256 eth_amount)
external
payable
nonReentrant()
returns (uint256)
{
require(
stEth.transferFrom(msg.sender, address(this), stEth_amount),
"stEth Transfer Failed"
);
require(
msg.value == eth_amount,
"Insufficient Eth transfer"
);
/*
Check if the ratio of tokens supplied is proportional
to reserve ratio to satisfy x * y = k for price to not
change if both reserves are greater than 0
*/
uint256 stEth_reserve = stEth.balanceOf(address(this)) - stEth_amount;
uint256 eth_reserve = address(this).balance - eth_amount;
if (stEth_reserve > 0 || eth_reserve > 0) {
require(
eth_amount * stEth_reserve == stEth_amount * eth_reserve,
"Unbalanced Liquidity Provided"
);
}
/*
Calculate number of liquidity shares to mint using
the geometric mean as a measure of liquidity. Increase
in liquidity is proportional to increase in shares
minted.
> S = (dx / x) * TL
> S = (dy / y) * TL
NOTE: Amount of liquidity shares minted formula is similar
to Uniswap V2 formula. For minting liquidity shares, we take
the minimum of the two values calculated to incentivize depositing
balanced liquidity.
*/
uint256 totalLiquidity = lpToken.totalSupply();
uint256 lp_amount;
if (totalLiquidity == 0) {
lp_amount = sqrt(stEth_amount * eth_amount);
} else {
lp_amount = min(
((stEth_amount * totalLiquidity) / stEth_reserve),
((eth_amount * totalLiquidity) / eth_reserve)
);
}
require(lp_amount > 0, "No Liquidity Shares Minted");
// Mint shares to user
lpToken.mint(msg.sender, lp_amount);
return lp_amount;
}
function removeLiquidity(uint256 lp_amount)
external
nonReentrant()
returns (uint256, uint256)
{
require(
lpToken.balanceOf(msg.sender) >= lp_amount,
"Insufficient liquidity shares"
);
// Get balance of both tokens
uint256 stEth_balance = stEth.balanceOf(address(this));
uint256 eth_balance = address(this).balance;
uint256 totalLiquidity = lpToken.totalSupply();
uint256 stEth_amount = (lp_amount * stEth_balance) / totalLiquidity;
uint256 eth_amount = (lp_amount * eth_balance) / totalLiquidity;
require(
stEth_amount > 0 && eth_amount > 0,
"Insufficient transfer amounts"
);
// Burn user liquidity shares
lpToken.burn(msg.sender, lp_amount);
// Transfer tokens to user
(bool success, ) = payable(msg.sender).call{value: eth_amount}("");
require(success, "Transfer failed");
stEth.transfer(msg.sender, stEth_amount);
return (stEth_amount, eth_amount);
}
// Internal function to square root a value from Uniswap V2
function sqrt(uint256 y) internal pure returns (uint256 z) {
if (y > 3) {
z = y;
uint256 x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
// Internal function to find minimum value from Uniswap V2
function min(uint256 x, uint256 y) internal pure returns (uint256) {
return x < y ? x : y;
}
function getSpotPriceStEth(uint256 amount) public view returns (uint256) {
return amount * address(this).balance / stEth.balanceOf(address(this));
}
function getSpotPriceEth(uint256 amount) public view returns (uint256) {
return amount * stEth.balanceOf(address(this)) / address(this).balance;
}
}