ETH Price: $2,346.01 (-24.69%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Date

Compiler Version
v0.6.6+commit.6c089d02

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2020-09-04
*/

// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;


library Date {
    struct _Date {
        uint16 year;
        uint8 month;
        uint8 day;
    }

    uint constant DAY_IN_SECONDS = 86400;
    uint constant YEAR_IN_SECONDS = 31536000;
    uint constant LEAP_YEAR_IN_SECONDS = 31622400;

    uint16 constant ORIGIN_YEAR = 1970;

    function isLeapYear(uint16 year) public pure returns (bool) {
        if (year % 4 != 0) {
                return false;
        }
        if (year % 100 != 0) {
                return true;
        }
        if (year % 400 != 0) {
                return false;
        }
        return true;
    }

    function leapYearsBefore(uint year) public pure returns (uint) {
        year -= 1;
        return year / 4 - year / 100 + year / 400;
    }

    function getDaysInMonth(uint8 month, uint16 year) public pure returns (uint8) {
        if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
                return 31;
        }
        else if (month == 4 || month == 6 || month == 9 || month == 11) {
                return 30;
        }
        else if (isLeapYear(year)) {
                return 29;
        }
        else {
                return 28;
        }
    }

    function parseTimestamp(uint timestamp) internal pure returns (_Date memory dt) {
        uint secondsAccountedFor = 0;
        uint buf;
        uint8 i;

        // Year
        dt.year = getYear(timestamp);
        buf = leapYearsBefore(dt.year) - leapYearsBefore(ORIGIN_YEAR);

        secondsAccountedFor += LEAP_YEAR_IN_SECONDS * buf;
        secondsAccountedFor += YEAR_IN_SECONDS * (dt.year - ORIGIN_YEAR - buf);

        // Month
        uint secondsInMonth;
        for (i = 1; i <= 12; i++) {
                secondsInMonth = DAY_IN_SECONDS * getDaysInMonth(i, dt.year);
                if (secondsInMonth + secondsAccountedFor > timestamp) {
                        dt.month = i;
                        break;
                }
                secondsAccountedFor += secondsInMonth;
        }

        // Day
        for (i = 1; i <= getDaysInMonth(dt.month, dt.year); i++) {
                if (DAY_IN_SECONDS + secondsAccountedFor > timestamp) {
                        dt.day = i;
                        break;
                }
                secondsAccountedFor += DAY_IN_SECONDS;
        }
    }

    function getYear(uint timestamp) public pure returns (uint16) {
        uint secondsAccountedFor = 0;
        uint16 year;
        uint numLeapYears;

        // Year
        year = uint16(ORIGIN_YEAR + timestamp / YEAR_IN_SECONDS);
        numLeapYears = leapYearsBefore(year) - leapYearsBefore(ORIGIN_YEAR);

        secondsAccountedFor += LEAP_YEAR_IN_SECONDS * numLeapYears;
        secondsAccountedFor += YEAR_IN_SECONDS * (year - ORIGIN_YEAR - numLeapYears);

        while (secondsAccountedFor > timestamp) {
                if (isLeapYear(uint16(year - 1))) {
                        secondsAccountedFor -= LEAP_YEAR_IN_SECONDS;
                }
                else {
                        secondsAccountedFor -= YEAR_IN_SECONDS;
                }
                year -= 1;
        }
        return year;
    }

    function getMonth(uint timestamp) public pure returns (uint8) {
        return parseTimestamp(timestamp).month;
    }

    function getDay(uint timestamp) public pure returns (uint8) {
        return parseTimestamp(timestamp).day;
    }

    function toTimestamp(uint16 year, uint8 month, uint8 day) public pure returns (uint timestamp) {
        uint16 i;

        // Year
        for (i = ORIGIN_YEAR; i < year; i++) {
                if (isLeapYear(i)) {
                        timestamp += LEAP_YEAR_IN_SECONDS;
                }
                else {
                        timestamp += YEAR_IN_SECONDS;
                }
        }

        // Month
        uint8[12] memory monthDayCounts;
        monthDayCounts[0] = 31;
        if (isLeapYear(year)) {
                monthDayCounts[1] = 29;
        }
        else {
                monthDayCounts[1] = 28;
        }
        monthDayCounts[2] = 31;
        monthDayCounts[3] = 30;
        monthDayCounts[4] = 31;
        monthDayCounts[5] = 30;
        monthDayCounts[6] = 31;
        monthDayCounts[7] = 31;
        monthDayCounts[8] = 30;
        monthDayCounts[9] = 31;
        monthDayCounts[10] = 30;
        monthDayCounts[11] = 31;

        for (i = 1; i < month; i++) {
                timestamp += DAY_IN_SECONDS * monthDayCounts[i - 1];
        }

        // Day
        timestamp += DAY_IN_SECONDS * (day - 1);

        return timestamp;
    }
}


contract Ownable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev The Ownable constructor sets the original `owner` of the contract to the sender
     * account.
     */
    constructor () internal {
        _owner = msg.sender;
        emit OwnershipTransferred(address(0), _owner);
    }

    /**
     * @return the address of the owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(isOwner());
        _;
    }

    /**
     * @return true if `msg.sender` is the owner of the contract.
     */
    function isOwner() public view returns (bool) {
        return msg.sender == _owner || tx.origin == _owner;
    }

    /**
     * @dev Allows the current owner to transfer control of the contract to a newOwner.
     * @param newOwner The address to transfer ownership to.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers control of the contract to a newOwner.
     * @param newOwner The address to transfer ownership to.
     */
    function _transferOwnership(address newOwner) internal {
        require(newOwner != address(0));
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

interface IERC20 {
    function transfer(address to, uint256 value) external returns (bool);

    function approve(address spender, uint256 value) external returns (bool);

    function transferFrom(address from, address to, uint256 value) external returns (bool);

    function totalSupply() external view returns (uint256);

    function balanceOf(address who) external view returns (uint256);

    function allowance(address owner, address spender) external view returns (uint256);

    event Transfer(address indexed from, address indexed to, uint256 value);

    event Approval(address indexed owner, address indexed spender, uint256 value);
}

library SafeMath {
    function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
        c = a + b;
        require(c >= a);
        return c;
    }

    function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
        if (a == 0) {
            return 0;
        }
        c = a * b;
        require(c / a == b);
        return c;
    }
    
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }
    
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }
    
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }
    
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }
}

contract StakingPool is Ownable {
    using SafeMath for uint256;

    IERC20 token;
    uint256 decimals = 18;
    uint256 minimumStakeAmount = 1000;
    address ZERO_ADDRESS = 0x0000000000000000000000000000000000000000;
    address BURN_ADDRESS;

    //Stats
    uint256 public totalStakes = 0;
    uint256 public totalStaked = 0;
    uint256 public adminCanWithdraw = 0;
    mapping(uint8 => uint256) public totalByLockup;
    uint256 public totalCompounding = 0;
    uint256 public totalNotCompounding = 0;

    struct Stake {
        bool exists;
        uint256 createdOn;
        uint256 initialAmount;
        bool compound;
        uint8 lockupPeriod;
        uint256 withdrawn;
        address referrer;
    }
    
    mapping(address => Stake) public stakes;
    
    uint256 DEFAULT_ROI1 = 13; //0.13% daily ROI, equivalent to 4% monthly
    uint256 DEFAULT_ROI2 = 15; //0.15% daily ROI
    uint256 DEFAULT_ROI3 = 17; //0.17% daily ROI
    uint256 DEFAULT_ROI6 = 19; //0.19% daily ROI
    
    bool isValidLockup1 = true;
    bool isValidLockup2 = false;
    bool isValidLockup3 = false;
    bool isValidLockup6 = false;

    struct ROI {
        bool exists;
        uint256 roi1;
        uint256 roi2;
        uint256 roi3;
        uint256 roi6;
    }

    //Year to Month to ROI
    mapping (uint256 => mapping (uint256 => ROI)) private rois;
    
    event NewStake(address indexed staker, uint256 totalStaked, uint8 lockupPeriod, bool compound, address referrer);
    event StakeIncreasedForReferral(address indexed staker, uint256 initialAmount, uint256 delta);
    event RewardsWithdrawn(address indexed staker, uint256 total);
    event StakeFinished(address indexed staker, uint256 totalReturned, uint256 totalDeducted);
    event TokensBurnt(address indexed staker, uint256 totalBurnt);
    
    constructor(IERC20 _token, address _burnAddress) public {
        token = _token;
        BURN_ADDRESS = _burnAddress;
    }
    
    function createStake(uint256 _amount, uint8 _lockupPeriod, bool _compound, address _referrer) public {
        require(!stakes[msg.sender].exists, "You already have a stake");
        require(_isValidLockupPeriod(_lockupPeriod), "Invalid lockup period");
        require(_amount >= getMinimumStakeAmount(), "Invalid minimum");
        
        require(IERC20(token).transferFrom(msg.sender, address(this), calculateTotalWithDecimals(_amount)), "Couldn't take the tokens");
        
        if (_referrer != address(0) && stakes[_referrer].exists) {
            uint256 amountToIncrease = stakes[_referrer].initialAmount.mul(1).div(100);
            emit StakeIncreasedForReferral(_referrer, stakes[_referrer].initialAmount, amountToIncrease);
            stakes[_referrer].initialAmount += amountToIncrease;
            totalStaked = totalStaked.add(amountToIncrease); 
        }
        else {
            _referrer = ZERO_ADDRESS;
        }

        Stake memory stake = Stake({exists:true,
                                    createdOn: now, 
                                    initialAmount:_amount, 
                                    compound:_compound, 
                                    lockupPeriod:_lockupPeriod, 
                                    withdrawn:0,
                                    referrer:_referrer
        });
                                    
        stakes[msg.sender] = stake;
        totalStakes = totalStakes.add(1);
        totalStaked = totalStaked.add(_amount);
        totalByLockup[_lockupPeriod] += 1;
        if (_compound) {
            totalCompounding = totalCompounding.add(1);
        } else {
            totalNotCompounding = totalNotCompounding.add(1);
        }
        
        emit NewStake(msg.sender, _amount, _lockupPeriod, _compound, _referrer);
    }
    
    function withdraw() public {
        require(stakes[msg.sender].exists, "Invalid stake");
        require(!stakes[msg.sender].compound, "Compounders can't withdraw before they finish their stake");

        Stake storage stake = stakes[msg.sender];
        uint256 total = getPartialToWidthdrawForNotCompounders(msg.sender, now);
        stake.withdrawn += total;
        
        require(token.transfer(msg.sender, calculateTotalWithDecimals(total)), "Couldn't withdraw");

        emit RewardsWithdrawn(msg.sender, total);
    }
    
    function finishStake() public {
        require(stakes[msg.sender].exists, "Invalid stake");
        
        Stake memory stake = stakes[msg.sender];
        
        uint256 finishesOn = _calculateFinishTimestamp(stake.createdOn, stake.lockupPeriod);
        require(now > finishesOn || !stake.compound, "Can't be finished yet");
        
        uint256 totalRewards;
        uint256 totalFees;
        uint256 totalPenalty;
        
        if (stake.compound) {
            totalRewards = getTotalToWidthdrawForCompounders(msg.sender); //This includes the initial amount
            totalRewards = totalRewards.sub(stake.initialAmount);
            totalFees = totalRewards.mul(5).div(100); //Flat fee of 5%
        }
        else {
            if (now > finishesOn) {
                totalRewards = getTotalToWidthdrawForNotCompounders(msg.sender);
            }  
            else {
                totalRewards = getPartialToWidthdrawForNotCompounders(msg.sender, now);
                //As it didn't finish, pay a fee of 10% (before first half) or 5% (after first half)
                uint8 penalty = _isFirstHalf(stake.createdOn, stake.lockupPeriod) ? 10 : 5;
                totalPenalty = totalRewards.mul(penalty).div(100);
            }
            totalFees = totalRewards.mul(2).div(100); //Flat fee of 2%
        }
        
        uint256 totalToDeduct = totalFees.add(totalPenalty);
        uint256 totalToTransfer = totalRewards.add(stake.initialAmount).sub(totalToDeduct);
        
        //10% of the fees are for the Admin.
        adminCanWithdraw = adminCanWithdraw.add(totalToDeduct.div(10));
        //The rest are burnt
        uint256 totalToBurn = totalToDeduct.mul(9).div(10);
        
        require(token.transfer(BURN_ADDRESS, calculateTotalWithDecimals(totalToBurn)), "Couldn't burn the tokens");
        emit TokensBurnt(msg.sender, totalToBurn);

        totalStakes = totalStakes.sub(1);
        totalStaked = totalStaked.sub(stake.initialAmount);
        totalByLockup[stake.lockupPeriod] = totalByLockup[stake.lockupPeriod].sub(1);
        if (stake.compound) {
            totalCompounding = totalCompounding.sub(1);
        } else {
            totalNotCompounding = totalNotCompounding.sub(1);
        }
        delete stakes[msg.sender];

        require(token.transfer(msg.sender, calculateTotalWithDecimals(totalToTransfer)), "Couldn't transfer the tokens");
        
        emit StakeFinished(msg.sender, totalToTransfer, totalToDeduct);
    }
    
    function calculateTotalWithDecimals(uint256 _amount) internal view returns (uint256) {
        return _amount * 10 ** decimals;
    }
    
    function _isFirstHalf(uint256 _createdOn, uint8 _lockupPeriod) internal view returns (bool) {
        uint256 day = 60 * 60 * 24;
        
        if (_lockupPeriod == 1) {
            return _createdOn + day.mul(15) > now;
        }
        if (_lockupPeriod == 2) {
            return _createdOn + day.mul(30) > now;
        }
        if (_lockupPeriod == 3) {
            return _createdOn + day.mul(45) > now;
        }
        return _createdOn + day.mul(90) > now;
    }
    
    function calcPartialRewardsForInitialMonth(Stake memory stake, uint8 _todayDay, Date._Date memory _initial, bool compounding) internal view returns (uint256) {
        uint256 roi = getRoi(_initial.month, _initial.year, stake.lockupPeriod);
        uint8 totalDays = _todayDay - _initial.day;
        return calculateRewards(stake.initialAmount, totalDays, roi, compounding);
    }

    function calcFullRewardsForInitialMonth(Stake memory stake, Date._Date memory _initial, bool compounding) internal view returns (uint256) {
        uint8 totalDays = Date.getDaysInMonth(_initial.month, _initial.year);
        uint256 roi = getRoi(_initial.month, _initial.year, stake.lockupPeriod);
        uint8 countDays = totalDays - _initial.day;
        return calculateRewards(stake.initialAmount, countDays, roi, compounding);
    }
    
    function calcFullRewardsForMonth(uint256 _currentTotal, uint256 _roi, uint16 _year, uint8 _month, bool compounding) internal pure returns (uint256) {
        uint256 totalDays = Date.getDaysInMonth(_month, _year);
        return calculateRewards(_currentTotal, totalDays, _roi, compounding);
    }
    
    function calculateRewards(uint256 currentTotal, uint256 totalDays, uint256 roi, bool compounding) internal pure returns (uint256) {
        if (compounding) {
            uint256 divFactor = 10000 ** 10;
            while(totalDays > 10) {
                currentTotal = currentTotal.mul(roi.add(10000) ** 10).div(divFactor);
                totalDays -= 10;
            }
            return currentTotal = currentTotal.mul(roi.add(10000) ** totalDays).div(10000 ** totalDays);
        }
        
        //Not compounding
        return currentTotal.mul(totalDays).mul(roi).div(10000);
    }
    
    //This function is meant to be called internally when finishing your stake
    function getTotalToWidthdrawForNotCompounders(address _account) internal view returns (uint256) {
        Stake memory stake = stakes[_account];
        
        Date._Date memory initial = Date.parseTimestamp(stake.createdOn);
        
        uint256 total = calcFullRewardsForInitialMonth(stake, initial, false);
        
        uint256 finishTimestamp = _calculateFinishTimestamp(stake.createdOn, stake.lockupPeriod);
        Date._Date memory finishes = Date.parseTimestamp(finishTimestamp);
        
        for(uint8 i=1;i<=stake.lockupPeriod;i++) {
            uint8 currentMonth = initial.month + i;
            uint16 currentYear = initial.year;
            if (currentMonth > 12) {
                currentYear += 1;
                currentMonth = currentMonth % 12;
            }

            uint256 roi = getRoi(currentMonth, currentYear ,stake.lockupPeriod);

            //This is the month it finishes on
            if (currentMonth == finishes.month) {
                //Calculates partial rewards for month
                total += calculateRewards(stake.initialAmount, finishes.day, roi, false);
                break;
            }
            
            //This is a complete month I need to add
            total += calcFullRewardsForMonth(stake.initialAmount, roi, currentYear, currentMonth, false);
        }
        
        total = total.sub(stake.withdrawn);
        return total;
    }
    
    //This function is meant to be called internally when withdrawing as much as you can, or by the UI
    function getPartialToWidthdrawForNotCompounders(address _account, uint256 _now) public view returns (uint256) {
        Stake memory stake = stakes[_account];
        
        Date._Date memory initial = Date.parseTimestamp(stake.createdOn);
        Date._Date memory today = Date.parseTimestamp(_now);
        
        //I am still in my first month of staking
        if (initial.month == today.month) {
            uint256 total = calcPartialRewardsForInitialMonth(stake, today.day, initial, false);
            total = total.sub(stake.withdrawn);
            return total;
        }
        
        //I am in a month after my first month of staking
        uint256 total = calcFullRewardsForInitialMonth(stake, initial, false);
        
        uint256 finishTimestamp = _calculateFinishTimestamp(stake.createdOn, stake.lockupPeriod);
        Date._Date memory finishes = Date.parseTimestamp(finishTimestamp);
        
        for(uint8 i=1;i<=stake.lockupPeriod;i++) {
            uint8 currentMonth = initial.month + i;
            uint16 currentYear = initial.year;
            if (currentMonth > 12) {
                currentYear += 1;
                currentMonth = currentMonth % 12;
            }

            uint256 roi = getRoi(currentMonth, currentYear, stake.lockupPeriod);

            //This is the month it finishes
            if (currentMonth == finishes.month) {
                uint8 upToDay = _getMin(finishes.day, today.day);
                //Calculates partial rewards for month
                total += calculateRewards(stake.initialAmount, upToDay, roi, false);
                break;
            }
            else if (currentMonth == today.month) { // We reached the current month
                //Calculates partial rewards for month
                total += calculateRewards(stake.initialAmount, today.day, roi, false);
                break;
            }
            
            //This is a complete month I need to add
            total += calcFullRewardsForMonth(stake.initialAmount, roi, currentYear, currentMonth, false);
        }
        
        total = total.sub(stake.withdrawn);
        return total;
    }
    
    //This function is meant to be called internally on finishing your stake
    function getTotalToWidthdrawForCompounders(address _account) internal view returns (uint256) {
        Stake memory stake = stakes[_account];
        
        Date._Date memory initial = Date.parseTimestamp(stake.createdOn);
        
        uint256 total = calcFullRewardsForInitialMonth(stake, initial, true);
        
        uint256 finishTimestamp = _calculateFinishTimestamp(stake.createdOn, stake.lockupPeriod);
        Date._Date memory finishes = Date.parseTimestamp(finishTimestamp);
        
        for(uint8 i=1;i<=stake.lockupPeriod;i++) {
            uint8 currentMonth = initial.month + i;
            uint16 currentYear = initial.year;
            if (currentMonth > 12) {
                currentYear += 1;
                currentMonth = currentMonth % 12;
            }

            uint256 roi = getRoi(currentMonth, currentYear, stake.lockupPeriod);

            //This is the month it finishes on
            if (currentMonth == finishes.month) {
                //Calculates partial rewards for month
                return calculateRewards(total, finishes.day, roi, true);
            }
            
            //This is a complete month I need to add
            total = calcFullRewardsForMonth(total, roi, currentYear, currentMonth, true);
        }
    }
    
    //This function is meant to be called from the UI
    function getPartialRewardsForCompounders(address _account, uint256 _now) public view returns (uint256) {
        Stake memory stake = stakes[_account];
        
        Date._Date memory initial = Date.parseTimestamp(stake.createdOn);
        Date._Date memory today = Date.parseTimestamp(_now);
        
        //I am still in my first month of staking
        if (initial.month == today.month) {
            uint256 total = calcPartialRewardsForInitialMonth(stake, today.day, initial, true);
            total = total.sub(stake.withdrawn);
            return total;
        }
        
        //I am in a month after my first month of staking
        uint256 total = calcFullRewardsForInitialMonth(stake, initial, true);
        
        uint256 finishTimestamp = _calculateFinishTimestamp(stake.createdOn, stake.lockupPeriod);
        Date._Date memory finishes = Date.parseTimestamp(finishTimestamp);
        
        for(uint8 i=1;i<=stake.lockupPeriod;i++) {
            uint8 currentMonth = initial.month + i;
            uint16 currentYear = initial.year;
            if (currentMonth > 12) {
                currentYear += 1;
                currentMonth = currentMonth % 12;
            }

            uint256 roi = getRoi(currentMonth, currentYear, stake.lockupPeriod);

            //This is the month it finishes
            if (currentMonth == finishes.month) {
                uint8 upToDay = _getMin(finishes.day, today.day);
                //Calculates partial rewards for month
                return calculateRewards(total, upToDay, roi, true);
            }
            else if (currentMonth == today.month) { // We reached the current month
                //Calculates partial rewards for month
                return calculateRewards(total, today.day, roi, true);
            }
            
            //This is a complete month I need to add
            total = calcFullRewardsForMonth(total, roi, currentYear, currentMonth, true);
        }
    }
    
    function _getMin(uint8 num1, uint8 num2) internal pure returns (uint8) {
        if (num1 < num2) {
            return num1;
        }
        
        return num2;
    }
    
    function calculateFinishTimestamp(address account) public view returns (uint256) {
        return _calculateFinishTimestamp(stakes[account].createdOn, stakes[account].lockupPeriod);
    }
    
    function _calculateFinishTimestamp(uint256 _timestamp, uint8 _lockupPeriod) internal pure returns (uint256) {
        uint16 year = Date.getYear(_timestamp);
        uint8 month = Date.getMonth(_timestamp);
        month += _lockupPeriod;
        if (month > 12) {
            year += 1;
            month = month % 12;
        }
        uint8 day = Date.getDay(_timestamp);
        return Date.toTimestamp(year, month, day);
    }
    
    function _isValidLockupPeriod(uint8 n) public view returns (bool) {
        return (isValidLockup1 && n == 1) || (isValidLockup2 && n == 2) || (isValidLockup3 && n == 3) || (isValidLockup6 && n == 6);
    }
    
    function _setValidLockups(bool _isValidLockup1, bool _isValidLockup2, bool _isValidLockup3, bool _isValidLockup6) public onlyOwner {
        isValidLockup1 = _isValidLockup1;
        isValidLockup2 = _isValidLockup2;
        isValidLockup3 = _isValidLockup3;
        isValidLockup6 = _isValidLockup6;
    }
    
    function _adminWithdraw() public onlyOwner {
        uint256 amount = adminCanWithdraw;
        adminCanWithdraw = 0;
        require(token.transfer(msg.sender, calculateTotalWithDecimals(amount)), "Couldn't withdraw");
    }

    function _extractDESTSentByMistake(uint256 amount, address _sendTo) public onlyOwner {
        require(token.transfer(_sendTo, amount));
    }
    
    function _setMinimumStakeAmount(uint256 _minimumStakeAmount) public onlyOwner {
        minimumStakeAmount = _minimumStakeAmount;
    }

    function getMinimumStakeAmount() public view returns (uint256) {
        return minimumStakeAmount;
    }
    
    function _setRoi(uint256 _month, uint256 _year, uint256 _roi1, uint256 _roi2, uint256 _roi3, uint256 _roi6) public onlyOwner {
        uint256 today_year = Date.getYear(now);
        uint256 today_month = Date.getMonth(now);
        
        require((_month >= today_month  && _year == today_year) || _year > today_year, "You can only set it for this month or a future month");
        
        rois[_year][_month].exists = true;
        rois[_year][_month].roi1 = _roi1;
        rois[_year][_month].roi2 = _roi2;
        rois[_year][_month].roi3 = _roi3;
        rois[_year][_month].roi6 = _roi6;
    }
    
    function _setDefaultRoi(uint256 _roi1, uint256 _roi2, uint256 _roi3, uint256 _roi6) public onlyOwner {
        DEFAULT_ROI1 = _roi1;
        DEFAULT_ROI2 = _roi2;
        DEFAULT_ROI3 = _roi3;
        DEFAULT_ROI6 = _roi6;
    }
    
    function getRoi(uint256 month, uint256 year, uint8 lockupPeriod) public view returns (uint256) {
        if (rois[year][month].exists) {
            if (lockupPeriod == 1) {
                return rois[year][month].roi1;
            }
            else if (lockupPeriod == 2) {
                return rois[year][month].roi2;
            }
            else if (lockupPeriod == 3) {
                return rois[year][month].roi3;
            }
            else if (lockupPeriod == 6) {
                return rois[year][month].roi6;
            }
        }
        
        if (lockupPeriod == 1) {
            return DEFAULT_ROI1;
        }
        else if (lockupPeriod == 2) {
            return DEFAULT_ROI2;
        }
        else if (lockupPeriod == 3) {
            return DEFAULT_ROI3;
        }
        else if (lockupPeriod == 6) {
            return DEFAULT_ROI6;
        }
        
        return 0;
    }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getDay","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint8","name":"month","type":"uint8"},{"internalType":"uint16","name":"year","type":"uint16"}],"name":"getDaysInMonth","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getMonth","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getYear","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint16","name":"year","type":"uint16"}],"name":"isLeapYear","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"year","type":"uint256"}],"name":"leapYearsBefore","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint16","name":"year","type":"uint16"},{"internalType":"uint8","name":"month","type":"uint8"},{"internalType":"uint8","name":"day","type":"uint8"}],"name":"toTimestamp","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"pure","type":"function"}]

610656610026600b82828239805160001a60731461001957fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106100875760003560e01c8063a324ad2411610065578063a324ad2414610137578063a6f0e57714610154578063b199993714610189578063b238ad0e146101a657610087565b806365c728401461008c5780638c8d98a0146100bf57806392d6631314610103575b600080fd5b6100a9600480360360208110156100a257600080fd5b50356101d0565b6040805160ff9092168252519081900360200190f35b6100f1600480360360608110156100d557600080fd5b5061ffff8135169060ff602082013581169160400135166101e7565b60408051918252519081900360200190f35b6101206004803603602081101561011957600080fd5b50356102ff565b6040805161ffff9092168252519081900360200190f35b6100a96004803603602081101561014d57600080fd5b503561038f565b6101756004803603602081101561016a57600080fd5b503561ffff166103a4565b604080519115158252519081900360200190f35b6100f16004803603602081101561019f57600080fd5b50356103f6565b6100a9600480360360408110156101bc57600080fd5b50803560ff16906020013561ffff16610411565b60006101db826104d9565b6040015190505b919050565b60006107b25b8461ffff168161ffff16101561022b57610206816103a4565b15610219576301e2850082019150610223565b6301e13380820191505b6001016101ed565b6102336105e1565b601f8152610240866103a4565b1561025157601d6020820152610259565b601c60208201525b601f60408201819052601e606083018190526080830182905260a0830181905260c0830182905260e0830182905261010083018190526101208301829052610140830152610160820152600191505b8460ff168261ffff1610156102e857806001830361ffff16600c81106102ca57fe5b602002015160ff1662015180028301925081806001019250506102a8565b50506201518060ff60001984011602019392505050565b6000806107b26301e133808404810190829061031a906103f6565b6103278361ffff166103f6565b039050806301e285000283019250806107b2830361ffff16036301e1338002830192505b848311156103875761035f600183036103a4565b15610372576301e285008303925061037c565b6301e13380830392505b60018203915061034b565b509392505050565b600061039a826104d9565b6020015192915050565b600060038216156103b7575060006101e2565b606461ffff83160661ffff166000146103d2575060016101e2565b61019061ffff83160661ffff166000146103ee575060006101e2565b506001919050565b60001901600061019082046064830460048404030192915050565b60008260ff166001148061042857508260ff166003145b8061043657508260ff166005145b8061044457508260ff166007145b8061045257508260ff166008145b8061046057508260ff16600a145b8061046e57508260ff16600c145b1561047b5750601f6104d3565b8260ff166004148061049057508260ff166006145b8061049e57508260ff166009145b806104ac57508260ff16600b145b156104b95750601e6104d3565b6104c2826103a4565b156104cf5750601d6104d3565b50601c5b92915050565b6104e1610600565b600080806104ee856102ff565b61ffff1684526104ff6107b26103f6565b845161050e9061ffff166103f6565b039150816301e285000283019250816107b285600001510361ffff16036301e1338002830192506000600191505b600c8260ff161161058657610555828660000151610411565b60ff16620151800290508584820111156105775760ff82166020860152610586565b9283019260019091019061053c565b600191505b61059d85602001518660000151610411565b60ff168260ff16116105d8578584620151800111156105c45760ff821660408601526105d8565b62015180939093019260019091019061058b565b50505050919050565b604051806101800160405280600c906020820280368337509192915050565b60408051606081018252600080825260208201819052918101919091529056fea2646970667358221220f400713900a965d94c09094fe7f0a47d580be88e84f763265c894a7624bed86464736f6c63430006060033

Deployed Bytecode

0x73d4dec04810fcb50ac2332b332a333f7211162c2630146080604052600436106100875760003560e01c8063a324ad2411610065578063a324ad2414610137578063a6f0e57714610154578063b199993714610189578063b238ad0e146101a657610087565b806365c728401461008c5780638c8d98a0146100bf57806392d6631314610103575b600080fd5b6100a9600480360360208110156100a257600080fd5b50356101d0565b6040805160ff9092168252519081900360200190f35b6100f1600480360360608110156100d557600080fd5b5061ffff8135169060ff602082013581169160400135166101e7565b60408051918252519081900360200190f35b6101206004803603602081101561011957600080fd5b50356102ff565b6040805161ffff9092168252519081900360200190f35b6100a96004803603602081101561014d57600080fd5b503561038f565b6101756004803603602081101561016a57600080fd5b503561ffff166103a4565b604080519115158252519081900360200190f35b6100f16004803603602081101561019f57600080fd5b50356103f6565b6100a9600480360360408110156101bc57600080fd5b50803560ff16906020013561ffff16610411565b60006101db826104d9565b6040015190505b919050565b60006107b25b8461ffff168161ffff16101561022b57610206816103a4565b15610219576301e2850082019150610223565b6301e13380820191505b6001016101ed565b6102336105e1565b601f8152610240866103a4565b1561025157601d6020820152610259565b601c60208201525b601f60408201819052601e606083018190526080830182905260a0830181905260c0830182905260e0830182905261010083018190526101208301829052610140830152610160820152600191505b8460ff168261ffff1610156102e857806001830361ffff16600c81106102ca57fe5b602002015160ff1662015180028301925081806001019250506102a8565b50506201518060ff60001984011602019392505050565b6000806107b26301e133808404810190829061031a906103f6565b6103278361ffff166103f6565b039050806301e285000283019250806107b2830361ffff16036301e1338002830192505b848311156103875761035f600183036103a4565b15610372576301e285008303925061037c565b6301e13380830392505b60018203915061034b565b509392505050565b600061039a826104d9565b6020015192915050565b600060038216156103b7575060006101e2565b606461ffff83160661ffff166000146103d2575060016101e2565b61019061ffff83160661ffff166000146103ee575060006101e2565b506001919050565b60001901600061019082046064830460048404030192915050565b60008260ff166001148061042857508260ff166003145b8061043657508260ff166005145b8061044457508260ff166007145b8061045257508260ff166008145b8061046057508260ff16600a145b8061046e57508260ff16600c145b1561047b5750601f6104d3565b8260ff166004148061049057508260ff166006145b8061049e57508260ff166009145b806104ac57508260ff16600b145b156104b95750601e6104d3565b6104c2826103a4565b156104cf5750601d6104d3565b50601c5b92915050565b6104e1610600565b600080806104ee856102ff565b61ffff1684526104ff6107b26103f6565b845161050e9061ffff166103f6565b039150816301e285000283019250816107b285600001510361ffff16036301e1338002830192506000600191505b600c8260ff161161058657610555828660000151610411565b60ff16620151800290508584820111156105775760ff82166020860152610586565b9283019260019091019061053c565b600191505b61059d85602001518660000151610411565b60ff168260ff16116105d8578584620151800111156105c45760ff821660408601526105d8565b62015180939093019260019091019061058b565b50505050919050565b604051806101800160405280600c906020820280368337509192915050565b60408051606081018252600080825260208201819052918101919091529056fea2646970667358221220f400713900a965d94c09094fe7f0a47d580be88e84f763265c894a7624bed86464736f6c63430006060033

Deployed Bytecode Sourcemap

64:4735:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;12:1:-1;9;2:12;3459:115:0;;;;;;15:2:-1;10:3;7:11;4:2;;;31:1;28;21:12;4:2;-1:-1;3459:115:0;;:::i;:::-;;;;;;;;;;;;;;;;;;;3582:1214;;;;;;15:2:-1;10:3;7:11;4:2;;;31:1;28;21:12;4:2;-1:-1;3582:1214:0;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;2479:845;;;;;;15:2:-1;10:3;7:11;4:2;;;31:1;28;21:12;4:2;-1:-1;2479:845:0;;:::i;:::-;;;;;;;;;;;;;;;;;;;3332:119;;;;;;15:2:-1;10:3;7:11;4:2;;;31:1;28;21:12;4:2;-1:-1;3332:119:0;;:::i;364:309::-;;;;;;15:2:-1;10:3;7:11;4:2;;;31:1;28;21:12;4:2;-1:-1;364:309:0;;;;:::i;:::-;;;;;;;;;;;;;;;;;;681:143;;;;;;15:2:-1;10:3;7:11;4:2;;;31:1;28;21:12;4:2;-1:-1;681:143:0;;:::i;832:484::-;;;;;;15:2:-1;10:3;7:11;4:2;;;31:1;28;21:12;4:2;-1:-1;832:484:0;;;;;;;;;;;:::i;3459:115::-;3512:5;3537:25;3552:9;3537:14;:25::i;:::-;:29;;;3530:36;;3459:115;;;;:::o;3582:1214::-;3661:14;351:4;3726:264;3752:4;3748:8;;:1;:8;;;3726:264;;;3786:13;3797:1;3786:10;:13::i;:::-;3782:197;;;304:8;3828:33;;;;3782:197;;;252:8;3931:28;;;;3782:197;3758:3;;3726:264;;;4020:31;;:::i;:::-;4082:2;4062:22;;4099:16;4110:4;4099:10;:16::i;:::-;4095:143;;;4156:2;4136:17;;;:22;4095:143;;;4224:2;4204:17;;;:22;4095:143;4268:2;4248:17;;;:22;;;4301:2;4281:17;;;:22;;;4314:17;;;:22;;;4347:17;;;:22;;;4380:17;;;:22;;;4413:17;;;:22;;;4446:17;;;:22;;;4479:17;;;:22;;;4512:18;;;:23;4546:18;;;:23;4591:1;;-1:-1:-1;4582:110:0;4598:5;4594:9;;:1;:9;;;4582:110;;;4659:14;4678:1;4674;:5;4659:21;;;;;;;;;;;;;4642:38;;208:5;4642:38;4629:51;;;;4605:3;;;;;;;4582:110;;;-1:-1:-1;;208:5:0;4733:26;-1:-1:-1;;4751:7:0;;4733:26;;4720:39;3582:1214;;;;;:::o;2479:845::-;2533:6;;351:4;252:8;2688:27;;2674:41;;;2533:6;;2766:28;;:15;:28::i;:::-;2742:21;2758:4;2742:21;;:15;:21::i;:::-;:52;2727:67;;2853:12;304:8;2830:35;2807:58;;;;2939:12;351:4;2918;:18;:33;;;252:8;2899:53;2876:76;;;;2965:330;2994:9;2972:19;:31;2965:330;;;3028:28;3053:1;3046:4;:8;3028:10;:28::i;:::-;3024:232;;;304:8;3085:43;;;;3024:232;;;252:8;3198:38;;;;3024:232;3282:1;3274:9;;;;2965:330;;;-1:-1:-1;3312:4:0;2479:845;-1:-1:-1;;;2479:845:0:o;3332:119::-;3387:5;3412:25;3427:9;3412:14;:25::i;:::-;:31;;;;3332:119;-1:-1:-1;;3332:119:0:o;364:309::-;418:4;439:8;;;:13;435:62;;-1:-1:-1;480:5:0;473:12;;435:62;518:3;511:10;;;;:15;;525:1;511:15;507:63;;-1:-1:-1;554:4:0;547:11;;507:63;591:3;584:10;;;;:15;;598:1;584:15;580:64;;-1:-1:-1;627:5:0;620:12;;580:64;-1:-1:-1;661:4:0;364:309;;;:::o;681:143::-;-1:-1:-1;;755:9:0;738:4;813:3;755:9;806:10;800:3;793:4;:10;789:1;782:4;:8;:21;:34;;681:143;-1:-1:-1;;681:143:0:o;832:484::-;903:5;925;:10;;934:1;925:10;:24;;;;939:5;:10;;948:1;939:10;925:24;:38;;;;953:5;:10;;962:1;953:10;925:38;:52;;;;967:5;:10;;976:1;967:10;925:52;:66;;;;981:5;:10;;990:1;981:10;925:66;:81;;;;995:5;:11;;1004:2;995:11;925:81;:96;;;;1010:5;:11;;1019:2;1010:11;925:96;921:388;;;-1:-1:-1;1049:2:0;1042:9;;921:388;1082:5;:10;;1091:1;1082:10;:24;;;;1096:5;:10;;1105:1;1096:10;1082:24;:38;;;;1110:5;:10;;1119:1;1110:10;1082:38;:53;;;;1124:5;:11;;1133:2;1124:11;1082:53;1078:231;;;-1:-1:-1;1163:2:0;1156:9;;1078:231;1196:16;1207:4;1196:10;:16::i;:::-;1192:117;;;-1:-1:-1;1240:2:0;1233:9;;1192:117;-1:-1:-1;1295:2:0;1192:117;832:484;;;;:::o;1324:1147::-;1387:15;;:::i;:::-;1415:24;;;1520:18;1528:9;1520:7;:18::i;:::-;1510:28;;;;1582;351:4;1582:15;:28::i;:::-;1571:7;;1555:24;;;;:15;:24::i;:::-;:55;1549:61;;1669:3;304:8;1646:26;1623:49;;;;1749:3;351:4;1725:2;:7;;;:21;:27;;;252:8;1706:47;1683:70;;;;1784:19;1823:1;1819:5;;1814:336;1831:2;1826:1;:7;;;1814:336;;1893:26;1908:1;1911:2;:7;;;1893:14;:26::i;:::-;1876:43;;208:5;1876:43;1859:60;;1981:9;1959:19;1942:14;:36;:48;1938:145;;;2019:12;;;:8;;;:12;2058:5;;1938:145;2101:37;;;;1835:3;;;;;1814:336;;;2187:1;2183:5;;2178:286;2195:33;2210:2;:8;;;2220:2;:7;;;2195:14;:33::i;:::-;2190:38;;:1;:38;;;2178:286;;2297:9;2275:19;208:5;2258:36;:48;2254:143;;;2335:10;;;:6;;;:10;2372:5;;2254:143;208:5;2415:37;;;;;2230:3;;;;;2178:286;;;1324:1147;;;;;;;:::o;64:4735::-;;;;;;;;;;;29:2:-1;21:6;17:15;125:4;109:14;101:6;88:42;-1:-1;64:4735:0;;;-1:-1:-1;;64:4735:0:o;:::-;;;;;;;;;-1:-1:-1;64:4735:0;;;;;;;;;;;;;;;;;:::o

Swarm Source

ipfs://f400713900a965d94c09094fe7f0a47d580be88e84f763265c894a7624bed864

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.