contract Staking is Ownable {
    using SignedSafeMath for int256;
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    uint256 private constant FLOAT_SCALAR = 2**64;

    struct User {
        uint256 balance;
        int256 scaledPayout;
    }

    IERC20 public token;
    IERC20 public WETH;
    uint256 public divPerShare;

    mapping(address => User) public users;

    event OnDeposit(address indexed from, uint256 amount);
    event OnWithdraw(address indexed from, uint256 amount);
    event OnClaim(address indexed from, uint256 amount);
    event OnTransfer(address indexed from, address indexed to, uint256 amount);
    
    constructor(address _weth) public {
        WETH = IERC20(_weth);
    }

    function setToken(address _token) external onlyOwner {
        require(address(token) == address(0));
        token = IERC20(_token);
    }

    function totalSupply() private view returns (uint256) {
        return token.balanceOf(address(this));
    }

    //@dev deposit dust token upon deployment to prevent division by zero 
    function distribute(uint256 _amount) external returns(bool) {
        WETH.safeTransferFrom(address(msg.sender), address(this), _amount);
        divPerShare = divPerShare.add((_amount.mul(FLOAT_SCALAR)).div(totalSupply()));
    }

    function deposit(uint256 _amount) external {
        token.safeTransferFrom(msg.sender, address(this), _amount);
        users[msg.sender].balance = users[msg.sender].balance.add(_amount);
        users[msg.sender].scaledPayout = users[msg.sender].scaledPayout.add(
            int256(_amount.mul(divPerShare))
        );
        emit OnDeposit(msg.sender, _amount);
    }

    function withdraw(uint256 _amount) external {
        require(balanceOf(msg.sender) >= _amount);
        users[msg.sender].balance = users[msg.sender].balance.sub(_amount);
        users[msg.sender].scaledPayout = users[msg.sender].scaledPayout.sub(
            int256(_amount.mul(divPerShare))
        );
        token.safeTransfer(msg.sender, _amount);
        emit OnWithdraw(msg.sender, _amount);
    }

    function claim() external {
        uint256 _dividends = dividendsOf(msg.sender);
        require(_dividends > 0);
        users[msg.sender].scaledPayout = users[msg.sender].scaledPayout.add(
            int256(_dividends.mul(FLOAT_SCALAR))
        );
        WETH.safeTransfer(address(msg.sender), _dividends);
        emit OnClaim(msg.sender, _dividends);
    }

    function transfer(address _to, uint256 _amount) external returns (bool) {
        return _transfer(msg.sender, _to, _amount);
    }

    function balanceOf(address _user) public view returns (uint256) {
        return users[_user].balance;
    }

    function dividendsOf(address _user) public view returns (uint256) {
        return
            uint256(
                int256(divPerShare.mul(balanceOf(_user))).sub(
                    users[_user].scaledPayout
                )
            )
                .div(FLOAT_SCALAR);
    }

    function _transfer(
        address _from,
        address _to,
        uint256 _amount
    ) internal returns (bool) {
        require(users[_from].balance >= _amount);
        users[_from].balance = users[_from].balance.sub(_amount);
        users[_from].scaledPayout = users[_from].scaledPayout.sub(
            int256(_amount.mul(divPerShare))
        );
        users[_to].balance = users[_to].balance.add(_amount);
        users[_to].scaledPayout = users[_to].scaledPayout.add(
            int256(_amount.mul(divPerShare))
        );
        emit OnTransfer(msg.sender, _to, _amount);
    }
}
function distribute(uint256 _amount) external returns(bool) {
    WETH.safeTransferFrom(address(msg.sender), address(this), _amount);
    divPerShare = divPerShare.add((_amount.mul(FLOAT_SCALAR)).div(totalSupply()));
}

//@dev deposit dust token upon deployment to prevent division by zero 