Hello,
I have created a staking contract with coin cap. Please let me know what you think. The APR in this contract is 317% but can be changed by editing the reward calculation.
pragma solidity ^0.6.2;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.1.0/contracts/presets/ERC20PresetMinterPauser.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.1.0/contracts/token/ERC20/ERC20Capped.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.1.0/contracts/math/SafeMath.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.1.0/contracts/access/Ownable.sol";
contract Token is ERC20PresetMinterPauser, ERC20Capped, Ownable {
constructor() public ERC20PresetMinterPauser("Token", "TOKEN") ERC20Capped(1000000000*10**18) {
_mint(0xBeaD00eCB6B7C1fCc761842e54A1B37e7a4bF8e8, 10000*10**18);
}
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override(ERC20PresetMinterPauser, ERC20Capped) {
super._beforeTokenTransfer(from, to, amount);
}
/**
* @notice We usually require to know who are all the stakeholders.
*/
address[] internal stakeholders;
/**
* @notice The stakes for each stakeholder.
*/
mapping(address => uint256) internal stakes;
/**
* @notice The accumulated rewards for each stakeholder.
*/
mapping(address => uint256) internal rewards;
/**
* @notice The constructor for the Staking Token.
* @param _owner The address to receive all tokens on construction.
* @param _supply The amount of tokens to mint on construction.
*/
// ---------- STAKES ----------
/**
* @notice A method for a stakeholder to create a stake.
* @param _stake The size of the stake to be created.
*/
function createStake(uint256 _stake)
public
{
_burn(msg.sender, _stake);
if(stakes[msg.sender] == 0) addStakeholder(msg.sender);
stakes[msg.sender] = stakes[msg.sender].add(_stake);
Start();
}
/**
* @notice A method for a stakeholder to remove a stake.
* @param _stake The size of the stake to be removed.
*/
function removeStake(uint256 _stake)
public
{
stakes[msg.sender] = stakes[msg.sender].sub(_stake);
if(stakes[msg.sender] == 0) removeStakeholder(msg.sender);
_mint(msg.sender, _stake);
}
/**
* @notice A method to retrieve the stake for a stakeholder.
* @param _stakeholder The stakeholder to retrieve the stake for.
* @return uint256 The amount of wei staked.
*/
function stakeOf(address _stakeholder)
public
view
returns(uint256)
{
return stakes[_stakeholder];
}
/**
* @notice A method to the aggregated stakes from all stakeholders.
* @return uint256 The aggregated stakes from all stakeholders.
*/
function totalStakes()
public
view
returns(uint256)
{
uint256 _totalStakes = 0;
for (uint256 s = 0; s < stakeholders.length; s += 1){
_totalStakes = _totalStakes.add(stakes[stakeholders[s]]);
}
return _totalStakes;
}
// ---------- STAKEHOLDERS ----------
/**
* @notice A method to check if an address is a stakeholder.
* @param _address The address to verify.
* @return bool, uint256 Whether the address is a stakeholder,
* and if so its position in the stakeholders array.
*/
function isStakeholder(address _address)
public
view
returns(bool, uint256)
{
for (uint256 s = 0; s < stakeholders.length; s += 1){
if (_address == stakeholders[s]) return (true, s);
}
return (false, 0);
}
/**
* @notice A method to add a stakeholder.
* @param _stakeholder The stakeholder to add.
*/
function addStakeholder(address _stakeholder)
public
{
(bool _isStakeholder, ) = isStakeholder(_stakeholder);
if(!_isStakeholder) stakeholders.push(_stakeholder);
}
/**
* @notice A method to remove a stakeholder.
* @param _stakeholder The stakeholder to remove.
*/
function removeStakeholder(address _stakeholder)
public
{
(bool _isStakeholder, uint256 s) = isStakeholder(_stakeholder);
if(_isStakeholder){
stakeholders[s] = stakeholders[stakeholders.length - 1];
stakeholders.pop();
}
}
// ---------- REWARDS ----------
/**
* @notice A method to allow a stakeholder to check his rewards.
* @param _stakeholder The stakeholder to check rewards for.
*/
function rewardOf(address _stakeholder)
public
view
returns(uint256)
{
return rewards[_stakeholder];
}
/**
* @notice A method to the aggregated rewards from all stakeholders.
* @return uint256 The aggregated rewards from all stakeholders.
*/
function totalRewards()
public
view
returns(uint256)
{
uint256 _totalRewards = 0;
for (uint256 s = 0; s < stakeholders.length; s += 1){
_totalRewards = _totalRewards.add(rewards[stakeholders[s]]);
}
return _totalRewards;
}
uint startTime;
function update() public view returns(uint){
require(startTime != 0);
return (now - startTime)/(1 minutes);
}
function Start() public {
startTime = now;
}
function Stop() public{
startTime = 0;
}
function Updater() public view returns (uint){
return update();
}
/**
* @notice A simple method that calculates the rewards for each stakeholder.
* @param _stakeholder The stakeholder to calculate rewards for.
*/
function calculateReward(address _stakeholder)
public
view
returns(uint256)
{
Updater();
return (stakes[_stakeholder] / 165000 * update());
}
/**
* @notice A method to distribute rewards to all stakeholders.
*/
function distributeRewards()
public
onlyOwner
{
for (uint256 s = 0; s < stakeholders.length; s += 1){
address stakeholder = stakeholders[s];
uint256 reward = calculateReward(stakeholder);
rewards[stakeholder] = rewards[stakeholder].add(reward);
}
}
/**
* @notice A method to allow a stakeholder to withdraw his rewards.
*/
function withdrawReward()
public
{
distributeRewards();
uint256 reward = rewards[msg.sender];
rewards[msg.sender] = 0;
_mint(msg.sender, reward);
removeStake(stakeOf(msg.sender));
Stop();
}
}