forked from secureum/AMAZEX-DSS-PARIS
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathYieldPool.sol
188 lines (163 loc) · 6.68 KB
/
YieldPool.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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import {IERC20, ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC3156FlashLender, IERC3156FlashBorrower} from "@openzeppelin/contracts/interfaces/IERC3156FlashLender.sol";
/**
* @title SecureumToken
*/
contract SecureumToken is ERC20("Secureum Token", "ST") {
constructor(uint256 amount) {
_mint(msg.sender, amount);
}
}
/**
* @title YieldPool
*/
contract YieldPool is ERC20("Safe Yield Pool", "syLP"), IERC3156FlashLender {
// The token address
IERC20 public immutable TOKEN;
// An arbitrary address to represent Ether
address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
// A constant to indicate a successful callback, according to ERC3156
bytes32 private constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan");
/**
* @dev Initializes the pool with the token address
* @param _token The address of the token contract
*/
constructor(IERC20 _token) {
TOKEN = _token;
}
//////// ERC3156 interface functions
/// @inheritdoc IERC3156FlashLender
function maxFlashLoan(address token) public view returns (uint256) {
if (token == ETH) {
return address(this).balance;
} else if (token == address(TOKEN)) {
return getReserve();
}
revert("Unknown token");
}
/**
* @notice The fee is 1%
* @inheritdoc IERC3156FlashLender
*/
function flashFee(address, uint256 amount) public pure returns (uint256) {
return amount / 100;
}
/// @inheritdoc IERC3156FlashLender
function flashLoan(IERC3156FlashBorrower receiver, address token, uint256 amount, bytes calldata data)
external
returns (bool)
{
require(amount <= maxFlashLoan(token), "not enough currency");
uint256 expected;
if (token == ETH) {
expected = address(this).balance + flashFee(token, amount);
(bool success,) = address(receiver).call{value: amount}("");
require(success, "ETH transfer failed");
success = false;
} else if (token == address(TOKEN)) {
expected = getReserve() + flashFee(token, amount);
require(TOKEN.transfer(address(receiver), amount), "Token transfer failed");
} else {
revert("Wrong token");
}
require(
receiver.onFlashLoan(msg.sender, token, amount, flashFee(token, amount), data) == CALLBACK_SUCCESS,
"Invalid callback return value"
);
if (token == ETH) {
require(address(this).balance >= expected, "Flash loan not repayed");
}
else {
require(getReserve() >= expected, "Flash loan not repayed");
}
return true;
}
// custom functions
/**
* @dev Preview the amount of TOKEN in the liquidity pool
* @return Amount of TOKEN in the protocol
*/
function getReserve() public view returns (uint256) {
return TOKEN.balanceOf(address(this));
}
/**
* @dev Add liquidity, which allows earning fees
* @param _amount The (maximum) amount of TOKEN that shall be provided as liquidity
* @notice The actual amount of transferred TOKEN is based on the amount of ETH sent along
* @return Amount of liquidity tokens which represent the users share of the pool
*/
function addLiquidity(uint256 _amount) public payable returns (uint256) {
uint256 liquidity;
uint256 ethBalance = address(this).balance;
uint256 tokenReserve = getReserve();
if (tokenReserve == 0) {
TOKEN.transferFrom(msg.sender, address(this), _amount);
liquidity = ethBalance;
_mint(msg.sender, liquidity);
} else {
uint256 ethReserve = ethBalance - msg.value;
uint256 tokenAmount = (msg.value * tokenReserve) / (ethReserve);
require(_amount >= tokenAmount, "Amount of tokens sent is less than the minimum tokens required");
TOKEN.transferFrom(msg.sender, address(this), tokenAmount);
liquidity = (totalSupply() * msg.value) / ethReserve;
_mint(msg.sender, liquidity);
}
return liquidity;
}
/**
* @dev Removes liquidity which has been provided before
* @param _amount Amount of liquidity tokens to be turned in
* @return Amount of (ETH, TOKEN) which have been returned
*/
function removeLiquidity(uint256 _amount) public returns (uint256, uint256) {
require(_amount > 0, "_amount should be greater than zero");
uint256 ethReserve = address(this).balance;
uint256 _totalSupply = totalSupply();
uint256 ethAmount = (ethReserve * _amount) / _totalSupply;
uint256 tokenAmount = (getReserve() * _amount) / _totalSupply;
_burn(msg.sender, _amount);
payable(msg.sender).transfer(ethAmount);
TOKEN.transfer(msg.sender, tokenAmount);
return (ethAmount, tokenAmount);
}
/**
* @dev Calculates the swap output amount based on reserves. Used to preview amount of TOKEN or ETH to be bought before execution
* @param _inputAmount Amount of input tokens (which should be sold)
* @param _inputReserve Amount of input reserves in the protocol
* @param _outputReserve Amount of output reserves in the protocol
* @return Amount of output tokens (which would be bought)
*/
function getAmountOfTokens(uint256 _inputAmount, uint256 _inputReserve, uint256 _outputReserve)
public
pure
returns (uint256)
{
require(_inputReserve > 0 && _outputReserve > 0, "invalid reserves");
uint256 inputAmountWithFee = _inputAmount * 99;
uint256 numerator = inputAmountWithFee * _outputReserve;
uint256 denominator = (_inputReserve * 100) + inputAmountWithFee;
return numerator / denominator;
}
/**
* @dev Swap ETH to TOKEN
* @notice Provided ETH will be sold for TOKEN
*/
function ethToToken() public payable {
uint256 tokenReserve = getReserve();
uint256 tokensBought = getAmountOfTokens(msg.value, address(this).balance - msg.value, tokenReserve);
TOKEN.transfer(msg.sender, tokensBought);
}
/**
* @dev Swap TOKEN to ETH
* @param _tokensSold The amount of TOKEN that should be sold
*/
function tokenToEth(uint256 _tokensSold) public {
uint256 tokenReserve = getReserve();
uint256 ethBought = getAmountOfTokens(_tokensSold, tokenReserve, address(this).balance);
TOKEN.transferFrom(msg.sender, address(this), _tokensSold);
payable(msg.sender).transfer(ethBought);
}
receive() external payable {}
}