-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patheverything.sol
More file actions
416 lines (311 loc) · 14.8 KB
/
everything.sol
File metadata and controls
416 lines (311 loc) · 14.8 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
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
// SPDX-License-Identifier: Unlicensed
pragma solidity =0.6.12;
import "./SpiritRouter.sol";
import "./SpiritMasterChef.sol";
import "./itarot.sol";
import "./FixedPoint.sol";
interface ERC20Interface {
function balanceOf(address user) external view returns (uint256);
function burnFrom(address account, uint256 amount) external;
}
library SafeToken {
function myBalance(address token) internal view returns (uint256) {
return ERC20Interface(token).balanceOf(address(this));
}
function balanceOf(address token, address user) internal view returns (uint256) {
return ERC20Interface(token).balanceOf(user);
}
function safeApprove(address token, address to, uint256 value) internal {
// bytes4(keccak256(bytes('approve(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeApprove");
}
function safeTransfer(address token, address to, uint256 value) internal {
// bytes4(keccak256(bytes('transfer(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeTransfer");
}
function safeTransferFrom(address token, address from, address to, uint256 value) internal {
// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeTransferFrom");
}
}
contract MultiVault {
using SafeToken for address;
using SafeMath for uint256;
address public owner;
mapping(address => uint256) public coolDowns; //cd
uint coolDownTime;//cd
IPancakePair[] public allPairs;
uint[] public pids;
SpiritRouter public router;
uint256 public justGot;
uint256 public justGotComp;
SpiritMasterChef public chef;
uint256 public sharePriceFTM;
IERC20 share;
ITarotPriceOracle lpOracle;
uint256 public lastTVL;
bool firstShare = true;
uint256 public minRatio;
/// @notice Only the owner can withdraw from this contract
modifier onlyOwner() {
require(msg.sender == owner || msg.sender == address(this));
_;
}
struct PoolInfo {
IBEP20 lpToken; // Address of LP token contract.
uint256 allocPoint; // How many allocation points assigned to this pool. SPIRITs to distribute per block.
uint256 lastRewardBlock; // Last block number that SPIRITs distribution occurs.
uint256 accSpiritPerShare; // Accumulated SPIRITs per share, times 1e12. See below.
uint16 depositFeeBP; // Deposit fee in basis points
}
struct UserInfo {
uint256 amount; // How many LP tokens the user has provided.
uint256 rewardDebt;
}
constructor() public {
//I hard code them here, but in other versions these addresses will all be replaced by correct router, chef, oracle, LP tokens, and pids as needed
router = SpiritRouter(0x16327E3FbDaCA3bcF7E38F5Af2599D2DDc33aE52);
chef = SpiritMasterChef(0x9083EA3756BDE6Ee6f27a6e996806FBD37F6F093);
share = IERC20(address(0));
owner = address(msg.sender);
//This share price is only here for testing purposes, so the real initial share price is set on the first deposit
sharePriceFTM = 0;
minRatio = 900;
coolDownTime = 1320; //cd seconds
lpOracle = ITarotPriceOracle(0x36Df0A76a124d8b2205fA11766eC2eFF8Ce38A35);
allPairs.push(IPancakePair(address(0xF050133847bb537C7476D054B8bE6e30253Fbd05)));//tarot-ftm
allPairs.push(IPancakePair(address(0x9Fe4c0CE5F533e96C2b72d852f190961AD5a7bB3)));//sushi-ftm
allPairs.push(IPancakePair(address(0x936D23C83c2469f6a14B9f5bEaec13879598A5aC)));//ice-ftm
//allPairs.push(IPancakePair(address(0xd473Ff135305F2fC7BC82D6B6831c911eBd55ed6)));//tomb-ftm
allPairs.push(IPancakePair(address(0x2c18c39622b90318B0124eCFd6d4aC81efcC51db)));//grim-ftm
allPairs.push(IPancakePair(address(0xd14Dd3c56D9bc306322d4cEa0E1C49e9dDf045D4)));//fusdt-ftm
allPairs.push(IPancakePair(address(0xB32b31DfAfbD53E310390F641C7119b5B9Ea0488)));//mim-ftm
allPairs.push(IPancakePair(address(0xe7E90f5a767406efF87Fdad7EB07ef407922EC1D)));//ftm-usdc
allPairs.push(IPancakePair(address(0x4fc38a2735C7da1d71ccAbf6DeC235a7DA4Ec52C)));//yfi-ftm
allPairs.push(IPancakePair(address(0x374C8ACb146407Ef0AE8F82BaAFcF8f4EC1708CF)));//crv-ftm
allPairs.push(IPancakePair(address(0xd061c6586670792331E14a80f3b3Bb267189C681)));//link-ftm
allPairs.push(IPancakePair(address(0x26D583028989378Cc1b7CBC023f4Ae049d5e5899)));//any-ftm
pids.push(40);
pids.push(8);
pids.push(7);
//pids.push(56);
pids.push(54);
pids.push(17);
pids.push(30);
pids.push(4);
pids.push(13);
pids.push(10);
pids.push(11);
pids.push(18);
}
/// @notice Allow deposits from anyone
receive() external payable {
if(msg.sender != address(0x16327E3FbDaCA3bcF7E38F5Af2599D2DDc33aE52)){
justGot = msg.value;
if (firstShare) {
require(msg.value == 1e18);
share.mint(address(msg.sender), 1e19);
firstShare = false;
} else {
uint shareAmnt = uint(1e18).mul(msg.value).div(calcShare());
require(shareAmnt>0);
share.mint(address(msg.sender), shareAmnt);
}
allocate(msg.value);
coolDowns[msg.sender] = now;//cd
} else {
justGotComp = msg.value;
}
}
function calcTVL() public returns(uint256 TVL){
TVL = 0;
//compound();
for (uint i = 0; i<11; i++) {
(uint amount, ) = chef.userInfo(pids[i], address(this));
uint LPP = getLPPriceFTM(address(allPairs[i]));
TVL = TVL.add(amount.mul(LPP));
}
TVL = TVL.div(2**112);
lastTVL = TVL;
}
function calcShare() public returns(uint sharePrice){
uint newTVL = calcTVL();
sharePrice = uint(1e18).mul(newTVL).div(share.totalSupply());
sharePriceFTM = sharePrice;
}
function declareShareAddress(address newShare) external onlyOwner{
require(share == IERC20(address(0)));
share = IERC20(newShare);
}
function setMinRatio(uint _minRatio) public onlyOwner {
require(_minRatio >= 0 && _minRatio <=1000);
minRatio = _minRatio;
}
function setCoolDown(uint _cd) public onlyOwner {
require(_cd <=86400);
coolDownTime = _cd;
}
/// @notice Full withdrawal
function withdraw() external {
withdraw(share.balanceOf(address(msg.sender)));
}
/// @notice Partial withdrawal
/// @param amount Amount requested for withdrawal
function withdraw(uint256 amount) public {
require(now - coolDowns[msg.sender] >= coolDownTime, "Wait for cooldown after depositing before you can withdraw"); //cd
address[] memory path = new address[](2);
uint total = 0;
for (uint i = 0; i<11; i++) {
(uint deposited, ) =chef.userInfo(pids[i], address(this));
uint pairBal = uint(1e10).mul(deposited).mul(amount).div(share.totalSupply()).div(1e10);
chef.withdraw(pids[i], pairBal);
address notWETH = (allPairs[i].token0() == router.WETH() ? allPairs[i].token1():allPairs[i].token0());
uint amntTok = notWETH.balanceOf(address(this));
uint eth = removeLiquidityETHSupportingFeeOnTransferTokens(notWETH,address(allPairs[i]),pairBal,0,0);
total= total.add(eth);
path[0] = notWETH;
path[1] = router.WETH();
uint reth;
uint rnot;
if (allPairs[i].token0() == router.WETH()){
(reth, rnot,) = allPairs[i].getReserves();
} else {
(rnot, reth,) = allPairs[i].getReserves();
}
swapExactTokensForETHSupportingFeeOnTransferTokens((notWETH.balanceOf(address(this)).sub(amntTok)),0,path);
total = total.add(justGotComp);
}
//have to have person call approve function of shares in web3 front end
uint256 allowance = share.allowance(msg.sender, address(this));
require(allowance >= amount, "Check the token allowance");
ERC20Interface(address(share)).burnFrom(address(msg.sender), amount);
payable(msg.sender).transfer(total.mul(999).div(1000));
payable(0x4cea75f8eFC9E1621AC72ba8C2Ca5CCF0e45Bb3d).transfer(total.div(1000)); //fee wallet
}
function approveRouter(address token, uint256 amount) private {
if (IERC20(token).allowance(address(this), address(router)) >= amount) return;
token.safeApprove(address(router), uint256(-1));
}
function swapExactETHForTokensSupportingFeeOnTransferTokens(uint eth, uint amountOutMin, address[] memory path) private {
approveRouter(router.WETH(), uint256(-1));
router.swapExactETHForTokensSupportingFeeOnTransferTokens{ value : eth}(
amountOutMin,
path,
address(this),
now);
}
function swapExactTokensForETHSupportingFeeOnTransferTokens(uint amountIn, uint amountOutMin, address[] memory path) private {
approveRouter(address(path[0]), uint256(-1));
router.swapExactTokensForETHSupportingFeeOnTransferTokens(
amountIn,
amountOutMin,
path,
address(this),
now);
}
function addLiquidityETH(
address token,
uint eth,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin
) private returns (uint liquidity){
approveRouter(token, uint(-1));
approveRouter(router.WETH(), uint(-1));
(, , liquidity) = router.addLiquidityETH{value: eth}(
token,
amountTokenDesired,
amountTokenMin,
amountETHMin,
address(this),
now
);
}
function removeLiquidityETHSupportingFeeOnTransferTokens(
address token,
address lptoken,
uint liquidity,
uint amountTokenMin,
uint amountETHMin
) private returns (uint amountETH){
approveRouter(lptoken, liquidity);
amountETH = router.removeLiquidityETHSupportingFeeOnTransferTokens(
token,
liquidity,
amountTokenMin,
amountETHMin,
address(this),
now);
}
function compound() external {
for (uint i = 0; i<11; i++) {
chef.withdraw(pids[i], 0);
}
address[] memory path = new address[](2);
path[0] = address(0x5Cc61A78F164885776AA610fb0FE1257df78E59B);
path[1] = router.WETH();
(uint px1,) = lpOracle.getResult(address(0x30748322B6E34545DBe0788C421886AEB5297789));
require(px1 != 1);
px1 = uint(0x100000000000000000000000000000000000000000000000000000000).div(px1);
//px1 is now always FTM per SPIRIT
uint minAmnt = (minRatio.mul(px1).mul(IERC20(address(0x5Cc61A78F164885776AA610fb0FE1257df78E59B)).balanceOf(address(this)))).div((uint(2**112)).mul(uint(1000)).mul(1e18));
swapExactTokensForETHSupportingFeeOnTransferTokens(IERC20(address(0x5Cc61A78F164885776AA610fb0FE1257df78E59B)).balanceOf(address(this)),minAmnt,path);
allocate(justGotComp.mul(955).div(1000));
payable(0x4cea75f8eFC9E1621AC72ba8C2Ca5CCF0e45Bb3d).transfer(justGotComp.mul(45).div(1000)); //fee wallet address
}
function getLPPriceFTM(address pair) private returns (uint LPP) {
//address token0 = IPancakePair(pair).token0();
address token1 = IPancakePair(pair).token1();
uint px0;
uint px1;
uint totalSupply = IPancakePair(pair).totalSupply();
(uint r0, uint r1, ) = IPancakePair(pair).getReserves();
uint sqrtK = HomoraMath.fdiv(Babylonian2.sqrt(r0.mul(r1)),(totalSupply));
(px1,) = lpOracle.getResult(pair);
px0 = 2**112;
if(token1 != router.WETH()){
require(px1 != 1);
px1 = uint(0x100000000000000000000000000000000000000000000000000000000).div(px1);
}
LPP = sqrtK.mul(2).mul(Babylonian2.sqrt(px0)).mul(Babylonian2.sqrt(px1)).div(2**112);
//lastLPP = LPP;
}
//This version of the allocate function splits evenly into 4 farms, but future versions may have a different allocation
//Ex split evenly into 8 farms, or split 60-30-10 into 3 farms
function allocate(uint256 depAmnt) private {
address[] memory path = new address[](2);
uint depAmntDiv = depAmnt.div(22);
for (uint i=0; i<11; i++){
//trade to assets if necessary
//pair assets
//deposit assets
//record userbalances
(uint px1,) = lpOracle.getResult(address(allPairs[i]));
if (allPairs[i].token1() == router.WETH()){
path[0] = address(allPairs[i].token1());
path[1] = address(allPairs[i].token0());
require(px1 != 1);
px1 = uint(0x100000000000000000000000000000000000000000000000000000000).div(px1);
} else {
path[0] = address(allPairs[i].token0());
path[1] = address(allPairs[i].token1());
}
//px1 is now always tokens per eth
uint minAmnt = (minRatio.mul(px1).mul(depAmntDiv)).div((uint(2**112)).mul(uint(1000)).mul(1e18));
swapExactETHForTokensSupportingFeeOnTransferTokens(depAmntDiv,minAmnt, path);
//addLiquidityETH
addLiquidityETH(
path[1],
depAmntDiv,
IBEP20(path[1]).balanceOf(address(this)),
0,
0);
//deposit in farm
address(allPairs[i]).safeApprove(address(chef), uint256(-1));
chef.deposit(pids[i], allPairs[i].balanceOf(address(this)));
}
}
}