-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathstaking_old.sol
More file actions
148 lines (132 loc) · 5.79 KB
/
Copy pathstaking_old.sol
File metadata and controls
148 lines (132 loc) · 5.79 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
// SPDX-License-Identifier: MIT
pragma solidity 0.8.12;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract OneYearStakingContract is Ownable, ReentrancyGuard {
struct Stake {
address user;
uint amount;
uint stakingEndDate;
uint unclaimed;
}
mapping(address => uint[]) private ownerStakeIds;
Stake[] public stakes;
uint public totalSupply;
uint public totalStaked;
bool public stakingAllowed;
uint public lastUpdate;
uint public maxApr;
uint public constant MAX_SUPPLY = 80 * 1e6 * 1e18;
uint public constant MINIMUM_AMOUNT = 500 * 1e18;
uint public constant REWARD_PER_BLOCK = 0.6808 * 1e18;
uint public constant SECONDS_IN_YEAR = 31536000;
address public constant STAKING_TOKEN_ADDRESS = 0x1d0Ac23F03870f768ca005c84cBb6FB82aa884fD; // galeon address
IERC20 private constant STAKING_TOKEN = IERC20(STAKING_TOKEN_ADDRESS);
constructor() {
lastUpdate = block.timestamp;
totalSupply = 0;
stakingAllowed = true;
maxApr = 500; // 500%
}
event Stacked(uint _amount,uint _totalSupply);
event Unstaked(uint _amount);
event Claimed(uint _claimed);
event StakingAllowed(bool _allow);
event Updated(uint _lastupdate);
event AdjustMaxApr(uint _maxApr);
function adjustMaxApr(uint _maxApr) external onlyOwner returns (uint newMaxApr) {
emit AdjustMaxApr(_maxApr);
return maxApr = _maxApr;
}
function allowStaking(bool _allow) external onlyOwner returns (bool allowed) {
emit StakingAllowed(_allow);
return stakingAllowed = _allow;
}
function forceUpdatePool() external updatePool nonReentrant returns (bool updated) {
return true;
}
function stake(uint _amount) external updatePool nonReentrant returns (bool staked) {
require(stakingAllowed, "Staking is not enabled");
require(_amount >= MINIMUM_AMOUNT, "Insuficient amount");
require(totalSupply + _amount <= MAX_SUPPLY, "Pool capacity exceeded");
require(_amount <= STAKING_TOKEN.balanceOf(msg.sender), "Insuficient balance");
require(STAKING_TOKEN.transferFrom(msg.sender, address(this), _amount), "TransferFrom failed");
stakes.push(Stake(msg.sender, _amount, block.timestamp + 365 days,0));
ownerStakeIds[msg.sender].push(stakes.length-1);
totalSupply += _amount;
totalStaked += _amount;
emit Stacked(_amount,totalSupply);
return true;
}
function claim() external updatePool nonReentrant returns(uint amount) {
uint claimed = 0;
uint j;
for(uint i = 0; i < ownerStakeIds[msg.sender].length; i++) {
j = ownerStakeIds[msg.sender][i];
if (stakes[j].unclaimed > 0) {
require(STAKING_TOKEN.balanceOf(address(this)) - totalStaked > stakes[j].unclaimed, "Insuficient contract balance");
require(STAKING_TOKEN.transfer(msg.sender,stakes[j].unclaimed), "Transfer failed");
}
claimed += stakes[j].unclaimed;
stakes[j].unclaimed = 0;
}
emit Claimed(claimed);
return claimed;
}
function unstake() external updatePool nonReentrant returns(uint unstaked) {
uint stakedAmount = 0;
uint unclaimedAmount = 0;
uint amount = 0;
uint i = 0;
uint stakeId;
while(i < ownerStakeIds[msg.sender].length) {
stakeId = ownerStakeIds[msg.sender][i];
if (stakes[stakeId].stakingEndDate < block.timestamp) {
amount = stakes[stakeId].amount + stakes[stakeId].unclaimed;
stakedAmount += stakes[stakeId].amount;
unclaimedAmount += stakes[stakeId].unclaimed;
require(STAKING_TOKEN.balanceOf(address(this)) > amount, "Insuficient contract balance");
require(STAKING_TOKEN.transfer(msg.sender,amount), "Transfer failed");
totalStaked -= stakes[stakeId].amount;
stakes[stakeId] = stakes[stakes.length -1];
for(uint k = 0; k < ownerStakeIds[stakes[stakeId].user].length; k++) {
if (ownerStakeIds[stakes[stakeId].user][k] == stakes.length -1) {
ownerStakeIds[stakes[stakeId].user][k] = stakeId;
}
}
stakes.pop();
ownerStakeIds[msg.sender][i] = ownerStakeIds[msg.sender][ownerStakeIds[msg.sender].length -1];
ownerStakeIds[msg.sender].pop();
} else {
i++;
}
}
require(stakedAmount > 0, "Nothing to unstake");
emit Unstaked(stakedAmount);
emit Claimed(unclaimedAmount);
return stakedAmount + unclaimedAmount;
}
function getUserStakesIds(address _user) external view returns (uint[] memory) {
return ownerStakeIds[_user];
}
modifier updatePool() {
uint maxRewardPerBlock = totalSupply * maxApr / SECONDS_IN_YEAR / 100 * 1e18;
uint rewardPerBlock;
if (REWARD_PER_BLOCK > maxRewardPerBlock) {
rewardPerBlock = maxRewardPerBlock / totalSupply;
} else {
rewardPerBlock = REWARD_PER_BLOCK / totalSupply;
}
for(uint i = 0; i < stakes.length; i++) {
if(stakes[i].stakingEndDate >= block.timestamp) {
stakes[i].unclaimed += stakes[i].amount * (block.timestamp - lastUpdate) * rewardPerBlock;
} else if (lastUpdate < stakes[i].stakingEndDate) {
stakes[i].unclaimed += stakes[i].amount * (stakes[i].stakingEndDate - lastUpdate) * rewardPerBlock;
}
}
lastUpdate = block.timestamp;
emit Updated(lastUpdate);
_;
}
}