forked from chocho13/token
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstaking.sol
More file actions
210 lines (180 loc) · 7.97 KB
/
Copy pathstaking.sol
File metadata and controls
210 lines (180 loc) · 7.97 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
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
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 Struct {
uint timestamp;
uint rewardPerBlock;
}
Struct[] public rewardPerBlockHistory;
mapping(address => uint[]) private ownerStakeIds;
mapping(uint => address) public stakingUser;
mapping(uint => uint) public stakingAmount;
mapping(uint => uint) public stakingEndDate;
mapping(uint => uint) public stakingLastClaim;
uint public stakesCount;
uint public totalSupply;
uint public totalStaked;
bool public stakingAllowed;
uint public lastUpdatePoolSizePercent;
uint public maxApr;
uint public percentAutoUpdatePool;
uint public constant MAX_SUPPLY = 80 * 1e6 * 1e18;
uint public constant MINIMUM_AMOUNT = 1 * 1e18;
uint public constant REWARD_PER_BLOCK = 0.000008 * 1e18;
uint public constant SECONDS_IN_YEAR = 31536000;
uint public constant MIN_APR = 25;
mapping(address => bool) private stakerAddressList;
address public constant STAKING_TOKEN_ADDRESS = 0x1d0Ac23F03870f768ca005c84cBb6FB82aa884fD; // galeon address
IERC20 private constant STAKING_TOKEN = IERC20(STAKING_TOKEN_ADDRESS);
constructor() {
totalSupply = 0;
lastUpdatePoolSizePercent = 0;
stakingAllowed = true;
maxApr = 500; // 500%
stakesCount = 0;
percentAutoUpdatePool = 5;
}
event Stacked(uint _amount,uint _totalSupply);
event Unstaked(uint _amount);
event Claimed(uint _claimed);
event StakingAllowed(bool _allow);
event RewardPerBlockUpdatedTimestamp(uint _lastupdate);
event AdjustMaxApr(uint _maxApr);
event AdjustpercentAutoUpdatePool(uint _percentAutoUpdatePool);
event UpdatedStaker(address _staker, bool _allowed);
function addStakerAddress(address _addr) public onlyOwner {
stakerAddressList[_addr] = true;
emit UpdatedStaker(_addr, true);
}
function delStakerAddress(address _addr) public onlyOwner {
stakerAddressList[_addr] = false;
emit UpdatedStaker(_addr, false);
}
function isStakerAddress(address check) public view returns(bool isIndeed) {
return stakerAddressList[check];
}
function adjustMaxApr(uint _maxApr) external onlyOwner {
maxApr = _maxApr;
emit AdjustMaxApr(maxApr);
}
function adjustpercentAutoUpdatePool(uint _percentAutoUpdatePool) external onlyOwner {
percentAutoUpdatePool = _percentAutoUpdatePool;
emit AdjustMaxApr(percentAutoUpdatePool);
}
function updateRewardPerBlock() external onlyOwner {
_updateRewardPerBlock();
lastUpdatePoolSizePercent = totalSupply * 100 / MAX_SUPPLY;
}
function allowStaking(bool _allow) external onlyOwner {
stakingAllowed = _allow;
emit StakingAllowed(_allow);
}
function stake(uint _amount) external {
_stake(_amount,msg.sender);
}
function stakForSomeoneElse(uint _amount,address _user) external {
require(isStakerAddress(msg.sender), "stakers allowed only");
_stake(_amount,_user);
}
function recompound() external nonReentrant {
uint toClaim = claimableRewards(msg.sender);
require(toClaim >= MINIMUM_AMOUNT,"Insuficient amount");
_stake(toClaim,msg.sender);
_updateLastClaim();
}
function claim() external nonReentrant {
uint toClaim = claimableRewards(msg.sender);
require(STAKING_TOKEN.balanceOf(address(this)) > toClaim + totalStaked, "Insuficient contract balance");
require(STAKING_TOKEN.transfer(msg.sender,toClaim), "Transfer failed");
_updateLastClaim();
emit Claimed(toClaim);
}
function unstake() external nonReentrant returns(uint) {
uint toUnstake = 0;
uint i = 0;
uint stakeId;
uint toClaim = claimableRewards(msg.sender);
while(i < ownerStakeIds[msg.sender].length) {
stakeId = ownerStakeIds[msg.sender][i];
if (stakingEndDate[stakeId] < block.timestamp) {
toUnstake += stakingAmount[stakeId];
totalStaked -= stakingAmount[stakeId];
stakingAmount[stakeId] = 0;
ownerStakeIds[msg.sender][i] = ownerStakeIds[msg.sender][ownerStakeIds[msg.sender].length -1];
ownerStakeIds[msg.sender].pop();
} else {
i++;
}
}
require(toUnstake > 0, "Nothing to unstake");
require(STAKING_TOKEN.balanceOf(address(this)) > toUnstake + toClaim, "Insuficient contract balance");
require(STAKING_TOKEN.transfer(msg.sender,toUnstake + toClaim), "Transfer failed");
totalStaked -= toUnstake;
_updateLastClaim();
emit Unstaked(toUnstake);
emit Claimed(toClaim);
return toUnstake + toClaim;
}
function getUserStakesIds(address _user) external view returns (uint[] memory) {
return ownerStakeIds[_user];
}
function claimableRewards(address _user) public view returns (uint) {
uint reward = 0;
uint stakeId;
for(uint i = 0; i < ownerStakeIds[_user].length; i++) {
stakeId = ownerStakeIds[_user][i];
uint lastClaim = stakingLastClaim[stakeId];
uint j;
for(j = 0; j < rewardPerBlockHistory.length; j++) {
if (rewardPerBlockHistory[j].timestamp > lastClaim) {
reward += stakingAmount[stakeId] * (rewardPerBlockHistory[j].timestamp - lastClaim) * rewardPerBlockHistory[j].rewardPerBlock;
lastClaim = rewardPerBlockHistory[j].timestamp;
}
}
reward += stakingAmount[stakeId] * (lastClaim - block.timestamp) * rewardPerBlockHistory[j].rewardPerBlock;
}
return (reward);
}
function _updateLastClaim() internal {
for(uint i = 0; i < ownerStakeIds[msg.sender].length; i++) {
stakingLastClaim[ownerStakeIds[msg.sender][i]] = block.timestamp;
}
}
function _stake(uint _amount, address _user) internal nonReentrant {
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");
require(ownerStakeIds[_user].length < 100, "User staking limit exceeded");
stakingUser[stakesCount] = _user;
stakingEndDate[stakesCount] = block.timestamp + 365 days;
stakingLastClaim[stakesCount] = block.timestamp;
ownerStakeIds[_user].push(stakesCount);
totalSupply += _amount;
totalStaked += _amount;
stakesCount += 1;
uint poolSizePercent = totalSupply * 100 / MAX_SUPPLY;
if (poolSizePercent > lastUpdatePoolSizePercent + percentAutoUpdatePool) {
_updateRewardPerBlock();
lastUpdatePoolSizePercent = poolSizePercent;
}
emit Stacked(_amount,totalSupply);
}
function _updateRewardPerBlock() internal {
uint maxRewardPerBlock = totalSupply * maxApr / SECONDS_IN_YEAR / 100 * 1e18;
uint minRewardPerBlock = totalSupply * MIN_APR / SECONDS_IN_YEAR / 100 * 1e18;
uint rewardPerBlock;
if (REWARD_PER_BLOCK < minRewardPerBlock) {
rewardPerBlock = minRewardPerBlock / totalSupply;
} else if (REWARD_PER_BLOCK > maxRewardPerBlock) {
rewardPerBlock = maxRewardPerBlock / totalSupply;
} else {
rewardPerBlock = REWARD_PER_BLOCK / totalSupply;
}
rewardPerBlockHistory.push(Struct(block.timestamp,rewardPerBlock));
emit RewardPerBlockUpdatedTimestamp(block.timestamp);
}}