ETH Price: $3,424.73 (-2.09%)
Gas: 5 Gwei

Contract

0x45B7cdaD1fb3F714F2046554fF34740b9B63903A
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040116931152021-01-20 15:33:181274 days ago1611156798IN
 Create: IncentivisedVotingLockup
0 ETH0.33209628101

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
IncentivisedVotingLockup

Compiler Version
v0.6.8+commit.0bbfe453

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 14 : IncentivisedVotingLockup.sol
/* solium-disable security/no-block-members */
pragma solidity 0.6.8;

import { IBasicToken } from "../shared/IBasicToken.sol";

import { DaoGovernable } from "../governance/DaoGovernable.sol";
import { ProtocolDaoGovernable } from "../governance/ProtocolDaoGovernable.sol";

import { IIncentivisedVotingLockup } from "../interfaces/IIncentivisedVotingLockup.sol";
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";

import { SafeERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { SignedSafeMath128 } from "../shared/SignedSafeMath128.sol";
import { StableMath, SafeMath } from "../shared/StableMath.sol";

import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";

/**
 * @title  IncentivisedVotingLockup
 * @author Voting Weight tracking & Decay
 *             -> Curve Finance (MIT) - forked & ported to Solidity
 *             -> https://github.com/curvefi/curve-dao-contracts/blob/master/contracts/VotingEscrow.vy
 *         osolmaz - Research & Reward distributions
 *         alsco77 - Solidity implementation
 * @notice Lockup MTA, receive vMTA (voting weight that decays over time), and earn
 *         rewards based on staticWeight
 * @dev    Supports:
 *            1) Tracking MTA Locked up (LockedBalance)
 *            2) Pull Based Reward allocations based on Lockup (Static Balance)
 *            3) Decaying voting weight lookup through CheckpointedERC20 (balanceOf)
 *            4) Ejecting fully decayed participants from reward allocation (eject)
 *            5) Migration of points to v2 (used as multiplier in future) ***** (rewardsPaid)
 *            6) Closure of contract (expire)
 */
contract IncentivisedVotingLockup is
    IIncentivisedVotingLockup,
    Initializable,
    ReentrancyGuardUpgradeable,
    DaoGovernable,
    ProtocolDaoGovernable
{
    using StableMath for uint256;
    using SafeMath for uint256;
    using SignedSafeMath128 for int128;
    using SafeERC20 for IERC20;

    /** Shared Events */
    event Deposit(address indexed provider, uint256 value, uint256 locktime, LockAction indexed action, uint256 ts);
    event Withdraw(address indexed provider, uint256 value, uint256 ts);
    event Expired();

    /** Shared Globals */
    IERC20 public stakingToken;
    uint256 private constant WEEK = 7 days;
    uint256 public constant MAXTIME = 1095 days; //3 years
    uint256 public END;
    bool public expired;

    /** Lockup */
    uint256 public globalEpoch;
    Point[] public pointHistory;
    mapping(address => Point[]) public userPointHistory;
    mapping(address => uint256) public userPointEpoch;
    mapping(uint256 => int128) public slopeChanges;
    mapping(address => LockedBalance) public locked;

    // Voting token - Checkpointed view only ERC20
    string public name;
    string public symbol;
    uint256 public decimals;
    
    /** Structs */
    struct Point {
        int128 bias;
        int128 slope;
        uint256 ts;
        uint256 blk;
    }

    struct LockedBalance {
        int128 amount;
        uint256 end;
    }

    enum LockAction {
        CREATE_LOCK,
        INCREASE_LOCK_AMOUNT,
        INCREASE_LOCK_TIME
    }

    // constructor(
    //     address _stakingToken,
    //     string memory _name,
    //     string memory _symbol,
    //     address _dao,
    //     address _protocolDao
    // )
    //     public
    //     DaoGovernable(_dao)
    //     ProtocolDaoGovernable(_protocolDao)
    // {

    //     _initialize(_stakingToken, _name, _symbol);

    // }

    function initialize(
        address _stakingToken,
        string calldata _name,
        string calldata _symbol,
        address _dao,
        address _protocolDao
    )
        external
        initializer
    {
        ReentrancyGuardUpgradeable.__ReentrancyGuard_init();
        DaoGovernable.__DaoGovernable_init(_dao);
        ProtocolDaoGovernable.__ProtocolDaoGovernable_init(_protocolDao);

        _initialize(_stakingToken, _name, _symbol);
    }

    function _initialize(
        address _stakingToken,
        string memory _name,
        string memory _symbol
    )
        internal
    {
        stakingToken = IERC20(_stakingToken);
        Point memory init = Point({ bias: int128(0), slope: int128(0), ts: block.timestamp, blk: block.number});
        pointHistory.push(init);

        decimals = IBasicToken(_stakingToken).decimals();
        require(decimals <= 18, "Cannot have more than 18 decimals");

        name = _name;
        symbol = _symbol;
        decimals = 18;

        END = block.timestamp.add(MAXTIME);

        expired = false;

    }

    /** @dev Modifier to ensure contract has not yet expired */
    modifier contractNotExpired(){
        require(!expired, "Contract is expired");
        _;
    }

    /**
    * @dev Validates that the user has an expired lock && they still have capacity to earn
    * @param _addr User address to check
    */
    modifier lockupIsOver(address _addr) {
        LockedBalance memory userLock = locked[_addr];
        require(userLock.amount > 0 && block.timestamp >= userLock.end, "Users lock didn't expire");
        // This is removed because we are using the voting weight instead of the static weight
        // In this case when the lock end is reached the balance will automatically be 0
        // require(balanceOf(_addr) > 0, "User must have existing bias");
        _;
    }

    /***************************************
                LOCKUP - GETTERS
    ****************************************/

    /**
     * @dev Gets the last available user point
     * @param _addr User address
     * @return bias i.e. y
     * @return slope i.e. linear gradient
     * @return ts i.e. time point was logged
     */
    function getLastUserPoint(address _addr)
        override
        external
        view
        returns(
            int128 bias,
            int128 slope,
            uint256 ts
        )
    {
        uint256 uepoch = userPointEpoch[_addr];
        if(uepoch == 0){
            return (0, 0, 0);
        }
        Point memory point = userPointHistory[_addr][uepoch];
        return (point.bias, point.slope, point.ts);
    }

    /***************************************
                    LOCKUP
    ****************************************/

    /**
     * @dev Records a checkpoint of both individual and global slope
     * @param _addr User address, or address(0) for only global
     * @param _oldLocked Old amount that user had locked, or null for global
     * @param _newLocked new amount that user has locked, or null for global
     */
    function _checkpoint(
        address _addr,
        LockedBalance memory _oldLocked,
        LockedBalance memory _newLocked
    )
        internal
    {
        Point memory userOldPoint;
        Point memory userNewPoint;
        int128 oldSlopeDelta = 0;
        int128 newSlopeDelta = 0;
        uint256 epoch = globalEpoch;

        if(_addr != address(0)){
            // Calculate slopes and biases
            // Kept at zero when they have to
            if(_oldLocked.end > block.timestamp && _oldLocked.amount > 0){
                userOldPoint.slope = _oldLocked.amount.div(int128(MAXTIME));
                userOldPoint.bias = userOldPoint.slope.mul(int128(_oldLocked.end.sub(block.timestamp)));
            }
            if(_newLocked.end > block.timestamp && _newLocked.amount > 0){
                userNewPoint.slope = _newLocked.amount.div(int128(MAXTIME));
                userNewPoint.bias = userNewPoint.slope.mul(int128(_newLocked.end.sub(block.timestamp)));
            }

            // Moved from bottom final if statement to resolve stack too deep err
            // start {
            // Now handle user history
            uint256 uEpoch = userPointEpoch[_addr];
            if(uEpoch == 0){
                userPointHistory[_addr].push(userOldPoint);
            }

            userPointEpoch[_addr] = uEpoch.add(1);
            userNewPoint.ts = block.timestamp;
            userNewPoint.blk = block.number;
            // userPointHistory[_addr][uEpoch.add(1)] = userNewPoint;
            userPointHistory[_addr].push(userNewPoint);

            // } end

            // Read values of scheduled changes in the slope
            // oldLocked.end can be in the past and in the future
            // newLocked.end can ONLY by in the FUTURE unless everything expired: than zeros
            oldSlopeDelta = slopeChanges[_oldLocked.end];
            if(_newLocked.end != 0){
                if (_newLocked.end == _oldLocked.end) {
                    newSlopeDelta = oldSlopeDelta;
                } else {
                    newSlopeDelta = slopeChanges[_newLocked.end];
                }
            }
        }

        Point memory lastPoint = Point({bias: 0, slope: 0, ts: block.timestamp, blk: block.number});
        if(epoch > 0){
            lastPoint = pointHistory[epoch];
        }
        uint256 lastCheckpoint = lastPoint.ts;

        // initialLastPoint is used for extrapolation to calculate block number
        // (approximately, for *At methods) and save them
        // as we cannot figure that out exactly from inside the contract
        Point memory initialLastPoint = Point({bias: 0, slope: 0, ts: lastPoint.ts, blk: lastPoint.blk});
        uint256 blockSlope = 0; // dblock/dt
        if(block.timestamp > lastPoint.ts){
            blockSlope = StableMath.scaleInteger(block.number.sub(lastPoint.blk)).div(block.timestamp.sub(lastPoint.ts));
        }
        // If last point is already recorded in this block, slope=0
        // But that's ok b/c we know the block in such case

        // Go over weeks to fill history and calculate what the current point is
        uint256 iterativeTime = _floorToWeek(lastCheckpoint);
        for (uint256 i = 0; i < 255; i++){
            // Hopefully it won't happen that this won't get used in 5 years!
            // If it does, users will be able to withdraw but vote weight will be broken
            iterativeTime = iterativeTime.add(WEEK);
            int128 dSlope = 0;
            if(iterativeTime > block.timestamp){
                iterativeTime = block.timestamp;
            } else {
                dSlope = slopeChanges[iterativeTime];
            }
            int128 biasDelta = lastPoint.slope.mul(int128(iterativeTime.sub(lastCheckpoint)));
            lastPoint.bias = lastPoint.bias.sub(biasDelta);
            lastPoint.slope = lastPoint.slope.add(dSlope);
            // This can happen
            if(lastPoint.bias < 0){
                lastPoint.bias = 0;
            }
            // This cannot happen - just in case
            if(lastPoint.slope < 0){
                lastPoint.slope = 0;
            }
            lastCheckpoint = iterativeTime;
            lastPoint.ts = iterativeTime;
            lastPoint.blk = initialLastPoint.blk.add(blockSlope.mulTruncate(iterativeTime.sub(initialLastPoint.ts)));

            // when epoch is incremented, we either push here or after slopes updated below
            epoch = epoch.add(1);
            if(iterativeTime == block.timestamp) {
                lastPoint.blk = block.number;
                break;
            } else {
                // pointHistory[epoch] = lastPoint;
                pointHistory.push(lastPoint);
            }
        }

        globalEpoch = epoch;
        // Now pointHistory is filled until t=now

        if(_addr != address(0)){
            // If last point was in this block, the slope change has been applied already
            // But in such case we have 0 slope(s)
            lastPoint.slope = lastPoint.slope.add(userNewPoint.slope.sub(userOldPoint.slope));
            lastPoint.bias = lastPoint.bias.add(userNewPoint.bias.sub(userOldPoint.bias));
            if(lastPoint.slope < 0) {
                lastPoint.slope = 0;
            }
            if(lastPoint.bias < 0){
                lastPoint.bias = 0;
            }
        }

        // Record the changed point into history
        // pointHistory[epoch] = lastPoint;
        pointHistory.push(lastPoint);

        if(_addr != address(0)){
            // Schedule the slope changes (slope is going down)
            // We subtract new_user_slope from [new_locked.end]
            // and add old_user_slope to [old_locked.end]
            if(_oldLocked.end > block.timestamp){
                // oldSlopeDelta was <something> - userOldPoint.slope, so we cancel that
                oldSlopeDelta = oldSlopeDelta.add(userOldPoint.slope);
                if(_newLocked.end == _oldLocked.end) {
                    oldSlopeDelta = oldSlopeDelta.sub(userNewPoint.slope);  // It was a new deposit, not extension
                }
                slopeChanges[_oldLocked.end] = oldSlopeDelta;
            }
            if(_newLocked.end > block.timestamp) {
                if(_newLocked.end > _oldLocked.end){
                    newSlopeDelta = newSlopeDelta.sub(userNewPoint.slope);  // old slope disappeared at this point
                    slopeChanges[_newLocked.end] = newSlopeDelta;
                }
                // else: we recorded it already in oldSlopeDelta
            }
        }
    }

    /**
     * @dev Deposits or creates a stake for a given address
     * @param _addr User address to assign the stake
     * @param _value Total units of StakingToken to lockup
     * @param _unlockTime Time at which the stake should unlock
     * @param _oldLocked Previous amount staked by this user
     * @param _action See LockAction enum
     */
    function _depositFor(
        address _addr,
        uint256 _value,
        uint256 _unlockTime,
        LockedBalance memory _oldLocked,
        LockAction _action
    )
        internal
    {
        LockedBalance memory newLocked = LockedBalance({amount: _oldLocked.amount, end: _oldLocked.end});

        // Adding to existing lock, or if a lock is expired - creating a new one
        newLocked.amount = newLocked.amount.add(int128(_value));
        if(_unlockTime != 0){
            newLocked.end = _unlockTime;
        }
        locked[_addr] = newLocked;

        // Possibilities:
        // Both _oldLocked.end could be current or expired (>/< block.timestamp)
        // value == 0 (extend lock) or value > 0 (add to lock or extend lock)
        // newLocked.end > block.timestamp (always)
        _checkpoint(_addr, _oldLocked, newLocked);

        if(_value != 0) {
            stakingToken.safeTransferFrom(_addr, address(this), _value);
        }

        emit Deposit(_addr, _value, newLocked.end, _action, block.timestamp);
    }

    /**
     * @dev Public function to trigger global checkpoint
     */
    function checkpoint() external {
        LockedBalance memory empty;
        _checkpoint(address(0), empty, empty);
    }

    /**
     * @dev Creates a new lock
     * @param _value Total units of StakingToken to lockup
     * @param _unlockTime Time at which the stake should unlock
     */
    function createLock(uint256 _value, uint256 _unlockTime)
        override
        external
        nonReentrant
        contractNotExpired
    {
        uint256 unlock_time = _floorToWeek(_unlockTime);  // Locktime is rounded down to weeks
        LockedBalance memory locked_ = LockedBalance({amount: locked[msg.sender].amount, end: locked[msg.sender].end});

        require(_value > 0, "Must stake non zero amount");
        require(locked_.amount == 0, "Withdraw old tokens first");

        require(unlock_time > block.timestamp, "Can only lock until time in the future");
        require(unlock_time <= END, "Voting lock can be 3 years max (until recol)");

        _depositFor(msg.sender, _value, unlock_time, locked_, LockAction.CREATE_LOCK);
    }

    /**
     * @dev Increases amount of stake thats locked up & resets decay
     * @param _value Additional units of StakingToken to add to exiting stake
     */
    function increaseLockAmount(uint256 _value)
        override
        external
        nonReentrant
        contractNotExpired
    {
        LockedBalance memory locked_ = LockedBalance({amount: locked[msg.sender].amount, end: locked[msg.sender].end});

        require(_value > 0, "Must stake non zero amount");
        require(locked_.amount > 0, "No existing lock found");
        require(locked_.end > block.timestamp, "Cannot add to expired lock. Withdraw");

        _depositFor(msg.sender, _value, 0, locked_, LockAction.INCREASE_LOCK_AMOUNT);
    }

    /**
     * @dev Increases length of lockup & resets decay
     * @param _unlockTime New unlocktime for lockup
     */
    function increaseLockLength(uint256 _unlockTime)
        override
        external
        nonReentrant
        contractNotExpired
    {
        LockedBalance memory locked_ = LockedBalance({amount: locked[msg.sender].amount, end: locked[msg.sender].end});
        uint256 unlock_time = _floorToWeek(_unlockTime);  // Locktime is rounded down to weeks

        require(locked_.amount > 0, "Nothing is locked");
        require(locked_.end > block.timestamp, "Lock expired");
        require(unlock_time > locked_.end, "Can only increase lock WEEK");
        require(unlock_time <= END, "Voting lock can be 3 years max (until recol)");

        _depositFor(msg.sender, 0, unlock_time, locked_, LockAction.INCREASE_LOCK_TIME);
    }

    /**
     * @dev Withdraws all the senders stake, providing lockup is over
     */
    function withdraw()
        override
        external
    {
        _withdraw(msg.sender);
    }

    /**
     * @dev Withdraws a given users stake, providing the lockup has finished
     * @param _addr User for which to withdraw
     */
    function _withdraw(address _addr)
        internal
        nonReentrant
    {
        LockedBalance memory oldLock = LockedBalance({ end: locked[_addr].end, amount: locked[_addr].amount });
        require(block.timestamp >= oldLock.end || expired, "The lock didn't expire");
        require(oldLock.amount > 0, "Must have something to withdraw");

        uint256 value = uint256(oldLock.amount);

        LockedBalance memory currentLock = LockedBalance({end: 0, amount: 0});
        locked[_addr] = currentLock;

        // oldLocked can have either expired <= timestamp or zero end
        // currentLock has only 0 end
        // Both can have >= 0 amount
        if(!expired){
            _checkpoint(_addr, oldLock, currentLock);
        }

        stakingToken.safeTransfer(_addr, value);

        emit Withdraw(_addr, value, block.timestamp);
    }

    /**
     * @dev Ends the contract, unlocking all stakes.
     * No more staking can happen. Only withdraw and Claim.
     */
    function expireContract()
        override
        external
        onlyProtocolDao
        contractNotExpired
    {
        expired = true;

        emit Expired();
    }

    /***************************************
                    GETTERS
    ****************************************/


    /** @dev Floors a timestamp to the nearest weekly increment */
    function _floorToWeek(uint256 _t)
        internal
        pure
        returns(uint256)
    {
        return _t.div(WEEK).mul(WEEK);
    }

    /**
     * @dev Uses binarysearch to find the most recent point history preceeding block
     * @param _block Find the most recent point history before this block
     * @param _maxEpoch Do not search pointHistories past this index
     */
    function _findBlockEpoch(uint256 _block, uint256 _maxEpoch)
        internal
        view
        returns(uint256)
    {
        // Binary search
        uint256 min = 0;
        uint256 max = _maxEpoch;
        // Will be always enough for 128-bit numbers
        for(uint256 i = 0; i < 128; i++){
            if (min >= max)
                break;
            uint256 mid = (min.add(max).add(1)).div(2);
            if (pointHistory[mid].blk <= _block){
                min = mid;
            } else {
                max = mid.sub(1);
            }
        }
        return min;
    }

    /**
     * @dev Uses binarysearch to find the most recent user point history preceeding block
     * @param _addr User for which to search
     * @param _block Find the most recent point history before this block
     */
    function _findUserBlockEpoch(address _addr, uint256 _block)
        internal
        view
        returns(uint256)
    {
        uint256 min = 0;
        uint256 max = userPointEpoch[_addr];
        for(uint256 i = 0; i < 128; i++) {
            if(min >= max){
                break;
            }
            uint256 mid = (min.add(max).add(1)).div(2);
            if(userPointHistory[_addr][mid].blk <= _block){
                min = mid;
            } else {
                max = mid.sub(1);
            }
        }
        return min;
    }

    /**
     * @dev Gets curent user voting weight (aka effectiveStake)
     * @param _owner User for which to return the balance
     * @return uint256 Balance of user
     */
    function balanceOf(address _owner)
        override
        public
        view
        returns (uint256)
    {
        uint256 epoch = userPointEpoch[_owner];
        if(epoch == 0){
            return 0;
        }
        Point memory lastPoint = userPointHistory[_owner][epoch];
        lastPoint.bias = lastPoint.bias.sub(lastPoint.slope.mul(int128(block.timestamp.sub(lastPoint.ts))));
        if(lastPoint.bias < 0) {
            lastPoint.bias = 0;
        }
        return uint256(lastPoint.bias);
    }

    /**
     * @dev Gets a users votingWeight at a given blockNumber
     * @param _owner User for which to return the balance
     * @param _blockNumber Block at which to calculate balance
     * @return uint256 Balance of user
     */
    function balanceOfAt(address _owner, uint256 _blockNumber)
        override
        public
        view
        returns (uint256)
    {
        require(_blockNumber <= block.number, "Must pass block number in the past");

        // Get most recent user Point to block
        uint256 userEpoch = _findUserBlockEpoch(_owner, _blockNumber);
        if(userEpoch == 0){
            return 0;
        }
        Point memory upoint = userPointHistory[_owner][userEpoch];

        // Get most recent global Point to block
        uint256 maxEpoch = globalEpoch;
        uint256 epoch = _findBlockEpoch(_blockNumber, maxEpoch);
        Point memory point0 = pointHistory[epoch];

        // Calculate delta (block & time) between user Point and target block
        // Allowing us to calculate the average seconds per block between
        // the two points
        uint256 dBlock = 0;
        uint256 dTime = 0;
        if(epoch < maxEpoch){
            Point memory point1 = pointHistory[epoch.add(1)];
            dBlock = point1.blk.sub(point0.blk);
            dTime = point1.ts.sub(point0.ts);
        } else {
            dBlock = block.number.sub(point0.blk);
            dTime = block.timestamp.sub(point0.ts);
        }
        // (Deterministically) Estimate the time at which block _blockNumber was mined
        uint256 blockTime = point0.ts;
        if(dBlock != 0) {
            // blockTime += dTime * (_blockNumber - point0.blk) / dBlock;
            blockTime = blockTime.add(dTime.mul(_blockNumber.sub(point0.blk)).div(dBlock));
        }
        // Current Bias = most recent bias - (slope * time since update)
        upoint.bias = upoint.bias.sub(upoint.slope.mul(int128(blockTime.sub(upoint.ts))));
        if(upoint.bias >= 0){
            return uint256(upoint.bias);
        } else {
            return 0;
        }
    }

    /**
     * @dev Calculates total supply of votingWeight at a given time _t
     * @param _point Most recent point before time _t
     * @param _t Time at which to calculate supply
     * @return totalSupply at given point in time
     */
    function _supplyAt(Point memory _point, uint256 _t)
        internal
        view
        returns (uint256)
    {
        Point memory lastPoint = _point;
        // Floor the timestamp to weekly interval
        uint256 iterativeTime = _floorToWeek(lastPoint.ts);
        // Iterate through all weeks between _point & _t to account for slope changes
        for(uint256 i = 0; i < 255; i++){
            iterativeTime = iterativeTime.add(WEEK);
            int128 dSlope = 0;
            // If week end is after timestamp, then truncate & leave dSlope to 0
            if(iterativeTime > _t){
                iterativeTime = _t;
            }
            // else get most recent slope change
            else {
                dSlope = slopeChanges[iterativeTime];
            }

            // lastPoint.bias -= lastPoint.slope * convert(iterativeTime - lastPoint.ts, int128)
            lastPoint.bias = lastPoint.bias.sub(lastPoint.slope.mul(int128(iterativeTime.sub(lastPoint.ts))));
            if(iterativeTime == _t){
                break;
            }
            lastPoint.slope = lastPoint.slope.add(dSlope);
            lastPoint.ts = iterativeTime;
        }

        if (lastPoint.bias < 0){
            lastPoint.bias = 0;
        }
        return uint256(lastPoint.bias);
    }

    /**
     * @dev Calculates current total supply of votingWeight
     * @return totalSupply of voting token weight
     */
    function totalSupply()
        override
        public
        view
        returns (uint256)
    {
        uint256 epoch_ = globalEpoch;
        Point memory lastPoint = pointHistory[epoch_];
        return _supplyAt(lastPoint, block.timestamp);
    }

    /**
     * @dev Calculates total supply of votingWeight at a given blockNumber
     * @param _blockNumber Block number at which to calculate total supply
     * @return totalSupply of voting token weight at the given blockNumber
     */
    function totalSupplyAt(uint256 _blockNumber)
        override
        public
        view
        returns (uint256)
    {
        require(_blockNumber <= block.number, "Must pass block number in the past");

        uint256 epoch = globalEpoch;
        uint256 targetEpoch = _findBlockEpoch(_blockNumber, epoch);

        Point memory point = pointHistory[targetEpoch];

        // If point.blk > _blockNumber that means we got the initial epoch & contract did not yet exist
        if(point.blk > _blockNumber){
            return 0;
        }

        uint256 dTime = 0;
        if(targetEpoch < epoch){
            Point memory pointNext = pointHistory[targetEpoch.add(1)];
            if(point.blk != pointNext.blk) {
                dTime = (_blockNumber.sub(point.blk)).mul(pointNext.ts.sub(point.ts)).div(pointNext.blk.sub(point.blk));
            }
        } else if (point.blk != block.number){
            dTime = (_blockNumber.sub(point.blk)).mul(block.timestamp.sub(point.ts)).div(block.number.sub(point.blk));
        }
        // Now dTime contains info on how far are we beyond point

        return _supplyAt(point, point.ts.add(dTime));
    }

    function getDuration()
        external
        pure
        returns (uint256)
    {
        return WEEK;
    }
    
    /***************************************
                    ADMIN
    ****************************************/

    /**
     * @dev Allows the DAO to salvage the full amount of any ERC20 tokens 
     * from the contract by supplying the token address.
     * @param _token Address of the token to be salvaged
     */
    function salvage(address _token)
        external
        onlyDao
    {
        uint256 balance = IERC20(_token).balanceOf(address(this));
        
        require(
            IERC20(_token).transfer(dao(), balance)
        );
    }

    // Reserved storage space to allow for layout changes in the future.
    uint256[50] private ______gap;

}

File 2 of 14 : DaoGovernable.sol
pragma solidity 0.6.8;

/**
 * @title   DaoGovernable
 * @author  Stability Labs Pty. Ltd.
 * @notice  Simple contract implementing an Ownable pattern.
 * @dev     Derives from V2.3.0 @openzeppelin/contracts/ownership/Ownable.sol
 *          Modified to have custom name and features
 *              - Removed `renounceOwnership`
 *              - Changes `_owner` to `_governor`
 */
contract DaoGovernable {

    event DaoChanged(address indexed previousDao, address indexed newDao);

    address private _dao;

    /**
     * @dev Initializes the contract setting supplied address as the initial Dao.
     */
    // constructor (address _newDao) internal {
    //     _dao = _newDao;
    //     emit DaoChanged(address(0), _dao);
    // }
    
    function __DaoGovernable_init(address _newDao) internal {
        _dao = _newDao;
        emit DaoChanged(address(0), _dao);
    }

    /**
     * @dev Returns the address of the current Dao.
     */
    function dao() public view returns (address) {
        return _dao;
    }

    /**
     * @dev Throws if called by any account other than the Dao.
     */
    modifier onlyDao() {
        require(isDao(), "GOV: caller is not the Dao");
        _;
    }

    /**
     * @dev Returns true if the caller is the current Dao.
     */
    function isDao() public view returns (bool) {
        return msg.sender == _dao;
    }

    /**
     * @dev Transfers Dao of the contract to a new account (`newDao`).
     * Can only be called by the current Dao.
     * @param _newDao Address of the new Dao
     */
    function changeDao(address _newDao) external onlyDao {
        _changeDao(_newDao);
    }

    /**
     * @dev Change Dao of the contract to a new account (`newDao`).
     * @param _newDao Address of the new Governor
     */
    function _changeDao(address _newDao) internal {
        require(_newDao != address(0), "GOV: new Dao is address(0)");
        emit DaoChanged(_dao, _newDao);
        _dao = _newDao;
    }

    // Reserved storage space to allow for layout changes in the future.
    uint256[50] private ______gap;

}

File 3 of 14 : ProtocolDaoGovernable.sol
pragma solidity 0.6.8;

/**
 * @title   ProtocolDaoGovernable
 * @author  Stability Labs Pty. Ltd.
 * @notice  Simple contract implementing an Ownable pattern.
 * @dev     Derives from V2.3.0 @openzeppelin/contracts/ownership/Ownable.sol
 *          Modified to have custom name and features
 *              - Removed `renounceOwnership`
 *              - Changes `_owner` to `_governor`
 */
contract ProtocolDaoGovernable {

    event ProtocolDaoChanged(address indexed previousProtocolDao, address indexed newProtocolDao);

    address private _protocolDao;

    /**
     * @dev Initializes the contract setting supplied address as the initial Protocol Dao.
     */
    // constructor (address _newProtocolDao) internal {
    //     _protocolDao = _newProtocolDao;
    //     emit ProtocolDaoChanged(address(0), _protocolDao);
    // }

    function __ProtocolDaoGovernable_init(address _newProtocolDao) internal {
        _protocolDao = _newProtocolDao;
        emit ProtocolDaoChanged(address(0), _protocolDao);
    }

    /**
     * @dev Returns the address of the current Protocol Dao.
     */
    function protocolDao() public view returns (address) {
        return _protocolDao;
    }

    /**
     * @dev Throws if called by any account other than the Protocol Dao.
     */
    modifier onlyProtocolDao() {
        require(isProtocolDao(), "GOV: caller is not the Protocol Dao");
        _;
    }

    /**
     * @dev Returns true if the caller is the current Protocol Dao.
     */
    function isProtocolDao() public view returns (bool) {
        return msg.sender == _protocolDao;
    }

    /**
     * @dev Transfers Protocol Dao of the contract to a new account (`newProtocolDao`).
     * Can only be called by the current Protocol Dao.
     * @param _newProtocolDao Address of the new Protocol Dao
     */
    function changeProtocolDao(address _newProtocolDao) external onlyProtocolDao {
        _changeProtocolDao(_newProtocolDao);
    }

    /**
     * @dev Change Protocol Dao of the contract to a new account (`newProtocolDao`).
     * @param _newProtocolDao Address of the new Governor
     */
    function _changeProtocolDao(address _newProtocolDao) internal {
        require(_newProtocolDao != address(0), "GOV: new Protocol Dao is address(0)");
        emit ProtocolDaoChanged(_protocolDao, _newProtocolDao);
        _protocolDao = _newProtocolDao;
    }

    // Reserved storage space to allow for layout changes in the future.
    uint256[50] private ______gap;

}

File 4 of 14 : IIncentivisedVotingLockup.sol
pragma solidity 0.6.8;

import { IERC20WithCheckpointing } from "../shared/IERC20WithCheckpointing.sol";

interface IIncentivisedVotingLockup is IERC20WithCheckpointing {

    function getLastUserPoint(address _addr) external view returns(int128 bias, int128 slope, uint256 ts);
    function createLock(uint256 _value, uint256 _unlockTime) external;
    function withdraw() external;
    function increaseLockAmount(uint256 _value) external;
    function increaseLockLength(uint256 _unlockTime) external;
    function expireContract() external;
    
}

File 5 of 14 : IBasicToken.sol
pragma solidity 0.6.8;

interface IBasicToken {
    function decimals() external view returns (uint8);
}

File 6 of 14 : IERC20WithCheckpointing.sol
pragma solidity 0.6.8;

// From https://github.com/aragonone/voting-connectors
interface IERC20WithCheckpointing {
    function balanceOf(address _owner) external view returns (uint256);
    function balanceOfAt(address _owner, uint256 _blockNumber) external view returns (uint256);

    function totalSupply() external view returns (uint256);
    function totalSupplyAt(uint256 _blockNumber) external view returns (uint256);
}

File 7 of 14 : SignedSafeMath128.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.6.8;

/**
 * @title SignedSafeMath128
 * @author OpenZeppelin > https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/SignedSafeMath.sol
 * @dev Signed math operations with safety checks that revert on error.
 *      Reverted to solc 0.5.16 & updated from int256 to int128
 */
library SignedSafeMath128 {
    int128 constant private _INT128_MIN = -2**127;

    /**
     * @dev Returns the multiplication of two signed integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(int128 a, int128 b) internal pure returns (int128) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        require(!(a == -1 && b == _INT128_MIN), "SignedSafeMath: multiplication overflow");

        int128 c = a * b;
        require(c / a == b, "SignedSafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two signed integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(int128 a, int128 b) internal pure returns (int128) {
        require(b != 0, "SignedSafeMath: division by zero");
        require(!(b == -1 && a == _INT128_MIN), "SignedSafeMath: division overflow");

        int128 c = a / b;

        return c;
    }

    /**
     * @dev Returns the subtraction of two signed integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(int128 a, int128 b) internal pure returns (int128) {
        int128 c = a - b;
        require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow");

        return c;
    }

    /**
     * @dev Returns the addition of two signed integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(int128 a, int128 b) internal pure returns (int128) {
        int128 c = a + b;
        require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow");

        return c;
    }
}

File 8 of 14 : StableMath.sol
pragma solidity 0.6.8;

import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";

/**
 * @title   StableMath
 * @author  Stability Labs Pty. Ltd.
 * @notice  A library providing safe mathematical operations to multiply and
 *          divide with standardised precision.
 * @dev     Derives from OpenZeppelin's SafeMath lib and uses generic system
 *          wide variables for managing precision.
 */
library StableMath {

    using SafeMath for uint256;

    /**
     * @dev Scaling unit for use in specific calculations,
     * where 1 * 10**18, or 1e18 represents a unit '1'
     */
    uint256 private constant FULL_SCALE = 1e18;

    /**
     * @notice Token Ratios are used when converting between units of bAsset, mAsset and MTA
     * Reasoning: Takes into account token decimals, and difference in base unit (i.e. grams to Troy oz for gold)
     * @dev bAsset ratio unit for use in exact calculations,
     * where (1 bAsset unit * bAsset.ratio) / ratioScale == x mAsset unit
     */
    uint256 private constant RATIO_SCALE = 1e8;

    /**
     * @dev Provides an interface to the scaling unit
     * @return Scaling unit (1e18 or 1 * 10**18)
     */
    function getFullScale() internal pure returns (uint256) {
        return FULL_SCALE;
    }

    /**
     * @dev Provides an interface to the ratio unit
     * @return Ratio scale unit (1e8 or 1 * 10**8)
     */
    function getRatioScale() internal pure returns (uint256) {
        return RATIO_SCALE;
    }

    /**
     * @dev Scales a given integer to the power of the full scale.
     * @param x   Simple uint256 to scale
     * @return    Scaled value a to an exact number
     */
    function scaleInteger(uint256 x)
        internal
        pure
        returns (uint256)
    {
        return x.mul(FULL_SCALE);
    }

    /***************************************
              PRECISE ARITHMETIC
    ****************************************/

    /**
     * @dev Multiplies two precise units, and then truncates by the full scale
     * @param x     Left hand input to multiplication
     * @param y     Right hand input to multiplication
     * @return      Result after multiplying the two inputs and then dividing by the shared
     *              scale unit
     */
    function mulTruncate(uint256 x, uint256 y)
        internal
        pure
        returns (uint256)
    {
        return mulTruncateScale(x, y, FULL_SCALE);
    }

    /**
     * @dev Multiplies two precise units, and then truncates by the given scale. For example,
     * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18
     * @param x     Left hand input to multiplication
     * @param y     Right hand input to multiplication
     * @param scale Scale unit
     * @return      Result after multiplying the two inputs and then dividing by the shared
     *              scale unit
     */
    function mulTruncateScale(uint256 x, uint256 y, uint256 scale)
        internal
        pure
        returns (uint256)
    {
        // e.g. assume scale = fullScale
        // z = 10e18 * 9e17 = 9e36
        uint256 z = x.mul(y);
        // return 9e38 / 1e18 = 9e18
        return z.div(scale);
    }

    /**
     * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result
     * @param x     Left hand input to multiplication
     * @param y     Right hand input to multiplication
     * @return      Result after multiplying the two inputs and then dividing by the shared
     *              scale unit, rounded up to the closest base unit.
     */
    function mulTruncateCeil(uint256 x, uint256 y)
        internal
        pure
        returns (uint256)
    {
        // e.g. 8e17 * 17268172638 = 138145381104e17
        uint256 scaled = x.mul(y);
        // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17
        uint256 ceil = scaled.add(FULL_SCALE.sub(1));
        // e.g. 13814538111.399...e18 / 1e18 = 13814538111
        return ceil.div(FULL_SCALE);
    }

    /**
     * @dev Precisely divides two units, by first scaling the left hand operand. Useful
     *      for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)
     * @param x     Left hand input to division
     * @param y     Right hand input to division
     * @return      Result after multiplying the left operand by the scale, and
     *              executing the division on the right hand input.
     */
    function divPrecisely(uint256 x, uint256 y)
        internal
        pure
        returns (uint256)
    {
        // e.g. 8e18 * 1e18 = 8e36
        uint256 z = x.mul(FULL_SCALE);
        // e.g. 8e36 / 10e18 = 8e17
        return z.div(y);
    }


    /***************************************
                  RATIO FUNCS
    ****************************************/

    /**
     * @dev Multiplies and truncates a token ratio, essentially flooring the result
     *      i.e. How much mAsset is this bAsset worth?
     * @param x     Left hand operand to multiplication (i.e Exact quantity)
     * @param ratio bAsset ratio
     * @return c     Result after multiplying the two inputs and then dividing by the ratio scale
     */
    function mulRatioTruncate(uint256 x, uint256 ratio)
        internal
        pure
        returns (uint256 c)
    {
        return mulTruncateScale(x, ratio, RATIO_SCALE);
    }

    /**
     * @dev Multiplies and truncates a token ratio, rounding up the result
     *      i.e. How much mAsset is this bAsset worth?
     * @param x     Left hand input to multiplication (i.e Exact quantity)
     * @param ratio bAsset ratio
     * @return      Result after multiplying the two inputs and then dividing by the shared
     *              ratio scale, rounded up to the closest base unit.
     */
    function mulRatioTruncateCeil(uint256 x, uint256 ratio)
        internal
        pure
        returns (uint256)
    {
        // e.g. How much mAsset should I burn for this bAsset (x)?
        // 1e18 * 1e8 = 1e26
        uint256 scaled = x.mul(ratio);
        // 1e26 + 9.99e7 = 100..00.999e8
        uint256 ceil = scaled.add(RATIO_SCALE.sub(1));
        // return 100..00.999e8 / 1e8 = 1e18
        return ceil.div(RATIO_SCALE);
    }


    /**
     * @dev Precisely divides two ratioed units, by first scaling the left hand operand
     *      i.e. How much bAsset is this mAsset worth?
     * @param x     Left hand operand in division
     * @param ratio bAsset ratio
     * @return c     Result after multiplying the left operand by the scale, and
     *              executing the division on the right hand input.
     */
    function divRatioPrecisely(uint256 x, uint256 ratio)
        internal
        pure
        returns (uint256 c)
    {
        // e.g. 1e14 * 1e8 = 1e22
        uint256 y = x.mul(RATIO_SCALE);
        // return 1e22 / 1e12 = 1e10
        return y.div(ratio);
    }

    /***************************************
                    HELPERS
    ****************************************/

    /**
     * @dev Calculates minimum of two numbers
     * @param x     Left hand input
     * @param y     Right hand input
     * @return      Minimum of the two inputs
     */
    function min(uint256 x, uint256 y)
        internal
        pure
        returns (uint256)
    {
        return x > y ? y : x;
    }

    /**
     * @dev Calculated maximum of two numbers
     * @param x     Left hand input
     * @param y     Right hand input
     * @return      Maximum of the two inputs
     */
    function max(uint256 x, uint256 y)
        internal
        pure
        returns (uint256)
    {
        return x > y ? x : y;
    }

    /**
     * @dev Clamps a value to an upper bound
     * @param x           Left hand input
     * @param upperBound  Maximum possible value to return
     * @return            Input x clamped to a maximum value, upperBound
     */
    function clamp(uint256 x, uint256 upperBound)
        internal
        pure
        returns (uint256)
    {
        return x > upperBound ? upperBound : x;
    }
}

File 9 of 14 : Initializable.sol
// SPDX-License-Identifier: MIT

// solhint-disable-next-line compiler-version
pragma solidity >=0.4.24 <0.8.0;


/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 * 
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.
 * 
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 */
abstract contract Initializable {

    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }

    /// @dev Returns true if and only if the function is running in the constructor
    function _isConstructor() private view returns (bool) {
        // extcodesize checks the size of the code stored in an address, and
        // address returns the current address. Since the code is still not
        // deployed when running a constructor, any checks on its code size will
        // yield zero, making it an effective way to detect if a contract is
        // under construction or not.
        address self = address(this);
        uint256 cs;
        // solhint-disable-next-line no-inline-assembly
        assembly { cs := extcodesize(self) }
        return cs == 0;
    }
}

File 10 of 14 : ReentrancyGuardUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;
import "../proxy/Initializable.sol";

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuardUpgradeable is Initializable {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    function __ReentrancyGuard_init() internal initializer {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal initializer {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
    uint256[49] private __gap;
}

File 11 of 14 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        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;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

File 12 of 14 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 13 of 14 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using SafeMath for uint256;
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 14 of 14 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.2 <0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain`call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
      return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

Settings
{
  "remappings": [],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "evmVersion": "istanbul",
  "libraries": {},
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousDao","type":"address"},{"indexed":true,"internalType":"address","name":"newDao","type":"address"}],"name":"DaoChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"locktime","type":"uint256"},{"indexed":true,"internalType":"enum IncentivisedVotingLockup.LockAction","name":"action","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[],"name":"Expired","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousProtocolDao","type":"address"},{"indexed":true,"internalType":"address","name":"newProtocolDao","type":"address"}],"name":"ProtocolDaoChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"END","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAXTIME","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_blockNumber","type":"uint256"}],"name":"balanceOfAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newDao","type":"address"}],"name":"changeDao","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newProtocolDao","type":"address"}],"name":"changeProtocolDao","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checkpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_unlockTime","type":"uint256"}],"name":"createLock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dao","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"expireContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"expired","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"getLastUserPoint","outputs":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"uint256","name":"ts","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"increaseLockAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_unlockTime","type":"uint256"}],"name":"increaseLockLength","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_stakingToken","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address","name":"_dao","type":"address"},{"internalType":"address","name":"_protocolDao","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isDao","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isProtocolDao","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"locked","outputs":[{"internalType":"int128","name":"amount","type":"int128"},{"internalType":"uint256","name":"end","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"pointHistory","outputs":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"uint256","name":"ts","type":"uint256"},{"internalType":"uint256","name":"blk","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolDao","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"salvage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"slopeChanges","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_blockNumber","type":"uint256"}],"name":"totalSupplyAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userPointEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userPointHistory","outputs":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"uint256","name":"ts","type":"uint256"},{"internalType":"uint256","name":"blk","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50613a82806100206000396000f3fe608060405234801561001057600080fd5b50600436106101fb5760003560e01c806372f702f31161011a578063ad2e8c9b116100ad578063cbf9fe5f1161007c578063cbf9fe5f14610619578063d8826fec14610662578063ee00ef3a14610688578063efe7a50414610690578063f52a36f714610698576101fb565b8063ad2e8c9b14610598578063b52c05fe146105a0578063c2c4c5c1146105c3578063c4698ee5146105cb576101fb565b806395d89b41116100e957806395d89b4114610563578063981b24d01461056b5780639d8ec51814610588578063a980cbf814610590576101fb565b806372f702f3146104f257806381fc83bb146104fa578063863a03e0146105205780638ad4c44714610546576101fb565b80634162169f116101925780635719098b116101615780635719098b146103c457806363160282146103cc5780636cf1dbed146103e957806370a08231146104cc576101fb565b80634162169f146103505780634c2067c7146103745780634ee2cd7e146103905780635029e602146103bc576101fb565b8063313ce567116101ce578063313ce567146102c757806334d901a4146102cf5780633ccfd60b1461032b578063403f444714610333576101fb565b806306fdde03146102005780630f73d0b41461027d57806318160ddd146102875780632d95baf9146102a1575b600080fd5b6102086106ce565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561024257818101518382015260200161022a565b50505050905090810190601f16801561026f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61028561075c565b005b61028f610825565b60408051918252519081900360200190f35b610285600480360360208110156102b757600080fd5b50356001600160a01b03166108a7565b61028f6108f6565b6102fb600480360360408110156102e557600080fd5b506001600160a01b0381351690602001356108fc565b60408051600f95860b860b815293850b90940b602084015282840191909152606082015290519081900360800190f35b610285610949565b6102856004803603602081101561034957600080fd5b5035610954565b610358610b2b565b604080516001600160a01b039092168252519081900360200190f35b61037c610b3a565b604080519115158252519081900360200190f35b61028f600480360360408110156103a657600080fd5b506001600160a01b038135169060200135610b43565b61037c610e58565b61028f610e69565b610285600480360360208110156103e257600080fd5b5035610e6f565b610285600480360360a08110156103ff57600080fd5b6001600160a01b03823516919081019060408101602082013564010000000081111561042a57600080fd5b82018360208201111561043c57600080fd5b8035906020019184600183028401116401000000008311171561045e57600080fd5b91939092909160208101903564010000000081111561047c57600080fd5b82018360208201111561048e57600080fd5b803590602001918460018302840111640100000000831117156104b057600080fd5b91935091506001600160a01b038135811691602001351661109b565b61028f600480360360208110156104e257600080fd5b50356001600160a01b03166111d2565b6103586112e1565b61028f6004803603602081101561051057600080fd5b50356001600160a01b03166112f0565b6102856004803603602081101561053657600080fd5b50356001600160a01b0316611302565b6102fb6004803603602081101561055c57600080fd5b5035611473565b6102086114b4565b61028f6004803603602081101561058157600080fd5b503561150f565b61037c61175a565b61035861176b565b61028f61177a565b610285600480360360408110156105b657600080fd5b5080359060200135611781565b6102856119a5565b6105f1600480360360208110156105e157600080fd5b50356001600160a01b03166119b9565b60408051600f94850b850b815292840b90930b60208301528183015290519081900360600190f35b61063f6004803603602081101561062f57600080fd5b50356001600160a01b0316611a82565b6040518083600f0b600f0b81526020018281526020019250505060405180910390f35b6102856004803603602081101561067857600080fd5b50356001600160a01b0316611aa2565b61028f611b04565b61028f611b0c565b6106b5600480360360208110156106ae57600080fd5b5035611b12565b60408051600f92830b90920b8252519081900360200190f35b60a2805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156107545780601f1061072957610100808354040283529160200191610754565b820191906000526020600020905b81548152906001019060200180831161073757829003601f168201915b505050505081565b61076461175a565b61079f5760405162461bcd60e51b81526004018080602001828103825260238152602001806138026023913960400191505060405180910390fd5b609b5460ff16156107ed576040805162461bcd60e51b815260206004820152601360248201527210dbdb9d1c9858dd081a5cc8195e1c1a5c9959606a1b604482015290519081900360640190fd5b609b805460ff191660011790556040517f203d82d8d99f63bfecc8335216735e0271df4249ea752b030f9ab305b94e5afe90600090a1565b609c54600090610833613724565b609d828154811061084057fe5b60009182526020918290206040805160808101825260039093029091018054600f81810b810b810b8552600160801b909104810b810b900b938301939093526001830154908201526002909101546060820152905061089f8142611b27565b925050505b90565b6108af61175a565b6108ea5760405162461bcd60e51b81526004018080602001828103825260238152602001806138026023913960400191505060405180910390fd5b6108f381611c3b565b50565b60a45481565b609e602052816000526040600020818154811061091557fe5b6000918252602090912060039091020180546001820154600290920154600f82810b9550600160801b90920490910b925084565b61095233611cdc565b565b6002600154141561099a576040805162461bcd60e51b815260206004820152601f6024820152600080516020613825833981519152604482015290519081900360640190fd5b6002600155609b5460ff16156109ed576040805162461bcd60e51b815260206004820152601360248201527210dbdb9d1c9858dd081a5cc8195e1c1a5c9959606a1b604482015290519081900360640190fd5b6109f5613752565b5060408051808201825233600081815260a160208181529482208054600f90810b900b8552929091528352600101549181019190915281610a7d576040805162461bcd60e51b815260206004820152601a60248201527f4d757374207374616b65206e6f6e207a65726f20616d6f756e74000000000000604482015290519081900360640190fd5b60008160000151600f0b13610ad2576040805162461bcd60e51b8152602060048201526016602482015275139bc8195e1a5cdd1a5b99c81b1bd8dac8199bdd5b9960521b604482015290519081900360640190fd5b42816020015111610b145760405162461bcd60e51b81526004018080602001828103825260248152602001806138456024913960400191505060405180910390fd5b610b2333836000846001611f07565b505060018055565b6033546001600160a01b031690565b609b5460ff1681565b600043821115610b845760405162461bcd60e51b815260040180806020018281038252602281526020018061388a6022913960400191505060405180910390fd5b6000610b908484612030565b905080610ba1576000915050610e52565b610ba9613724565b6001600160a01b0385166000908152609e60205260409020805483908110610bcd57fe5b600091825260208083206040805160808101825260039094029091018054600f81810b810b810b8652600160801b909104810b810b900b92840192909252600182015490830152600201546060820152609c5490925090610c2e86836120f9565b9050610c38613724565b609d8281548110610c4557fe5b600091825260208083206040805160808101825260039094029091018054600f81810b810b810b8652600160801b909104810b810b900b9284019290925260018201549083015260020154606082015291508084841015610d5557610ca8613724565b609d610cbb86600163ffffffff61217c16565b81548110610cc557fe5b60009182526020918290206040805160808101825260039093029091018054600f81810b810b810b8552600160801b909104810b810b900b938301939093526001830154908201526002909101546060808301829052860151919250610d309163ffffffff6121dd16565b9250610d4d846040015182604001516121dd90919063ffffffff16565b915050610d87565b6060830151610d6b90439063ffffffff6121dd16565b9150610d848360400151426121dd90919063ffffffff16565b90505b60408301518215610ddc57610dd9610dcc84610dc0610db388606001518f6121dd90919063ffffffff16565b869063ffffffff61221f16565b9063ffffffff61227816565b829063ffffffff61217c16565b90505b610e1e610e0d610df98960400151846121dd90919063ffffffff16565b60208a0151600f0b9063ffffffff6122ba16565b8851600f0b9063ffffffff61238516565b600f90810b810b8089526000910b12610e455750509351600f0b9550610e52945050505050565b6000985050505050505050505b92915050565b6033546001600160a01b0316331490565b609c5481565b60026001541415610eb5576040805162461bcd60e51b815260206004820152601f6024820152600080516020613825833981519152604482015290519081900360640190fd5b6002600155609b5460ff1615610f08576040805162461bcd60e51b815260206004820152601360248201527210dbdb9d1c9858dd081a5cc8195e1c1a5c9959606a1b604482015290519081900360640190fd5b610f10613752565b5060408051808201825233600081815260a160208181529482208054600f90810b900b855292825284526001909101549282019290925290610f51836123fd565b905060008260000151600f0b13610fa3576040805162461bcd60e51b8152602060048201526011602482015270139bdd1a1a5b99c81a5cc81b1bd8dad959607a1b604482015290519081900360640190fd5b42826020015111610fea576040805162461bcd60e51b815260206004820152600c60248201526b131bd8dac8195e1c1a5c995960a21b604482015290519081900360640190fd5b81602001518111611042576040805162461bcd60e51b815260206004820152601b60248201527f43616e206f6e6c7920696e637265617365206c6f636b205745454b0000000000604482015290519081900360640190fd5b609a548111156110835760405162461bcd60e51b815260040180806020018281038252602c8152602001806138d2602c913960400191505060405180910390fd5b61109233600083856002611f07565b50506001805550565b600054610100900460ff16806110b457506110b4612416565b806110c2575060005460ff16155b6110fd5760405162461bcd60e51b815260040180806020018281038252602e8152602001806138fe602e913960400191505060405180910390fd5b600054610100900460ff16158015611128576000805460ff1961ff0019909116610100171660011790555b61113061241c565b611139836124c5565b61114282612517565b6111b68888888080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516020601f8c018190048102820181019092528a815292508a915089908190840183828082843760009201919091525061256992505050565b80156111c8576000805461ff00191690555b5050505050505050565b6001600160a01b0381166000908152609f6020526040812054806111fa5760009150506112dc565b611202613724565b6001600160a01b0384166000908152609e6020526040902080548390811061122657fe5b60009182526020918290206040805160808101825260039093029091018054600f81810b810b810b8552600160801b909104810b810b900b938301939093526001830154908201819052600290920154606082015291506112bb906112aa9061129690429063ffffffff6121dd16565b6020840151600f0b9063ffffffff6122ba16565b8251600f0b9063ffffffff61238516565b600f90810b810b8083526000910b12156112d457600081525b51600f0b9150505b919050565b6099546001600160a01b031681565b609f6020526000908152604090205481565b61130a610e58565b61135b576040805162461bcd60e51b815260206004820152601a60248201527f474f563a2063616c6c6572206973206e6f74207468652044616f000000000000604482015290519081900360640190fd5b604080516370a0823160e01b815230600482015290516000916001600160a01b038416916370a0823191602480820192602092909190829003018186803b1580156113a557600080fd5b505afa1580156113b9573d6000803e3d6000fd5b505050506040513d60208110156113cf57600080fd5b505190506001600160a01b03821663a9059cbb6113ea610b2b565b836040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561143a57600080fd5b505af115801561144e573d6000803e3d6000fd5b505050506040513d602081101561146457600080fd5b505161146f57600080fd5b5050565b609d818154811061148057fe5b6000918252602090912060039091020180546001820154600290920154600f82810b9450600160801b90920490910b919084565b60a3805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156107545780601f1061072957610100808354040283529160200191610754565b6000438211156115505760405162461bcd60e51b815260040180806020018281038252602281526020018061388a6022913960400191505060405180910390fd5b609c54600061155f84836120f9565b9050611569613724565b609d828154811061157657fe5b60009182526020918290206040805160808101825260039093029091018054600f81810b810b810b8552600160801b909104810b810b900b93830193909352600183015490820152600290910154606082018190529091508510156115e157600093505050506112dc565b6000838310156116d8576115f3613724565b609d61160685600163ffffffff61217c16565b8154811061161057fe5b60009182526020918290206040805160808101825260039093029091018054600f81810b810b810b8552600160801b909104810b810b900b938301939093526001830154908201526002909101546060808301829052850151919250146116d2576116cf61168f846060015183606001516121dd90919063ffffffff16565b610dc06116ad866040015185604001516121dd90919063ffffffff16565b60608701516116c3908c9063ffffffff6121dd16565b9063ffffffff61221f16565b91505b50611730565b438260600151146117305761172d6116fd8360600151436121dd90919063ffffffff16565b610dc06117178560400151426121dd90919063ffffffff16565b60608601516116c3908b9063ffffffff6121dd16565b90505b6117508261174b83856040015161217c90919063ffffffff16565b611b27565b9695505050505050565b6066546001600160a01b0316331490565b6066546001600160a01b031690565b62093a8090565b600260015414156117c7576040805162461bcd60e51b815260206004820152601f6024820152600080516020613825833981519152604482015290519081900360640190fd5b6002600155609b5460ff161561181a576040805162461bcd60e51b815260206004820152601360248201527210dbdb9d1c9858dd081a5cc8195e1c1a5c9959606a1b604482015290519081900360640190fd5b6000611825826123fd565b905061182f613752565b5060408051808201825233600081815260a160208181529482208054600f90810b900b85529290915283526001015491810191909152836118b7576040805162461bcd60e51b815260206004820152601a60248201527f4d757374207374616b65206e6f6e207a65726f20616d6f756e74000000000000604482015290519081900360640190fd5b8051600f0b1561190e576040805162461bcd60e51b815260206004820152601960248201527f5769746864726177206f6c6420746f6b656e7320666972737400000000000000604482015290519081900360640190fd5b42821161194c5760405162461bcd60e51b81526004018080602001828103825260268152602001806139d96026913960400191505060405180910390fd5b609a5482111561198d5760405162461bcd60e51b815260040180806020018281038252602c8152602001806138d2602c913960400191505060405180910390fd5b61199b338584846000611f07565b5050600180555050565b6119ad613752565b6108f360008283612775565b6001600160a01b0381166000908152609f602052604081205481908190806119eb575060009250829150819050611a7b565b6119f3613724565b6001600160a01b0386166000908152609e60205260409020805483908110611a1757fe5b60009182526020918290206040805160808101825260039093029091018054600f81810b810b810b808652600160801b909204810b810b900b9484018590526001820154928401839052600290910154606090930192909252909650909450925050505b9193909250565b60a16020526000908152604090208054600190910154600f9190910b9082565b611aaa610e58565b611afb576040805162461bcd60e51b815260206004820152601a60248201527f474f563a2063616c6c6572206973206e6f74207468652044616f000000000000604482015290519081900360640190fd5b6108f381612fe0565b6305a39a8081565b609a5481565b60a060205260009081526040902054600f0b81565b6000611b31613724565b8390506000611b4382604001516123fd565b905060005b60ff811015611c1957611b648262093a8063ffffffff61217c16565b9150600085831115611b7857859250611b8c565b50600082815260a06020526040902054600f0b5b611bce611bbd611ba98660400151866121dd90919063ffffffff16565b6020870151600f0b9063ffffffff6122ba16565b8551600f0b9063ffffffff61238516565b600f90810b900b845282861415611be55750611c19565b6020840151611bfd90600f0b8263ffffffff61309716565b600f90810b900b60208501525060408301829052600101611b48565b5060008260000151600f0b1215611c2f57600082525b5051600f0b9392505050565b6001600160a01b038116611c805760405162461bcd60e51b815260040180806020018281038252602381526020018061396e6023913960400191505060405180910390fd5b6066546040516001600160a01b038084169216907f370ebd24a4f14b319c7fa82630f6571686fc22c4c000f9b0523dd6dc5e3405db90600090a3606680546001600160a01b0319166001600160a01b0392909216919091179055565b60026001541415611d22576040805162461bcd60e51b815260206004820152601f6024820152600080516020613825833981519152604482015290519081900360640190fd5b6002600155611d2f613752565b506040805180820182526001600160a01b038316600081815260a160208181529482208054600f90810b900b8552929091528352600101549181018290529042101580611d7e5750609b5460ff165b611dc8576040805162461bcd60e51b8152602060048201526016602482015275546865206c6f636b206469646e27742065787069726560501b604482015290519081900360640190fd5b60008160000151600f0b13611e24576040805162461bcd60e51b815260206004820152601f60248201527f4d757374206861766520736f6d657468696e6720746f20776974686472617700604482015290519081900360640190fd5b8051600f0b611e31613752565b50604080518082018252600080825260208083018281526001600160a01b038816835260a190915292902081518154600f9190910b6001600160801b03166001600160801b03199091161781559151600190920191909155609b5460ff16611e9e57611e9e848483612775565b609954611ebb906001600160a01b0316858463ffffffff61310f16565b6040805183815242602082015281516001600160a01b038716927ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568928290030190a25050600180555050565b611f0f613752565b50604080518082019091528251600f90810b80835260208086015190840152611f3f910b8663ffffffff61309716565b600f90810b900b81528315611f5657602081018490525b6001600160a01b038616600090815260a16020908152604090912082518154600f9190910b6001600160801b03166001600160801b031990911617815590820151600190910155611fa8868483612775565b8415611fcc57609954611fcc906001600160a01b031687308863ffffffff61316616565b816002811115611fd857fe5b60208083015160408051898152928301919091524282820152516001600160a01b038916917fbe9cf0e939c614fad640a623a53ba0a807c8cb503c4c4c8dacabe27b86ff2dd5919081900360600190a3505050505050565b6001600160a01b0382166000908152609f60205260408120548190815b60808110156120ef57818310612062576120ef565b600061208a6002610dc0600161207e888863ffffffff61217c16565b9063ffffffff61217c16565b6001600160a01b0388166000908152609e6020526040902080549192508791839081106120b357fe5b906000526020600020906003020160020154116120d2578093506120e6565b6120e381600163ffffffff6121dd16565b92505b5060010161204d565b5090949350505050565b60008082815b60808110156120ef57818310612114576120ef565b60006121306002610dc0600161207e888863ffffffff61217c16565b905086609d828154811061214057fe5b9060005260206000209060030201600201541161215f57809350612173565b61217081600163ffffffff6121dd16565b92505b506001016120ff565b6000828201838110156121d6576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b60006121d683836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506131c6565b60008261222e57506000610e52565b8282028284828161223b57fe5b04146121d65760405162461bcd60e51b81526004018080602001828103825260218152602001806139916021913960400191505060405180910390fd5b60006121d683836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061325d565b600082600f0b600014156122d057506000610e52565b82600f0b6000191480156122ef5750600f82900b60016001607f1b0319145b1561232b5760405162461bcd60e51b81526004018080602001828103825260278152602001806139b26027913960400191505060405180910390fd5b6000828402905082600f0b84600f0b82600f0b8161234557fe5b05600f0b146121d65760405162461bcd60e51b81526004018080602001828103825260278152602001806139b26027913960400191505060405180910390fd5b6000818303600f83900b82138015906123a4575083600f0b81600f0b13155b806123c25750600083600f0b1280156123c2575083600f0b81600f0b135b6121d65760405162461bcd60e51b81526004018080602001828103825260248152602001806139ff6024913960400191505060405180910390fd5b6000610e5262093a806116c3848263ffffffff61227816565b303b1590565b600054610100900460ff16806124355750612435612416565b80612443575060005460ff16155b61247e5760405162461bcd60e51b815260040180806020018281038252602e8152602001806138fe602e913960400191505060405180910390fd5b600054610100900460ff161580156124a9576000805460ff1961ff0019909116610100171660011790555b6124b16132c2565b80156108f3576000805461ff001916905550565b603380546001600160a01b0319166001600160a01b0383811691909117918290556040519116906000907ffcde6c827a52b0870bc44ed9b10212272e18c9ea1725b772e9b493750afd8da4908290a350565b606680546001600160a01b0319166001600160a01b0383811691909117918290556040519116906000907f370ebd24a4f14b319c7fa82630f6571686fc22c4c000f9b0523dd6dc5e3405db908290a350565b609980546001600160a01b0319166001600160a01b03851617905561258c613724565b506040805160808101825260008082526020808301828152428486019081524360608601908152609d8054600181018255955285517fd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7b6003909602958601805494516001600160801b0319909516600f92830b6001600160801b03908116919091178116600160801b9690930b169490940217909255517fd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7c840155517fd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7d90920191909155825163313ce56760e01b8152925191926001600160a01b0387169263313ce56792600480840193919291829003018186803b1580156126ae57600080fd5b505afa1580156126c2573d6000803e3d6000fd5b505050506040513d60208110156126d857600080fd5b505160ff1660a4819055601210156127215760405162461bcd60e51b815260040180806020018281038252602181526020018061394d6021913960400191505060405180910390fd5b82516127349060a2906020860190613769565b5081516127489060a3906020850190613769565b50601260a455612762426305a39a8063ffffffff61217c16565b609a555050609b805460ff191690555050565b61277d613724565b612785613724565b609c5460009081906001600160a01b03881615612a20574287602001511180156127b6575060008760000151600f0b135b156128055786516127d490600f0b6305a39a8063ffffffff61336716565b600f90810b900b6020808701919091528701516127fb90611ba9904263ffffffff6121dd16565b600f90810b900b85525b42866020015111801561281f575060008660000151600f0b135b1561288257855161283d90600f0b6305a39a8063ffffffff61336716565b600f90810b900b60208086019190915286015161287890612864904263ffffffff6121dd16565b6020860151600f0b9063ffffffff6122ba16565b600f90810b900b84525b6001600160a01b0388166000908152609f602052604090205480612924576001600160a01b0389166000908152609e60209081526040808320805460018181018355918552938390208a516003909502018054938b0151600f90810b6001600160801b03908116600160801b029690910b81166001600160801b0319909516949094179093169390931782558801519181019190915560608701516002909101555b61293581600163ffffffff61217c16565b6001600160a01b038a166000818152609f6020908152604080832094909455428985019081524360608b01908152938352609e82528483208054600181810183559185528385208c5160039092020180548d860151600f90810b6001600160801b03908116600160801b0294820b81166001600160801b03199093169290921790911692909217815592519183019190915593516002909101558b810151825260a08152929020549189015191900b945015612a1e57876020015187602001511415612a0357839250612a1e565b602080880151600090815260a09091526040902054600f0b92505b505b612a28613724565b50604080516080810182526000808252602082015242918101919091524360608201528115612ab457609d8281548110612a5e57fe5b60009182526020918290206040805160808101825260039093029091018054600f81810b810b810b8552600160801b909104810b810b900b93830193909352600183015490820152600290910154606082015290505b6040810151612ac1613724565b5060408051608081018252600080825260208201819052848301805193830193909352606080860151908301529151909190421115612b3657612b33612b148560400151426121dd90919063ffffffff16565b610dc0612b2e8760600151436121dd90919063ffffffff16565b613437565b90505b6000612b41846123fd565b905060005b60ff811015612d5257612b628262093a8063ffffffff61217c16565b9150600042831115612b7657429250612b8a565b50600082815260a06020526040902054600f0b5b6000612b9f610df9858963ffffffff6121dd16565b8851909150612bb790600f0b8263ffffffff61238516565b600f90810b810b89526020890151612bd6910b8363ffffffff61309716565b600f90810b810b60208a015288516000910b1215612bf357600088525b60008860200151600f0b1215612c0b57600060208901525b83965083886040018181525050612c53612c42612c358860400151876121dd90919063ffffffff16565b879063ffffffff61345116565b60608801519063ffffffff61217c16565b6060890152612c6989600163ffffffff61217c16565b985042841415612c80575050436060870152612d52565b609d8054600181018255600091909152885160039091027fd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7b8101805460208c0151600f90810b6001600160801b03908116600160801b029590910b81166001600160801b031990921691909117169290921790915560408901517fd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7c82015560608901517fd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7d909101555050600101612b46565b50609c8690556001600160a01b038d1615612e0957612d9c612d888b602001518b60200151600f0b61238590919063ffffffff16565b6020870151600f0b9063ffffffff61309716565b600f90810b810b60208701528a518a51612dd492612dc39291900b9063ffffffff61238516565b8651600f0b9063ffffffff61309716565b600f90810b810b865260208601516000910b1215612df457600060208601525b60008560000151600f0b1215612e0957600085525b609d8054600181018255600091909152855160039091027fd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7b810180546020890151600f90810b6001600160801b03908116600160801b029590910b81166001600160801b031990921691909117169290921790915560408601517fd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7c82015560608601517fd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7d909101556001600160a01b038d1615612fd157428c602001511115612f6757612f068a6020015189600f0b61309790919063ffffffff16565b97508b602001518b602001511415612f3557612f32896020015189600f0b61238590919063ffffffff16565b97505b60208c810151600090815260a09091526040902080546001600160801b0319166001600160801b03600f8b900b161790555b428b602001511115612fd1578b602001518b602001511115612fd157612f9d896020015188600f0b61238590919063ffffffff16565b60208c810151600090815260a09091526040902080546001600160801b0319166001600160801b03600f84900b1617905596505b50505050505050505050505050565b6001600160a01b03811661303b576040805162461bcd60e51b815260206004820152601a60248201527f474f563a206e65772044616f2069732061646472657373283029000000000000604482015290519081900360640190fd5b6033546040516001600160a01b038084169216907ffcde6c827a52b0870bc44ed9b10212272e18c9ea1725b772e9b493750afd8da490600090a3603380546001600160a01b0319166001600160a01b0392909216919091179055565b6000828201600f83900b82138015906130b6575083600f0b81600f0b12155b806130d45750600083600f0b1280156130d4575083600f0b81600f0b125b6121d65760405162461bcd60e51b81526004018080602001828103825260218152602001806138696021913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052613161908490613466565b505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526131c0908590613466565b50505050565b600081848411156132555760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561321a578181015183820152602001613202565b50505050905090810190601f1680156132475780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600081836132ac5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561321a578181015183820152602001613202565b5060008385816132b857fe5b0495945050505050565b600054610100900460ff16806132db57506132db612416565b806132e9575060005460ff16155b6133245760405162461bcd60e51b815260040180806020018281038252602e8152602001806138fe602e913960400191505060405180910390fd5b600054610100900460ff1615801561334f576000805460ff1961ff0019909116610100171660011790555b6001805580156108f3576000805461ff001916905550565b600081600f0b600014156133c2576040805162461bcd60e51b815260206004820181905260248201527f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f604482015290519081900360640190fd5b81600f0b6000191480156133e15750600f83900b60016001607f1b0319145b1561341d5760405162461bcd60e51b815260040180806020018281038252602181526020018061392c6021913960400191505060405180910390fd5b600082600f0b84600f0b8161342e57fe5b05949350505050565b6000610e5282670de0b6b3a764000063ffffffff61221f16565b60006121d68383670de0b6b3a7640000613517565b60606134bb826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166135459092919063ffffffff16565b805190915015613161578080602001905160208110156134da57600080fd5b50516131615760405162461bcd60e51b815260040180806020018281038252602a815260200180613a23602a913960400191505060405180910390fd5b60008061352a858563ffffffff61221f16565b905061353c818463ffffffff61227816565b95945050505050565b6060613554848460008561355c565b949350505050565b60608247101561359d5760405162461bcd60e51b81526004018080602001828103825260268152602001806138ac6026913960400191505060405180910390fd5b6135a6856136b8565b6135f7576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b602083106136365780518252601f199092019160209182019101613617565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114613698576040519150601f19603f3d011682016040523d82523d6000602084013e61369d565b606091505b50915091506136ad8282866136be565b979650505050505050565b3b151590565b606083156136cd5750816121d6565b8251156136dd5782518084602001fd5b60405162461bcd60e51b815260206004820181815284516024840152845185939192839260440191908501908083836000831561321a578181015183820152602001613202565b60405180608001604052806000600f0b81526020016000600f0b815260200160008152602001600081525090565b604080518082019091526000808252602082015290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106137aa57805160ff19168380011785556137d7565b828001600101855582156137d7579182015b828111156137d75782518255916020019190600101906137bc565b506137e39291506137e7565b5090565b6108a491905b808211156137e357600081556001016137ed56fe474f563a2063616c6c6572206973206e6f74207468652050726f746f636f6c2044616f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0043616e6e6f742061646420746f2065787069726564206c6f636b2e2057697468647261775369676e6564536166654d6174683a206164646974696f6e206f766572666c6f774d757374207061737320626c6f636b206e756d62657220696e207468652070617374416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c566f74696e67206c6f636b2063616e2062652033207965617273206d61782028756e74696c207265636f6c29496e697469616c697a61626c653a20636f6e747261637420697320616c726561647920696e697469616c697a65645369676e6564536166654d6174683a206469766973696f6e206f766572666c6f7743616e6e6f742068617665206d6f7265207468616e20313820646563696d616c73474f563a206e65772050726f746f636f6c2044616f2069732061646472657373283029536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7743616e206f6e6c79206c6f636b20756e74696c2074696d6520696e20746865206675747572655369676e6564536166654d6174683a207375627472616374696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a264697066735822122002c8ea0f120c536e077c29133ee511f07d38696b10a9b965502a9b71763b67d864736f6c63430006080033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101fb5760003560e01c806372f702f31161011a578063ad2e8c9b116100ad578063cbf9fe5f1161007c578063cbf9fe5f14610619578063d8826fec14610662578063ee00ef3a14610688578063efe7a50414610690578063f52a36f714610698576101fb565b8063ad2e8c9b14610598578063b52c05fe146105a0578063c2c4c5c1146105c3578063c4698ee5146105cb576101fb565b806395d89b41116100e957806395d89b4114610563578063981b24d01461056b5780639d8ec51814610588578063a980cbf814610590576101fb565b806372f702f3146104f257806381fc83bb146104fa578063863a03e0146105205780638ad4c44714610546576101fb565b80634162169f116101925780635719098b116101615780635719098b146103c457806363160282146103cc5780636cf1dbed146103e957806370a08231146104cc576101fb565b80634162169f146103505780634c2067c7146103745780634ee2cd7e146103905780635029e602146103bc576101fb565b8063313ce567116101ce578063313ce567146102c757806334d901a4146102cf5780633ccfd60b1461032b578063403f444714610333576101fb565b806306fdde03146102005780630f73d0b41461027d57806318160ddd146102875780632d95baf9146102a1575b600080fd5b6102086106ce565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561024257818101518382015260200161022a565b50505050905090810190601f16801561026f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61028561075c565b005b61028f610825565b60408051918252519081900360200190f35b610285600480360360208110156102b757600080fd5b50356001600160a01b03166108a7565b61028f6108f6565b6102fb600480360360408110156102e557600080fd5b506001600160a01b0381351690602001356108fc565b60408051600f95860b860b815293850b90940b602084015282840191909152606082015290519081900360800190f35b610285610949565b6102856004803603602081101561034957600080fd5b5035610954565b610358610b2b565b604080516001600160a01b039092168252519081900360200190f35b61037c610b3a565b604080519115158252519081900360200190f35b61028f600480360360408110156103a657600080fd5b506001600160a01b038135169060200135610b43565b61037c610e58565b61028f610e69565b610285600480360360208110156103e257600080fd5b5035610e6f565b610285600480360360a08110156103ff57600080fd5b6001600160a01b03823516919081019060408101602082013564010000000081111561042a57600080fd5b82018360208201111561043c57600080fd5b8035906020019184600183028401116401000000008311171561045e57600080fd5b91939092909160208101903564010000000081111561047c57600080fd5b82018360208201111561048e57600080fd5b803590602001918460018302840111640100000000831117156104b057600080fd5b91935091506001600160a01b038135811691602001351661109b565b61028f600480360360208110156104e257600080fd5b50356001600160a01b03166111d2565b6103586112e1565b61028f6004803603602081101561051057600080fd5b50356001600160a01b03166112f0565b6102856004803603602081101561053657600080fd5b50356001600160a01b0316611302565b6102fb6004803603602081101561055c57600080fd5b5035611473565b6102086114b4565b61028f6004803603602081101561058157600080fd5b503561150f565b61037c61175a565b61035861176b565b61028f61177a565b610285600480360360408110156105b657600080fd5b5080359060200135611781565b6102856119a5565b6105f1600480360360208110156105e157600080fd5b50356001600160a01b03166119b9565b60408051600f94850b850b815292840b90930b60208301528183015290519081900360600190f35b61063f6004803603602081101561062f57600080fd5b50356001600160a01b0316611a82565b6040518083600f0b600f0b81526020018281526020019250505060405180910390f35b6102856004803603602081101561067857600080fd5b50356001600160a01b0316611aa2565b61028f611b04565b61028f611b0c565b6106b5600480360360208110156106ae57600080fd5b5035611b12565b60408051600f92830b90920b8252519081900360200190f35b60a2805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156107545780601f1061072957610100808354040283529160200191610754565b820191906000526020600020905b81548152906001019060200180831161073757829003601f168201915b505050505081565b61076461175a565b61079f5760405162461bcd60e51b81526004018080602001828103825260238152602001806138026023913960400191505060405180910390fd5b609b5460ff16156107ed576040805162461bcd60e51b815260206004820152601360248201527210dbdb9d1c9858dd081a5cc8195e1c1a5c9959606a1b604482015290519081900360640190fd5b609b805460ff191660011790556040517f203d82d8d99f63bfecc8335216735e0271df4249ea752b030f9ab305b94e5afe90600090a1565b609c54600090610833613724565b609d828154811061084057fe5b60009182526020918290206040805160808101825260039093029091018054600f81810b810b810b8552600160801b909104810b810b900b938301939093526001830154908201526002909101546060820152905061089f8142611b27565b925050505b90565b6108af61175a565b6108ea5760405162461bcd60e51b81526004018080602001828103825260238152602001806138026023913960400191505060405180910390fd5b6108f381611c3b565b50565b60a45481565b609e602052816000526040600020818154811061091557fe5b6000918252602090912060039091020180546001820154600290920154600f82810b9550600160801b90920490910b925084565b61095233611cdc565b565b6002600154141561099a576040805162461bcd60e51b815260206004820152601f6024820152600080516020613825833981519152604482015290519081900360640190fd5b6002600155609b5460ff16156109ed576040805162461bcd60e51b815260206004820152601360248201527210dbdb9d1c9858dd081a5cc8195e1c1a5c9959606a1b604482015290519081900360640190fd5b6109f5613752565b5060408051808201825233600081815260a160208181529482208054600f90810b900b8552929091528352600101549181019190915281610a7d576040805162461bcd60e51b815260206004820152601a60248201527f4d757374207374616b65206e6f6e207a65726f20616d6f756e74000000000000604482015290519081900360640190fd5b60008160000151600f0b13610ad2576040805162461bcd60e51b8152602060048201526016602482015275139bc8195e1a5cdd1a5b99c81b1bd8dac8199bdd5b9960521b604482015290519081900360640190fd5b42816020015111610b145760405162461bcd60e51b81526004018080602001828103825260248152602001806138456024913960400191505060405180910390fd5b610b2333836000846001611f07565b505060018055565b6033546001600160a01b031690565b609b5460ff1681565b600043821115610b845760405162461bcd60e51b815260040180806020018281038252602281526020018061388a6022913960400191505060405180910390fd5b6000610b908484612030565b905080610ba1576000915050610e52565b610ba9613724565b6001600160a01b0385166000908152609e60205260409020805483908110610bcd57fe5b600091825260208083206040805160808101825260039094029091018054600f81810b810b810b8652600160801b909104810b810b900b92840192909252600182015490830152600201546060820152609c5490925090610c2e86836120f9565b9050610c38613724565b609d8281548110610c4557fe5b600091825260208083206040805160808101825260039094029091018054600f81810b810b810b8652600160801b909104810b810b900b9284019290925260018201549083015260020154606082015291508084841015610d5557610ca8613724565b609d610cbb86600163ffffffff61217c16565b81548110610cc557fe5b60009182526020918290206040805160808101825260039093029091018054600f81810b810b810b8552600160801b909104810b810b900b938301939093526001830154908201526002909101546060808301829052860151919250610d309163ffffffff6121dd16565b9250610d4d846040015182604001516121dd90919063ffffffff16565b915050610d87565b6060830151610d6b90439063ffffffff6121dd16565b9150610d848360400151426121dd90919063ffffffff16565b90505b60408301518215610ddc57610dd9610dcc84610dc0610db388606001518f6121dd90919063ffffffff16565b869063ffffffff61221f16565b9063ffffffff61227816565b829063ffffffff61217c16565b90505b610e1e610e0d610df98960400151846121dd90919063ffffffff16565b60208a0151600f0b9063ffffffff6122ba16565b8851600f0b9063ffffffff61238516565b600f90810b810b8089526000910b12610e455750509351600f0b9550610e52945050505050565b6000985050505050505050505b92915050565b6033546001600160a01b0316331490565b609c5481565b60026001541415610eb5576040805162461bcd60e51b815260206004820152601f6024820152600080516020613825833981519152604482015290519081900360640190fd5b6002600155609b5460ff1615610f08576040805162461bcd60e51b815260206004820152601360248201527210dbdb9d1c9858dd081a5cc8195e1c1a5c9959606a1b604482015290519081900360640190fd5b610f10613752565b5060408051808201825233600081815260a160208181529482208054600f90810b900b855292825284526001909101549282019290925290610f51836123fd565b905060008260000151600f0b13610fa3576040805162461bcd60e51b8152602060048201526011602482015270139bdd1a1a5b99c81a5cc81b1bd8dad959607a1b604482015290519081900360640190fd5b42826020015111610fea576040805162461bcd60e51b815260206004820152600c60248201526b131bd8dac8195e1c1a5c995960a21b604482015290519081900360640190fd5b81602001518111611042576040805162461bcd60e51b815260206004820152601b60248201527f43616e206f6e6c7920696e637265617365206c6f636b205745454b0000000000604482015290519081900360640190fd5b609a548111156110835760405162461bcd60e51b815260040180806020018281038252602c8152602001806138d2602c913960400191505060405180910390fd5b61109233600083856002611f07565b50506001805550565b600054610100900460ff16806110b457506110b4612416565b806110c2575060005460ff16155b6110fd5760405162461bcd60e51b815260040180806020018281038252602e8152602001806138fe602e913960400191505060405180910390fd5b600054610100900460ff16158015611128576000805460ff1961ff0019909116610100171660011790555b61113061241c565b611139836124c5565b61114282612517565b6111b68888888080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516020601f8c018190048102820181019092528a815292508a915089908190840183828082843760009201919091525061256992505050565b80156111c8576000805461ff00191690555b5050505050505050565b6001600160a01b0381166000908152609f6020526040812054806111fa5760009150506112dc565b611202613724565b6001600160a01b0384166000908152609e6020526040902080548390811061122657fe5b60009182526020918290206040805160808101825260039093029091018054600f81810b810b810b8552600160801b909104810b810b900b938301939093526001830154908201819052600290920154606082015291506112bb906112aa9061129690429063ffffffff6121dd16565b6020840151600f0b9063ffffffff6122ba16565b8251600f0b9063ffffffff61238516565b600f90810b810b8083526000910b12156112d457600081525b51600f0b9150505b919050565b6099546001600160a01b031681565b609f6020526000908152604090205481565b61130a610e58565b61135b576040805162461bcd60e51b815260206004820152601a60248201527f474f563a2063616c6c6572206973206e6f74207468652044616f000000000000604482015290519081900360640190fd5b604080516370a0823160e01b815230600482015290516000916001600160a01b038416916370a0823191602480820192602092909190829003018186803b1580156113a557600080fd5b505afa1580156113b9573d6000803e3d6000fd5b505050506040513d60208110156113cf57600080fd5b505190506001600160a01b03821663a9059cbb6113ea610b2b565b836040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561143a57600080fd5b505af115801561144e573d6000803e3d6000fd5b505050506040513d602081101561146457600080fd5b505161146f57600080fd5b5050565b609d818154811061148057fe5b6000918252602090912060039091020180546001820154600290920154600f82810b9450600160801b90920490910b919084565b60a3805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156107545780601f1061072957610100808354040283529160200191610754565b6000438211156115505760405162461bcd60e51b815260040180806020018281038252602281526020018061388a6022913960400191505060405180910390fd5b609c54600061155f84836120f9565b9050611569613724565b609d828154811061157657fe5b60009182526020918290206040805160808101825260039093029091018054600f81810b810b810b8552600160801b909104810b810b900b93830193909352600183015490820152600290910154606082018190529091508510156115e157600093505050506112dc565b6000838310156116d8576115f3613724565b609d61160685600163ffffffff61217c16565b8154811061161057fe5b60009182526020918290206040805160808101825260039093029091018054600f81810b810b810b8552600160801b909104810b810b900b938301939093526001830154908201526002909101546060808301829052850151919250146116d2576116cf61168f846060015183606001516121dd90919063ffffffff16565b610dc06116ad866040015185604001516121dd90919063ffffffff16565b60608701516116c3908c9063ffffffff6121dd16565b9063ffffffff61221f16565b91505b50611730565b438260600151146117305761172d6116fd8360600151436121dd90919063ffffffff16565b610dc06117178560400151426121dd90919063ffffffff16565b60608601516116c3908b9063ffffffff6121dd16565b90505b6117508261174b83856040015161217c90919063ffffffff16565b611b27565b9695505050505050565b6066546001600160a01b0316331490565b6066546001600160a01b031690565b62093a8090565b600260015414156117c7576040805162461bcd60e51b815260206004820152601f6024820152600080516020613825833981519152604482015290519081900360640190fd5b6002600155609b5460ff161561181a576040805162461bcd60e51b815260206004820152601360248201527210dbdb9d1c9858dd081a5cc8195e1c1a5c9959606a1b604482015290519081900360640190fd5b6000611825826123fd565b905061182f613752565b5060408051808201825233600081815260a160208181529482208054600f90810b900b85529290915283526001015491810191909152836118b7576040805162461bcd60e51b815260206004820152601a60248201527f4d757374207374616b65206e6f6e207a65726f20616d6f756e74000000000000604482015290519081900360640190fd5b8051600f0b1561190e576040805162461bcd60e51b815260206004820152601960248201527f5769746864726177206f6c6420746f6b656e7320666972737400000000000000604482015290519081900360640190fd5b42821161194c5760405162461bcd60e51b81526004018080602001828103825260268152602001806139d96026913960400191505060405180910390fd5b609a5482111561198d5760405162461bcd60e51b815260040180806020018281038252602c8152602001806138d2602c913960400191505060405180910390fd5b61199b338584846000611f07565b5050600180555050565b6119ad613752565b6108f360008283612775565b6001600160a01b0381166000908152609f602052604081205481908190806119eb575060009250829150819050611a7b565b6119f3613724565b6001600160a01b0386166000908152609e60205260409020805483908110611a1757fe5b60009182526020918290206040805160808101825260039093029091018054600f81810b810b810b808652600160801b909204810b810b900b9484018590526001820154928401839052600290910154606090930192909252909650909450925050505b9193909250565b60a16020526000908152604090208054600190910154600f9190910b9082565b611aaa610e58565b611afb576040805162461bcd60e51b815260206004820152601a60248201527f474f563a2063616c6c6572206973206e6f74207468652044616f000000000000604482015290519081900360640190fd5b6108f381612fe0565b6305a39a8081565b609a5481565b60a060205260009081526040902054600f0b81565b6000611b31613724565b8390506000611b4382604001516123fd565b905060005b60ff811015611c1957611b648262093a8063ffffffff61217c16565b9150600085831115611b7857859250611b8c565b50600082815260a06020526040902054600f0b5b611bce611bbd611ba98660400151866121dd90919063ffffffff16565b6020870151600f0b9063ffffffff6122ba16565b8551600f0b9063ffffffff61238516565b600f90810b900b845282861415611be55750611c19565b6020840151611bfd90600f0b8263ffffffff61309716565b600f90810b900b60208501525060408301829052600101611b48565b5060008260000151600f0b1215611c2f57600082525b5051600f0b9392505050565b6001600160a01b038116611c805760405162461bcd60e51b815260040180806020018281038252602381526020018061396e6023913960400191505060405180910390fd5b6066546040516001600160a01b038084169216907f370ebd24a4f14b319c7fa82630f6571686fc22c4c000f9b0523dd6dc5e3405db90600090a3606680546001600160a01b0319166001600160a01b0392909216919091179055565b60026001541415611d22576040805162461bcd60e51b815260206004820152601f6024820152600080516020613825833981519152604482015290519081900360640190fd5b6002600155611d2f613752565b506040805180820182526001600160a01b038316600081815260a160208181529482208054600f90810b900b8552929091528352600101549181018290529042101580611d7e5750609b5460ff165b611dc8576040805162461bcd60e51b8152602060048201526016602482015275546865206c6f636b206469646e27742065787069726560501b604482015290519081900360640190fd5b60008160000151600f0b13611e24576040805162461bcd60e51b815260206004820152601f60248201527f4d757374206861766520736f6d657468696e6720746f20776974686472617700604482015290519081900360640190fd5b8051600f0b611e31613752565b50604080518082018252600080825260208083018281526001600160a01b038816835260a190915292902081518154600f9190910b6001600160801b03166001600160801b03199091161781559151600190920191909155609b5460ff16611e9e57611e9e848483612775565b609954611ebb906001600160a01b0316858463ffffffff61310f16565b6040805183815242602082015281516001600160a01b038716927ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568928290030190a25050600180555050565b611f0f613752565b50604080518082019091528251600f90810b80835260208086015190840152611f3f910b8663ffffffff61309716565b600f90810b900b81528315611f5657602081018490525b6001600160a01b038616600090815260a16020908152604090912082518154600f9190910b6001600160801b03166001600160801b031990911617815590820151600190910155611fa8868483612775565b8415611fcc57609954611fcc906001600160a01b031687308863ffffffff61316616565b816002811115611fd857fe5b60208083015160408051898152928301919091524282820152516001600160a01b038916917fbe9cf0e939c614fad640a623a53ba0a807c8cb503c4c4c8dacabe27b86ff2dd5919081900360600190a3505050505050565b6001600160a01b0382166000908152609f60205260408120548190815b60808110156120ef57818310612062576120ef565b600061208a6002610dc0600161207e888863ffffffff61217c16565b9063ffffffff61217c16565b6001600160a01b0388166000908152609e6020526040902080549192508791839081106120b357fe5b906000526020600020906003020160020154116120d2578093506120e6565b6120e381600163ffffffff6121dd16565b92505b5060010161204d565b5090949350505050565b60008082815b60808110156120ef57818310612114576120ef565b60006121306002610dc0600161207e888863ffffffff61217c16565b905086609d828154811061214057fe5b9060005260206000209060030201600201541161215f57809350612173565b61217081600163ffffffff6121dd16565b92505b506001016120ff565b6000828201838110156121d6576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b60006121d683836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506131c6565b60008261222e57506000610e52565b8282028284828161223b57fe5b04146121d65760405162461bcd60e51b81526004018080602001828103825260218152602001806139916021913960400191505060405180910390fd5b60006121d683836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061325d565b600082600f0b600014156122d057506000610e52565b82600f0b6000191480156122ef5750600f82900b60016001607f1b0319145b1561232b5760405162461bcd60e51b81526004018080602001828103825260278152602001806139b26027913960400191505060405180910390fd5b6000828402905082600f0b84600f0b82600f0b8161234557fe5b05600f0b146121d65760405162461bcd60e51b81526004018080602001828103825260278152602001806139b26027913960400191505060405180910390fd5b6000818303600f83900b82138015906123a4575083600f0b81600f0b13155b806123c25750600083600f0b1280156123c2575083600f0b81600f0b135b6121d65760405162461bcd60e51b81526004018080602001828103825260248152602001806139ff6024913960400191505060405180910390fd5b6000610e5262093a806116c3848263ffffffff61227816565b303b1590565b600054610100900460ff16806124355750612435612416565b80612443575060005460ff16155b61247e5760405162461bcd60e51b815260040180806020018281038252602e8152602001806138fe602e913960400191505060405180910390fd5b600054610100900460ff161580156124a9576000805460ff1961ff0019909116610100171660011790555b6124b16132c2565b80156108f3576000805461ff001916905550565b603380546001600160a01b0319166001600160a01b0383811691909117918290556040519116906000907ffcde6c827a52b0870bc44ed9b10212272e18c9ea1725b772e9b493750afd8da4908290a350565b606680546001600160a01b0319166001600160a01b0383811691909117918290556040519116906000907f370ebd24a4f14b319c7fa82630f6571686fc22c4c000f9b0523dd6dc5e3405db908290a350565b609980546001600160a01b0319166001600160a01b03851617905561258c613724565b506040805160808101825260008082526020808301828152428486019081524360608601908152609d8054600181018255955285517fd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7b6003909602958601805494516001600160801b0319909516600f92830b6001600160801b03908116919091178116600160801b9690930b169490940217909255517fd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7c840155517fd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7d90920191909155825163313ce56760e01b8152925191926001600160a01b0387169263313ce56792600480840193919291829003018186803b1580156126ae57600080fd5b505afa1580156126c2573d6000803e3d6000fd5b505050506040513d60208110156126d857600080fd5b505160ff1660a4819055601210156127215760405162461bcd60e51b815260040180806020018281038252602181526020018061394d6021913960400191505060405180910390fd5b82516127349060a2906020860190613769565b5081516127489060a3906020850190613769565b50601260a455612762426305a39a8063ffffffff61217c16565b609a555050609b805460ff191690555050565b61277d613724565b612785613724565b609c5460009081906001600160a01b03881615612a20574287602001511180156127b6575060008760000151600f0b135b156128055786516127d490600f0b6305a39a8063ffffffff61336716565b600f90810b900b6020808701919091528701516127fb90611ba9904263ffffffff6121dd16565b600f90810b900b85525b42866020015111801561281f575060008660000151600f0b135b1561288257855161283d90600f0b6305a39a8063ffffffff61336716565b600f90810b900b60208086019190915286015161287890612864904263ffffffff6121dd16565b6020860151600f0b9063ffffffff6122ba16565b600f90810b900b84525b6001600160a01b0388166000908152609f602052604090205480612924576001600160a01b0389166000908152609e60209081526040808320805460018181018355918552938390208a516003909502018054938b0151600f90810b6001600160801b03908116600160801b029690910b81166001600160801b0319909516949094179093169390931782558801519181019190915560608701516002909101555b61293581600163ffffffff61217c16565b6001600160a01b038a166000818152609f6020908152604080832094909455428985019081524360608b01908152938352609e82528483208054600181810183559185528385208c5160039092020180548d860151600f90810b6001600160801b03908116600160801b0294820b81166001600160801b03199093169290921790911692909217815592519183019190915593516002909101558b810151825260a08152929020549189015191900b945015612a1e57876020015187602001511415612a0357839250612a1e565b602080880151600090815260a09091526040902054600f0b92505b505b612a28613724565b50604080516080810182526000808252602082015242918101919091524360608201528115612ab457609d8281548110612a5e57fe5b60009182526020918290206040805160808101825260039093029091018054600f81810b810b810b8552600160801b909104810b810b900b93830193909352600183015490820152600290910154606082015290505b6040810151612ac1613724565b5060408051608081018252600080825260208201819052848301805193830193909352606080860151908301529151909190421115612b3657612b33612b148560400151426121dd90919063ffffffff16565b610dc0612b2e8760600151436121dd90919063ffffffff16565b613437565b90505b6000612b41846123fd565b905060005b60ff811015612d5257612b628262093a8063ffffffff61217c16565b9150600042831115612b7657429250612b8a565b50600082815260a06020526040902054600f0b5b6000612b9f610df9858963ffffffff6121dd16565b8851909150612bb790600f0b8263ffffffff61238516565b600f90810b810b89526020890151612bd6910b8363ffffffff61309716565b600f90810b810b60208a015288516000910b1215612bf357600088525b60008860200151600f0b1215612c0b57600060208901525b83965083886040018181525050612c53612c42612c358860400151876121dd90919063ffffffff16565b879063ffffffff61345116565b60608801519063ffffffff61217c16565b6060890152612c6989600163ffffffff61217c16565b985042841415612c80575050436060870152612d52565b609d8054600181018255600091909152885160039091027fd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7b8101805460208c0151600f90810b6001600160801b03908116600160801b029590910b81166001600160801b031990921691909117169290921790915560408901517fd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7c82015560608901517fd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7d909101555050600101612b46565b50609c8690556001600160a01b038d1615612e0957612d9c612d888b602001518b60200151600f0b61238590919063ffffffff16565b6020870151600f0b9063ffffffff61309716565b600f90810b810b60208701528a518a51612dd492612dc39291900b9063ffffffff61238516565b8651600f0b9063ffffffff61309716565b600f90810b810b865260208601516000910b1215612df457600060208601525b60008560000151600f0b1215612e0957600085525b609d8054600181018255600091909152855160039091027fd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7b810180546020890151600f90810b6001600160801b03908116600160801b029590910b81166001600160801b031990921691909117169290921790915560408601517fd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7c82015560608601517fd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7d909101556001600160a01b038d1615612fd157428c602001511115612f6757612f068a6020015189600f0b61309790919063ffffffff16565b97508b602001518b602001511415612f3557612f32896020015189600f0b61238590919063ffffffff16565b97505b60208c810151600090815260a09091526040902080546001600160801b0319166001600160801b03600f8b900b161790555b428b602001511115612fd1578b602001518b602001511115612fd157612f9d896020015188600f0b61238590919063ffffffff16565b60208c810151600090815260a09091526040902080546001600160801b0319166001600160801b03600f84900b1617905596505b50505050505050505050505050565b6001600160a01b03811661303b576040805162461bcd60e51b815260206004820152601a60248201527f474f563a206e65772044616f2069732061646472657373283029000000000000604482015290519081900360640190fd5b6033546040516001600160a01b038084169216907ffcde6c827a52b0870bc44ed9b10212272e18c9ea1725b772e9b493750afd8da490600090a3603380546001600160a01b0319166001600160a01b0392909216919091179055565b6000828201600f83900b82138015906130b6575083600f0b81600f0b12155b806130d45750600083600f0b1280156130d4575083600f0b81600f0b125b6121d65760405162461bcd60e51b81526004018080602001828103825260218152602001806138696021913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052613161908490613466565b505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526131c0908590613466565b50505050565b600081848411156132555760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561321a578181015183820152602001613202565b50505050905090810190601f1680156132475780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600081836132ac5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561321a578181015183820152602001613202565b5060008385816132b857fe5b0495945050505050565b600054610100900460ff16806132db57506132db612416565b806132e9575060005460ff16155b6133245760405162461bcd60e51b815260040180806020018281038252602e8152602001806138fe602e913960400191505060405180910390fd5b600054610100900460ff1615801561334f576000805460ff1961ff0019909116610100171660011790555b6001805580156108f3576000805461ff001916905550565b600081600f0b600014156133c2576040805162461bcd60e51b815260206004820181905260248201527f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f604482015290519081900360640190fd5b81600f0b6000191480156133e15750600f83900b60016001607f1b0319145b1561341d5760405162461bcd60e51b815260040180806020018281038252602181526020018061392c6021913960400191505060405180910390fd5b600082600f0b84600f0b8161342e57fe5b05949350505050565b6000610e5282670de0b6b3a764000063ffffffff61221f16565b60006121d68383670de0b6b3a7640000613517565b60606134bb826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166135459092919063ffffffff16565b805190915015613161578080602001905160208110156134da57600080fd5b50516131615760405162461bcd60e51b815260040180806020018281038252602a815260200180613a23602a913960400191505060405180910390fd5b60008061352a858563ffffffff61221f16565b905061353c818463ffffffff61227816565b95945050505050565b6060613554848460008561355c565b949350505050565b60608247101561359d5760405162461bcd60e51b81526004018080602001828103825260268152602001806138ac6026913960400191505060405180910390fd5b6135a6856136b8565b6135f7576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b602083106136365780518252601f199092019160209182019101613617565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114613698576040519150601f19603f3d011682016040523d82523d6000602084013e61369d565b606091505b50915091506136ad8282866136be565b979650505050505050565b3b151590565b606083156136cd5750816121d6565b8251156136dd5782518084602001fd5b60405162461bcd60e51b815260206004820181815284516024840152845185939192839260440191908501908083836000831561321a578181015183820152602001613202565b60405180608001604052806000600f0b81526020016000600f0b815260200160008152602001600081525090565b604080518082019091526000808252602082015290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106137aa57805160ff19168380011785556137d7565b828001600101855582156137d7579182015b828111156137d75782518255916020019190600101906137bc565b506137e39291506137e7565b5090565b6108a491905b808211156137e357600081556001016137ed56fe474f563a2063616c6c6572206973206e6f74207468652050726f746f636f6c2044616f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0043616e6e6f742061646420746f2065787069726564206c6f636b2e2057697468647261775369676e6564536166654d6174683a206164646974696f6e206f766572666c6f774d757374207061737320626c6f636b206e756d62657220696e207468652070617374416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c566f74696e67206c6f636b2063616e2062652033207965617273206d61782028756e74696c207265636f6c29496e697469616c697a61626c653a20636f6e747261637420697320616c726561647920696e697469616c697a65645369676e6564536166654d6174683a206469766973696f6e206f766572666c6f7743616e6e6f742068617665206d6f7265207468616e20313820646563696d616c73474f563a206e65772050726f746f636f6c2044616f2069732061646472657373283029536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7743616e206f6e6c79206c6f636b20756e74696c2074696d6520696e20746865206675747572655369676e6564536166654d6174683a207375627472616374696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a264697066735822122002c8ea0f120c536e077c29133ee511f07d38696b10a9b965502a9b71763b67d864736f6c63430006080033

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
[ Download: CSV Export  ]

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.