This repository has been archived by the owner on Aug 3, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 24
/
staking.sol
179 lines (140 loc) · 6.67 KB
/
staking.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
pragma solidity ^0.4.22;
import "./erc20interface.sol";
import "./safemath.sol";
import "./Ownable.sol";
contract Staking is Ownable {
using SafeMath for uint;
uint BIGNUMBER = 10**18;
uint DECIMAL = 10**3;
struct stakingInfo {
uint amount;
bool requested;
uint releaseDate;
}
//allowed token addresses
mapping (address => bool) public allowedTokens;
mapping (address => mapping(address => stakingInfo)) public StakeMap; //tokenAddr to user to stake amount
mapping (address => mapping(address => uint)) public userCummRewardPerStake; //tokenAddr to user to remaining claimable amount per stake
mapping (address => uint) public tokenCummRewardPerStake; //tokenAddr to cummulative per token reward since the beginning or time
mapping (address => uint) public tokenTotalStaked; //tokenAddr to total token claimed
mapping (address => address) public Mediator;
modifier isValidToken(address _tokenAddr){
require(allowedTokens[_tokenAddr]);
_;
}
modifier isMediator(address _tokenAddr){
require(Mediator[_tokenAddr] == msg.sender);
_;
}
address public StakeTokenAddr;
constructor(address _tokenAddr) public{
StakeTokenAddr= _tokenAddr;
}
/**
* @dev add approved token address to the mapping
*/
function addToken( address _tokenAddr) onlyOwner external {
allowedTokens[_tokenAddr] = true;
}
/**
* @dev remove approved token address from the mapping
*/
function removeToken( address _tokenAddr) onlyOwner external {
allowedTokens[_tokenAddr] = false;
}
/**
* @dev stake a specific amount to a token
* @param _amount the amount to be staked
* @param _tokenAddr the token the user wish to stake on
* for demo purposes, not requiring user to actually send in tokens right now
*/
function stake(uint _amount, address _tokenAddr) isValidToken(_tokenAddr) external returns (bool){
require(_amount != 0);
//require(ERC20(StakeTokenAddr).transferFrom(msg.sender,this,_amount));
if (StakeMap[_tokenAddr][msg.sender].amount ==0){
StakeMap[_tokenAddr][msg.sender].amount = _amount;
userCummRewardPerStake[_tokenAddr][msg.sender] = tokenCummRewardPerStake[_tokenAddr];
}else{
claim(_tokenAddr, msg.sender);
StakeMap[_tokenAddr][msg.sender].amount = StakeMap[_tokenAddr][msg.sender].amount.add( _amount);
}
tokenTotalStaked[_tokenAddr] = tokenTotalStaked[_tokenAddr].add(_amount);
return true;
}
/**
* demo version
* @dev pay out dividends to stakers, update how much per token each staker can claim
* @param _reward the aggregate amount to be send to all stakers
* @param _tokenAddr the token that this dividend gets paied out in
*/
function distribute(uint _reward,address _tokenAddr) isValidToken(_tokenAddr) external returns (bool){
require(tokenTotalStaked[_tokenAddr] != 0);
uint reward = _reward.mul(BIGNUMBER); //simulate floating point operations
uint rewardAddedPerToken = reward/tokenTotalStaked[_tokenAddr];
tokenCummRewardPerStake[_tokenAddr] = tokenCummRewardPerStake[_tokenAddr].add(rewardAddedPerToken);
return true;
}
// /**
// * production version
// * @dev pay out dividends to stakers, update how much per token each staker can claim
// * @param _reward the aggregate amount to be send to all stakers
// */
// function distribute(uint _reward) isValidToken(msg.sender) external returns (bool){
// require(tokenTotalStaked[msg.sender] != 0);
// uint reward = _reward.mul(BIGNUMBER);
// tokenCummRewardPerStake[msg.sender] += reward/tokenTotalStaked[msg.sender];
// return true;
// }
event claimed(uint amount);
/**
* @dev claim dividends for a particular token that user has stake in
* @param _tokenAddr the token that the claim is made on
* @param _receiver the address which the claim is paid to
*/
function claim(address _tokenAddr, address _receiver) isValidToken(_tokenAddr) public returns (uint) {
uint stakedAmount = StakeMap[_tokenAddr][msg.sender].amount;
//the amount per token for this user for this claim
uint amountOwedPerToken = tokenCummRewardPerStake[_tokenAddr].sub(userCummRewardPerStake[_tokenAddr][msg.sender]);
uint claimableAmount = stakedAmount.mul(amountOwedPerToken); //total amoun that can be claimed by this user
claimableAmount = claimableAmount.mul(DECIMAL); //simulate floating point operations
claimableAmount = claimableAmount.div(BIGNUMBER); //simulate floating point operations
userCummRewardPerStake[_tokenAddr][msg.sender]=tokenCummRewardPerStake[_tokenAddr];
// if (_receiver == address(0)){
// require(ERC20(_tokenAddr).transfer(msg.sender,claimableAmount));
// }else{
// require(ERC20(_tokenAddr).transfer(_receiver,claimableAmount));
// }
emit claimed(claimableAmount);
return claimableAmount;
}
/**
* @dev request to withdraw stake from a particular token, must wait 4 weeks
*/
function initWithdraw(address _tokenAddr) isValidToken(_tokenAddr) external returns (bool){
require(StakeMap[_tokenAddr][msg.sender].amount >0 );
require(! StakeMap[_tokenAddr][msg.sender].requested );
StakeMap[_tokenAddr][msg.sender].releaseDate = now + 4 weeks;
return true;
}
/**
* @dev finalize withdraw of stake
*/
function finalizeWithdraw(uint _amount, address _tokenAddr) isValidToken(_tokenAddr) external returns(bool){
require(StakeMap[_tokenAddr][msg.sender].amount >0 );
require(StakeMap[_tokenAddr][msg.sender].requested );
require(now > StakeMap[_tokenAddr][msg.sender].releaseDate );
claim(_tokenAddr, msg.sender);
require(ERC20(_tokenAddr).transfer(msg.sender,_amount));
tokenTotalStaked[_tokenAddr] = tokenTotalStaked[_tokenAddr].sub(_amount);
StakeMap[_tokenAddr][msg.sender].requested = false;
return true;
}
function releaseStake(address _tokenAddr, address[] _stakers, uint[] _amounts,address _dest) isMediator(_tokenAddr) isValidToken(_tokenAddr) constant external returns (bool){
require(_stakers.length == _amounts.length);
for (uint i =0; i< _stakers.length; i++){
require(ERC20(_tokenAddr).transfer(_dest,_amounts[i]));
StakeMap[_tokenAddr][_stakers[i]].amount -= _amounts[i];
}
return true;
}
}