Giter Club home page Giter Club logo

cold-staking's Introduction

Staking contract at CLO mainnet (will be deprecated in favor of Cold Staking V2)

Staking contract is currently deployed at Callisto Mainnet: https://explorer2.callisto.network/addr/0xd813419749b3c2cdc94a2f9cfcf154113264a9d6#tab_addr_1

Compiled with REMIX solc version:0.4.25+commit.59dbf8f1 optimization enabled.

Cold staking protocol for EVM-compatible blockchain projects

Reference implementation of smart-contract based cold staking protocol. Formal descriptin will be released later.

https://docs.google.com/document/d/1LpRHzW7gGV1JfiXUQOmW-UEVF9TCNNWrYiItDuLgiGA/edit?usp=sharing

The protocol is currently in proof of concept phase.

cold-staking's People

Contributors

alex39web avatar dexaran avatar yuriy77k avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

cold-staking's Issues

Duplicate method staker_info

This method:

function staker_info(address _addr) public constant returns (uint _amount, uint _time)

Is already exist because of this line:

mapping(address => Staker) public staker;

Public property auto-generates getter:

function staker(address staker) public constant returns(uint _amount, uint _time);

Auditing smart contracts in live stream: https://www.youtube.com/watch?v=efZY3_COaiE

Your CryptoManiacs :)

Internal compiler error

safeMath.sol

pragma solidity ^0.4.12;


/**
 * @title SafeMath
 * @dev Math operations with safety checks that throw on error
 */
library SafeMath {
  function mul(uint256 a, uint256 b) internal constant returns (uint256) {
    if (a == 0) {
      return 0;
    }
    uint256 c = a * b;
    assert(c / a == b);
    return c;
  }

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

  function sub(uint256 a, uint256 b) internal constant returns (uint256) {
    assert(b <= a);
    return a - b;
  }

  function add(uint256 a, uint256 b) internal constant returns (uint256) {
    uint256 c = a + b;
    assert(c >= a);
    return c;
  }
}

ColdStaking.sol

pragma solidity ^0.4.12;

import './safeMath.sol';

contract ColdStaking {

    using SafeMath for uint256;

    event StartStaking(address addr, uint256 value, uint256 weight, uint256 init_block);
    event WithdrawStake(address staker, uint256 weight);
    event Claim(address staker, uint256 reward);
    event FirstStakeDonation(address _address, uint256 value);


    /*struct Staker
    // Commented out due to error in solidity compiler
    {
        uint256 weight;
        uint256 init_block;
    }*/

    uint256 public staking_pool;

    uint256 public staking_threshold = 0 ether;

    //uint256 public round_interval    = 172800; // approx. 1 month in blocks
    //uint256 public max_delay      = 172800 * 12; // approx. 1 year in blocks


    /// TESTING VALUES
    uint256 public round_interval = 200; // approx. 1 month in blocks
    uint256 public max_delay = 7 * 6000; // approx. 1 year in blocks

    mapping(address => uint256) staker_weight;
    mapping(address => uint256) staker_init_block;
    mapping(address => bool) private muted;


    function() public payable
    {
        // No donations accepted to fallback!
        // Consider value deposit is an attempt to become staker.
        start_staking();
    }

    function start_staking() public payable
    {
        assert(msg.value >= staking_threshold);
        staking_pool = staking_pool.add(msg.value);
        staker_weight[msg.sender] = staker_weight[msg.sender].add(msg.value);
        staker_init_block[msg.sender] = block.number;

        /*StartStaking(
            msg.sender,
            msg.value,
            staker[msg.sender].weight,
            staker[msg.sender].init_block
        );*/


    }


    function First_Stake_donation() public payable {

        FirstStakeDonation(msg.sender, msg.value);

    }

    function claim_and_withdraw() public
    {
        claim();
        withdraw_stake();
    }

    function withdraw_stake() public only_staker mutex(msg.sender)
    {
        
        msg.sender.transfer(staker[msg.sender].weight);
        staking_pool = staking_pool.sub(staker_weight[msg.sender]);
        staker_weight[msg.sender] = staker_weight[msg.sender].sub(staker_weight[msg.sender]);
        WithdrawStake(msg.sender, staker_weight[msg.sender]);

    }

    function claim() public only_staker mutex(msg.sender)
    {
        require(block.number >= staker_init_block[msg.sender].add(round_interval));

        uint256 _reward = stake_reward(msg.sender);
        msg.sender.transfer(_reward);
        staker_init_block[msg.sender] = block.number;

        Claim(msg.sender, _reward);
    }

    function stake_reward(address _addr) public constant returns (uint256 _reward)
    {
        return (reward() * stakerTimeStake(_addr) * stakerWeightStake(_addr));
    }
    function stakerTimeStake(address _addr) public constant returns (uint256 _time)
    {
        return ((block.number - staker_init_block[_addr]) / round_interval);
        //return 1;
    }
    function stakerWeightStake(address _addr) public constant returns (uint256 _stake)
    {
        return (staker_weight[_addr] / (staking_pool + staker_weight[_addr] * (stakerTimeStake(_addr) - 1)));
        //return 0;
    }

    function report_abuse(address _addr) public only_staker mutex(_addr)
    {
        assert(staker[_addr].weight > 0);
        assert(block.number > staker_init_block[_addr].add(max_delay));

        _addr.transfer(staker_weight[msg.sender]);
        staker_weight[_addr] = 0;
    }

    function reward() public constant returns (uint256)
    {
        return address(this).balance.sub(staking_pool);
    }

    modifier only_staker
    {
        assert(staker_weight[msg.sender] > 0);
        _;
    }

    modifier mutex(address _target)
    {
        if (muted[_target])
        {
            revert();
        }

        muted[_target] = true;
        _;
        muted[_target] = false;
    }

    ////////////// DEBUGGING /////////////////////////////////////////////////////////////


    function test() public constant returns (string)
    {
        return "success!";
    }

    function infoOfStaker(address _addr) constant returns (uint256 weight, uint256 init, uint256 stake_time, uint256 _reward)
    {
        if (staker[_addr].init_block == 0)
        {
            return (staker_weight[_addr],staker_init_block[_addr],0,stake_reward(_addr));
        }
        return (staker_weight[_addr],staker_init_block[_addr],block.number - staker_init_block[_addr],stake_reward(_addr));
    }
    
    function info_WEIGHT(address _addr) constant returns (uint256)
    {
        return staker_weight[_addr];
    }
    
    function info_INIT(address _addr) constant returns (uint256)
    {
        return staker[_addr].init_block;
    }
}

ERROR

Internal exception in StandardCompiler::compileInternal: /src/libsolidity/ast/ASTJsonConverter.cpp(791): Throw in function string dev::solidity::ASTJsonConverter::functionCallKind(dev::solidity::FunctionCallKind)
Dynamic exception type: N5boost16exception_detail10clone_implIN3dev8solidity21InternalCompilerErrorEEE
std::exception::what: std::exception
[PN3dev11tag_commentE] = Unknown kind of function call .

StakingRewardPool value update

StakingRewardPool is updated once inside newBlock() function:

StakingRewardPool = address(this).balance.sub(TotalStakingAmount + msg.value);

However stakers can call start_staking multiple times in the same block. which mean that StakingRewardPool will be updated only once because of the following condition:

if (block.number > LastBlock)
{
    ....
}

The previous used CurrentBlockDeposits might solve this issue.

State Variables Initialization

The following variables are not initialized:

uint public LastBlock;
uint public Timestamp;

check here

The value of the following local variables will be affected.

uint _blocks = block.number - _LastBlock;
uint _seconds = now - Timestamp;

check here

It will also affect the future values of timestamp:

Timestamp += _seconds;

check here

If the above mentioned state variables won't be set in the constructor, the following condition will be true for a significant amount of time, which will stretch the time since the actual block time at the moment of writing is ~12 s, and users will be able to claim in less than the real 27 days:

 if (_seconds > _blocks * 25) 
{
    _seconds = _blocks * 25;
}

If the state variables are initialized in the constructor, the above mentioned condition won't be required. and Timestamp can be set directly with now value

here is what I think can optimize the code and solve the issue:
master...RideSolo:master

Unknown transaction resets staking period

About 29 days ago I did deposit to staking contract 17130 CLO (after that, there are about 122000 coins in the contract).
Staking period must be ended 5 january 2019. During process was error about gas required exceeds.

31 december 2018 (5 days before staking period must end) I send to my wallet 13980 CLO from HitBtc.
After that, there are about 19000 coins in the wallet.

After a couple of minutes, a transaction appeared that sent exactly 17130 CLO to the staking contract.
It looks as if this transaction was created at the beginning of the period (that is, 22 days ago) and was expecting the necessary amount on the wallet to be executed.

So, this transaction which I did not send has reset staking period and reward.


I would not want this to happen again. Not only because I lost about 15,000 coins as a reward for the second round, but even so such "dormant transactions" are not credible.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.