diff --git a/ColdStaking.sol b/ColdStaking.sol index fab8dfb..004b070 100644 --- a/ColdStaking.sol +++ b/ColdStaking.sol @@ -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; } @@ -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 =========== @@ -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); } @@ -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; } } @@ -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 ); } @@ -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. @@ -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;