Skip to content
113 changes: 59 additions & 54 deletions ColdStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,29 @@ pragma solidity ^0.4.24;
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
function mul(uint a, uint b) internal pure returns (uint) {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint c = a * b;
uint256 c = a * b;
require(c / a == b);
return c;
}

function div(uint a, uint b) internal pure returns (uint) {
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint c = a / b;
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}

function sub(uint a, uint b) internal pure returns (uint) {
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
return a - b;
}

function add(uint a, uint b) internal pure returns (uint) {
uint c = a + b;
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
Expand All @@ -37,34 +37,37 @@ contract ColdStaking {

// NOTE: The contract only works for intervals of time > round_interval

using SafeMath for uint;
using SafeMath for uint256;

event StartStaking(address addr, uint value, uint amount, uint time);
event WithdrawStake(address staker, uint amount);
event Claim(address staker, uint reward);
event DonationDeposited(address _address, uint value);
event StartStaking(address indexed addr, uint256 value, uint256 amount, uint256 time);
event WithdrawStake(address indexed staker, uint256 amount);
event Claim(address indexed staker, uint256 reward);
event DonationDeposited(address indexed _address, uint256 value);


struct Staker
{
uint amount;
uint time;
uint256 amount;
uint256 time;
}


uint public LastBlock = block.number;
uint public Timestamp = now; //timestamp of the last interaction with the contract.
uint256 public LastBlock = block.number;
uint256 public Timestamp = now; //timestamp of the last interaction with the contract.

uint public TotalStakingWeight; //total weight = sum (each_staking_amount * each_staking_time).
uint public TotalStakingAmount; //currently frozen amount for Staking.
uint public StakingRewardPool; //available amount for paying rewards.
uint256 public TotalStakingWeight; //total weight = sum (each_staking_amount * each_staking_time).
uint256 public TotalStakingAmount; //currently frozen amount for Staking.
uint256 public StakingRewardPool; //available amount for paying rewards.
bool public CS_frozen; //Cold Staking frozen.
uint public staking_threshold = 0 ether;
uint256 public staking_threshold = 0 ether;
address public Treasury = 0x3c06f218ce6dd8e2c535a8925a2edf81674984d9; // Callisto Staking Reserve address.

uint public round_interval = 27 days; // 1 month.
uint public max_delay = 365 * 2 days;// 2 years.
uint public DateStartStaking = 1541980800; // 12.11.2018 0:0:0 UTC.
uint256 public round_interval = 27 days; // 1 month.
uint256 public max_delay = 365 * 2 days;// 2 years.
uint256 public DateStartStaking = 1541980800; // 12.11.2018 0:0:0 UTC.

uint256 private constant MIN_BLOCK_NUMBER_TO_CLEAR_TREASURER = 1800000;
uint256 private constant BLOCK_MULTIPLIER = 25;


/*========== TESTING VALUES ===========
Expand All @@ -91,8 +94,8 @@ contract ColdStaking {

function clear_treasurer () public only_treasurer
{
require(block.number > 1800000 && !CS_frozen);
Treasury = 0x00;
require(block.number > MIN_BLOCK_NUMBER_TO_CLEAR_TREASURER && !CS_frozen);
Treasury = address(0);
}


Expand All @@ -105,28 +108,27 @@ contract ColdStaking {
}

// this function can be called for manualy update TotalStakingAmount value.
function new_block() public
function new_block() public payable
{
if (block.number > LastBlock) //run once per block.
{
uint _LastBlock = LastBlock;
uint256 _LastBlock = LastBlock;
LastBlock = block.number;

StakingRewardPool = address(this).balance.sub(TotalStakingAmount + msg.value); //fix rewards pool for this block.
// msg.value here for case new_block() is calling from start_staking(), and msg.value will be added to CurrentBlockDeposits.

//The consensus protocol enforces block timestamps are always atleast +1 from their parent, so a node cannot "lie into the past".
if (now > Timestamp) //But with this condition I feel safer :) May be removed.
assert(now > Timestamp); //But with this condition I feel safer :) May be removed.

uint256 _blocks = block.number - _LastBlock;
uint256 _seconds = now - Timestamp;
if (_seconds > _blocks * BLOCK_MULTIPLIER) //if time goes far in the future, then use new time as 25 second * blocks.
{
uint _blocks = block.number - _LastBlock;
uint _seconds = now - Timestamp;
if (_seconds > _blocks * 25) //if time goes far in the future, then use new time as 25 second * blocks.
{
_seconds = _blocks * 25;
}
TotalStakingWeight += _seconds.mul(TotalStakingAmount);
Timestamp += _seconds;
_seconds = _blocks * BLOCK_MULTIPLIER;
}
TotalStakingWeight += _seconds.mul(TotalStakingAmount);
Timestamp += _seconds;
}
}

Expand All @@ -135,26 +137,27 @@ contract ColdStaking {
assert(msg.value >= staking_threshold);
new_block(); //run once per block.

Staker storage _staker = staker[msg.sender];
// claim reward if available.
if (staker[msg.sender].amount > 0)
if (_staker.amount > 0)
{
if (Timestamp >= staker[msg.sender].time + round_interval)
if (Timestamp >= _staker.time + round_interval)
{
claim();
}
TotalStakingWeight = TotalStakingWeight.sub((Timestamp.sub(staker[msg.sender].time)).mul(staker[msg.sender].amount)); // remove from Weight
TotalStakingWeight = TotalStakingWeight.sub((Timestamp.sub(_staker.time)).mul(_staker.amount)); // remove from Weight
}

TotalStakingAmount = TotalStakingAmount.add(msg.value);
staker[msg.sender].time = Timestamp;
staker[msg.sender].amount = staker[msg.sender].amount.add(msg.value);
_staker.time = Timestamp;
_staker.amount = _staker.amount.add(msg.value);


emit StartStaking(
msg.sender,
msg.value,
staker[msg.sender].amount,
staker[msg.sender].time
_staker.amount,
_staker.time
);
}

Expand Down Expand Up @@ -190,12 +193,12 @@ contract ColdStaking {
if (CS_frozen) return; //Don't pay rewards when Cold Staking frozen.

new_block(); //run once per block
uint _StakingInterval = Timestamp.sub(staker[msg.sender].time); //time interval of deposit.
uint256 _StakingInterval = Timestamp.sub(staker[msg.sender].time); //time interval of deposit.
if (_StakingInterval >= round_interval)
{
uint _CompleteRoundsInterval = (_StakingInterval / round_interval).mul(round_interval); //only complete rounds.
uint _StakerWeight = _CompleteRoundsInterval.mul(staker[msg.sender].amount); //Weight of completed rounds.
uint _reward = StakingRewardPool.mul(_StakerWeight).div(TotalStakingWeight); //StakingRewardPool * _StakerWeight/TotalStakingWeight
uint256 _CompleteRoundsInterval = (_StakingInterval / round_interval).mul(round_interval); //only complete rounds.
uint256 _StakerWeight = _CompleteRoundsInterval.mul(staker[msg.sender].amount); //Weight of completed rounds.
uint256 _reward = StakingRewardPool.mul(_StakerWeight).div(TotalStakingWeight); //StakingRewardPool * _StakerWeight/TotalStakingWeight

StakingRewardPool = StakingRewardPool.sub(_reward);
TotalStakingWeight = TotalStakingWeight.sub(_StakerWeight); // remove paid Weight.
Expand All @@ -209,21 +212,23 @@ contract ColdStaking {
}

//This function may be used for info only. This can show estimated user reward at current time.
function stake_reward(address _addr) public constant returns (uint)
function stake_reward(address _addr) public view returns (uint256)
{
require(staker[_addr].amount > 0);
Staker memory _staker = staker[_addr];

require(_staker.amount > 0);
require(!CS_frozen);

uint _StakingInterval = now.sub(staker[_addr].time); //time interval of deposit.
uint256 _StakingInterval = now.sub(_staker.time); //time interval of deposit.

//uint _StakerWeight = _StakingInterval.mul(staker[_addr].amount); //Staker weight.
uint _CompleteRoundsInterval = (_StakingInterval / round_interval).mul(round_interval); //only complete rounds.
uint _StakerWeight = _CompleteRoundsInterval.mul(staker[_addr].amount); //Weight of completed rounds.
//uint _StakerWeight = _StakingInterval.mul(_staker.amount); //Staker weight.
uint256 _CompleteRoundsInterval = (_StakingInterval / round_interval).mul(round_interval); //only complete rounds.
uint256 _StakerWeight = _CompleteRoundsInterval.mul(_staker.amount); //Weight of completed rounds.

return StakingRewardPool.mul(_StakerWeight).div(TotalStakingWeight); //StakingRewardPool * _StakerWeight/TotalStakingWeight
}

function staker_info(address _addr) public constant returns (uint _amount, uint _time)
function staker_info(address _addr) public view returns (uint256 _amount, uint256 _time)
{
_amount = staker[_addr].amount;
_time = staker[_addr].time;
Expand Down