Transactions
Token Transfers
Tokens
Internal Transactions
Coin Balance History
Logs
Code
Read Contract
Write Contract
- Contract name:
- ColdStaking
- Optimization enabled
- true
- Compiler version
- v0.6.12+commit.27d51765
- Optimization runs
- 200
- EVM Version
- default
- Verified at
- 2022-06-09T14:18:11.131301Z
Contract source code
// SPDX-License-Identifier: No License (None) pragma solidity ^0.6.0; /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { function mul(uint a, uint b) internal pure returns (uint) { if (a == 0) { return 0; } uint c = a * b; require(c / a == b); return c; } function div(uint a, uint b) internal pure returns (uint) { // assert(b > 0); // Solidity automatically throws when dividing by 0 uint 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) { require(b <= a); return a - b; } function add(uint a, uint b) internal pure returns (uint) { uint c = a + b; require(c >= a); return c; } } contract ColdStaking { // NOTE: The contract only works for intervals of time > round_interval using SafeMath for uint; event StartStaking(address addr, uint value, uint amount, uint time, uint end_time); event WithdrawStake(address staker, uint amount); event Claim(address staker, uint reward); event DonationDeposited(address _address, uint value); struct Staker { uint amount; uint time; // Staking start time or last claim rewards uint multiplier; // Rewards multiplier = 0.40 + (0.05 * rounds). [0.45..1] (max rounds 12) uint end_time; // Time when staking ends and user may withdraw. After this time user will not receive rewards. } uint public LastBlock = block.number; uint 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. uint public staking_threshold = 0 ether; uint public constant round_interval = 27 days; // 1 month. uint public constant max_delay = 365 days; // 1 year after staking ends. uint public constant BlockStartStaking = 7600000; uint constant NOMINATOR = 10**18; // Nominator / denominator used for float point numbers //========== TESTNET VALUES =========== //uint public constant round_interval = 1 hours; //uint public constant max_delay = 2 days; //uint public constant BlockStartStaking = 0; //========== END TEST VALUES ========== mapping(address => Staker) public staker; receive() external payable { // No donations accepted to fallback! // Consider value deposit is an attempt to become staker. // May not accept deposit from other contracts due GAS limit. // by default stake for 1 round start_staking(1); } // update TotalStakingAmount value. function new_block() internal { if (block.number > LastBlock) //run once per block. { uint _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 at least +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. { 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; } } } function start_staking() external payable { // by default stake for 1 round start_staking(1); } function start_staking(uint rounds) public staking_available payable { assert(msg.value >= staking_threshold); require(rounds > 0); new_block(); //run once per block. // to reduce gas cost we will use local variable instead of global uint _Timestamp = Timestamp; uint staker_amount = staker[msg.sender].amount; uint r = rounds; if (r > 12) r = 12; uint multiplier = (40 + (5 * r)) * NOMINATOR / 100; // staker multiplier = 0.40 + (0.05 * rounds). [0.45..1] uint end_time = _Timestamp.add(round_interval.mul(rounds)); // claim reward if available. if (staker_amount > 0) { if (_Timestamp >= staker[msg.sender].time + round_interval) { _claim(msg.sender); } uint staker_end_time = staker[msg.sender].end_time; if (staker_end_time > end_time) { end_time = staker_end_time; // Staking end time is the bigger from previous and new one. r = (end_time.sub(_Timestamp)).div(round_interval); // update number of rounds if (r > 12) r = 12; multiplier = (40 + (5 * r)) * NOMINATOR / 100; // staker multiplier = 0.40 + (0.05 * rounds). [0.45..1] } // if there is active staking with bigger multiplier if (staker[msg.sender].multiplier > multiplier && staker_end_time > _Timestamp) { // recalculate multiplier = (staker.multiplier * staker.amount + new.multiplier * new.amount) / ( staker.amount + new.amount) multiplier = ((staker[msg.sender].multiplier.mul(staker_amount)).add(multiplier.mul(msg.value))).div(staker_amount.add(msg.value)); if (multiplier > NOMINATOR) multiplier = NOMINATOR; // multiplier can't be more then 1 } TotalStakingWeight = TotalStakingWeight.sub((_Timestamp.sub(staker[msg.sender].time)).mul(staker_amount)); // remove from Weight } TotalStakingAmount = TotalStakingAmount.add(msg.value); staker[msg.sender].time = _Timestamp; staker[msg.sender].amount = staker_amount.add(msg.value); staker[msg.sender].multiplier = multiplier; staker[msg.sender].end_time = end_time; emit StartStaking( msg.sender, msg.value, staker[msg.sender].amount, _Timestamp, end_time ); } function DEBUG_donation() external payable { emit DonationDeposited(msg.sender, msg.value); } function withdraw_stake() external { _withdraw_stake(msg.sender); } function withdraw_stake(address payable user) external { _withdraw_stake(user); } function _withdraw_stake(address payable user) internal { new_block(); //run once per block. require(Timestamp >= staker[user].end_time); //reject withdrawal before end time. uint _amount = staker[user].amount; require(_amount != 0); // claim reward if available. _claim(user); TotalStakingAmount = TotalStakingAmount.sub(_amount); TotalStakingWeight = TotalStakingWeight.sub((Timestamp.sub(staker[user].time)).mul(staker[user].amount)); // remove from Weight. staker[user].amount = 0; user.transfer(_amount); emit WithdrawStake(user, _amount); } //claim rewards function claim() external only_staker { _claim(msg.sender); } function _claim(address payable user) internal { new_block(); //run once per block // to reduce gas cost we will use local variable instead of global uint _Timestamp = Timestamp; if (_Timestamp > staker[user].end_time) _Timestamp = staker[user].end_time; // rewards calculates until staking ends uint _StakingInterval = _Timestamp.sub(staker[user].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[user].amount); //Weight of completed rounds. uint _reward = StakingRewardPool.mul(_StakerWeight).div(TotalStakingWeight); //StakingRewardPool * _StakerWeight/TotalStakingWeight _reward = _reward.mul(staker[user].multiplier) / NOMINATOR; // reduce rewards if staked on less then 12 rounds. StakingRewardPool = StakingRewardPool.sub(_reward); TotalStakingWeight = TotalStakingWeight.sub(_StakerWeight); // remove paid Weight. staker[user].time = staker[user].time.add(_CompleteRoundsInterval); // reset to paid time, staking continue without a loss of incomplete rounds. user.transfer(_reward); emit Claim(user, _reward); } } //This function may be used for info only. This can show estimated user reward at current time. function stake_reward(address _addr) external view returns (uint _reward) { require(staker[_addr].amount > 0); 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; } uint _Timestamp = Timestamp + _seconds; if (_Timestamp > staker[_addr].end_time) _Timestamp = staker[_addr].end_time; // rewards calculates until staking ends uint _TotalStakingWeight = TotalStakingWeight + _seconds.mul(TotalStakingAmount); uint _StakingInterval = _Timestamp.sub(staker[_addr].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 _StakingRewardPool = address(this).balance.sub(TotalStakingAmount); _reward = _StakingRewardPool.mul(_StakerWeight).div(_TotalStakingWeight); //StakingRewardPool * _StakerWeight/TotalStakingWeight _reward = _reward.mul(staker[_addr].multiplier) / NOMINATOR; // reduce rewards if staked on less then 12 rounds. } modifier only_staker { require(staker[msg.sender].amount > 0); _; } modifier staking_available { require(block.number >= BlockStartStaking); _; } //return deposit to inactive staker after 1 year when staking ends. function report_abuse(address payable _addr) public only_staker { require(staker[_addr].amount > 0); new_block(); //run once per block. require(Timestamp > staker[_addr].end_time.add(max_delay)); uint _amount = staker[_addr].amount; TotalStakingAmount = TotalStakingAmount.sub(_amount); TotalStakingWeight = TotalStakingWeight.sub((Timestamp.sub(staker[_addr].time)).mul(_amount)); // remove from Weight. staker[_addr].amount = 0; _addr.transfer(_amount); } }
Contract ABI
[{"type":"event","name":"Claim","inputs":[{"type":"address","name":"staker","internalType":"address","indexed":false},{"type":"uint256","name":"reward","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"DonationDeposited","inputs":[{"type":"address","name":"_address","internalType":"address","indexed":false},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"StartStaking","inputs":[{"type":"address","name":"addr","internalType":"address","indexed":false},{"type":"uint256","name":"value","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"uint256","name":"time","internalType":"uint256","indexed":false},{"type":"uint256","name":"end_time","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"WithdrawStake","inputs":[{"type":"address","name":"staker","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"BlockStartStaking","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"DEBUG_donation","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"LastBlock","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"StakingRewardPool","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"Timestamp","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"TotalStakingAmount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"TotalStakingWeight","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claim","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"max_delay","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"report_abuse","inputs":[{"type":"address","name":"_addr","internalType":"address payable"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"round_interval","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_reward","internalType":"uint256"}],"name":"stake_reward","inputs":[{"type":"address","name":"_addr","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"time","internalType":"uint256"},{"type":"uint256","name":"multiplier","internalType":"uint256"},{"type":"uint256","name":"end_time","internalType":"uint256"}],"name":"staker","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"staking_threshold","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"start_staking","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"start_staking","inputs":[{"type":"uint256","name":"rounds","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdraw_stake","inputs":[{"type":"address","name":"user","internalType":"address payable"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdraw_stake","inputs":[]},{"type":"receive","stateMutability":"payable"}]
Deployed ByteCode
0x60806040526004361061010d5760003560e01c806382e4eda411610095578063bf92b4ef11610064578063bf92b4ef146102b4578063cd948855146102e7578063e2d40568146102fc578063e6369e4114610311578063edbbc33b146103265761011e565b806382e4eda4146101fe578063aaee69d914610257578063b3fefcf51461028a578063b43e92fa1461029f5761011e565b80634e71d92d116100dc5780634e71d92d1461016f5780635c854aa9146101845780635d8c85ef146101995780637157c113146101b657806378be0ad4146101cb5761011e565b806314657ef0146101235780631e37de4c1461014a5780631f288efb1461015f5780633a339a4c146101675761011e565b3661011e5761011c600161033b565b005b600080fd5b34801561012f57600080fd5b506101386105a6565b60408051918252519081900360200190f35b34801561015657600080fd5b506101386105ac565b61011c6105b2565b61011c6105be565b34801561017b57600080fd5b5061011c6105fa565b34801561019057600080fd5b5061013861061c565b61011c600480360360208110156101af57600080fd5b503561033b565b3480156101c257600080fd5b50610138610622565b3480156101d757600080fd5b5061011c600480360360208110156101ee57600080fd5b50356001600160a01b0316610629565b34801561020a57600080fd5b506102316004803603602081101561022157600080fd5b50356001600160a01b0316610635565b604080519485526020850193909352838301919091526060830152519081900360800190f35b34801561026357600080fd5b5061011c6004803603602081101561027a57600080fd5b50356001600160a01b031661065c565b34801561029657600080fd5b5061013861077f565b3480156102ab57600080fd5b50610138610785565b3480156102c057600080fd5b50610138600480360360208110156102d757600080fd5b50356001600160a01b031661078b565b3480156102f357600080fd5b5061011c61090c565b34801561030857600080fd5b50610138610915565b34801561031d57600080fd5b5061013861091d565b34801561033257600080fd5b50610138610923565b6273f78043101561034b57600080fd5b60055434101561035757fe5b6000811161036457600080fd5b61036c61092a565b6001543360009081526006602052604090205482600c81111561038d5750600c5b6064670de0b6b3a764000060058302602801020460006103ba6103b36223988088610998565b86906109c8565b905083156105015733600090815260066020526040902060010154622398800185106103e9576103e9336109da565b33600090815260066020526040902060030154818111156104455790508061041e622398806104188389610bd3565b90610be8565b9350600c84111561042e57600c93505b606460286005860201670de0b6b3a7640000020492505b336000908152600660205260409020600201548310801561046557508581115b156104c5576104a761047786346109c8565b6104186104848634610998565b336000908152600660205260409020600201546104a1908a610998565b906109c8565b9250670de0b6b3a76400008311156104c557670de0b6b3a764000092505b336000908152600660205260409020600101546104fc906104f39087906104ed908a90610bd3565b90610998565b60025490610bd3565b600255505b60035461050e90346109c8565b60035533600090815260066020526040902060010185905561053084346109c8565b33600081815260066020908152604091829020848155600281018790556003018590558151928352349083015281810192909252606081018790526080810183905290517f59314c5dd1994b45a3987ac7bace43129e2616c39c4ce9a995c6cc5e4adc5cb49181900360a00190a1505050505050565b60025481565b60045481565b6105bc600161033b565b565b6040805133815234602082015281517fe669e18d16cf72448be9ee29e977628bedcffa0e0c0cde5e8448849fb9426857929181900390910190a1565b3360009081526006602052604090205461061357600080fd5b6105bc336109da565b60055481565b6223988081565b61063281610bfd565b50565b60066020526000908152604090208054600182015460028301546003909301549192909184565b3360009081526006602052604090205461067557600080fd5b6001600160a01b03811660009081526006602052604090205461069757600080fd5b61069f61092a565b6001600160a01b0381166000908152600660205260409020600301546106c9906301e133806109c8565b600154116106d657600080fd5b6001600160a01b0381166000908152600660205260409020546003546106fc9082610bd3565b6003556001600160a01b03821660009081526006602052604090206001908101549054610733916104f39184916104ed9190610bd3565b6002556001600160a01b0382166000818152600660205260408082208290555183156108fc0291849190818181858888f1935050505015801561077a573d6000803e3d6000fd5b505050565b60035481565b60005481565b6001600160a01b0381166000908152600660205260408120546107ad57600080fd5b6000546001544391909103904203601982028111156107cc5750601981025b6001546001600160a01b0385166000908152600660205260409020600301549082019081111561081457506001600160a01b0384166000908152600660205260409020600301545b600061082b6003548461099890919063ffffffff16565b6002546001600160a01b0388166000908152600660205260408120600101549290910192509061085c908490610bd3565b905060006108716223988080845b0490610998565b6001600160a01b03891660009081526006602052604081205491925090610899908390610998565b905060006108b260035447610bd390919063ffffffff16565b90506108c2856104188385610998565b6001600160a01b038b16600090815260066020526040902060020154909950670de0b6b3a7640000906108f6908b90610998565b816108fd57fe5b049a9950505050505050505050565b6105bc33610bfd565b6301e1338081565b60015481565b6273f78081565b6000544311156105bc57600080544390915560035461094c9047903401610bd3565b6004556001544211156106325760015443829003904203601982028111156109745750601981025b600354610982908290610998565b6002805490910190556001805490910190555050565b6000826109a7575060006109c2565b828202828482816109b457fe5b04146109bf57600080fd5b90505b92915050565b6000828201838110156109bf57600080fd5b6109e261092a565b6001546001600160a01b038216600090815260066020526040902060030154811115610a2657506001600160a01b0381166000908152600660205260409020600301545b6001600160a01b038216600090815260066020526040812060010154610a4d908390610bd3565b905062239880811061077a576000610a6962239880808461086a565b6001600160a01b03851660009081526006602052604081205491925090610a91908390610998565b90506000610ab06002546104188460045461099890919063ffffffff16565b6001600160a01b038716600090815260066020526040902060020154909150670de0b6b3a764000090610ae4908390610998565b81610aeb57fe5b049050610b0381600454610bd390919063ffffffff16565b600455600254610b139083610bd3565b6002556001600160a01b038616600090815260066020526040902060010154610b3c90846109c8565b6001600160a01b038716600081815260066020526040808220600101939093559151909183156108fc02918491818181858888f19350505050158015610b86573d6000803e3d6000fd5b50604080516001600160a01b03881681526020810183905281517f47cee97cb7acd717b3c0aa1435d004cd5b3c8c57d70dbceb4e4458bbd60e39d4929181900390910190a1505050505050565b600082821115610be257600080fd5b50900390565b600080828481610bf457fe5b04949350505050565b610c0561092a565b6001600160a01b0381166000908152600660205260409020600301546001541015610c2f57600080fd5b6001600160a01b03811660009081526006602052604090205480610c5257600080fd5b610c5b826109da565b600354610c689082610bd3565b6003556001600160a01b038216600090815260066020526040902080546001918201549154610c9f926104f392916104ed91610bd3565b6002556001600160a01b0382166000818152600660205260408082208290555183156108fc0291849190818181858888f19350505050158015610ce6573d6000803e3d6000fd5b50604080516001600160a01b03841681526020810183905281517f141ef67c4a6d3ec2adfb2f66d33c2b11de5b4f34344757554d430570b18a92ec929181900390910190a1505056fea2646970667358221220657e04f5924264894a178c19b59770cb1d79c43f94ad4d239384d5313e95c9b864736f6c634300060c0033