Token veDYST

 

Overview ERC-721

Total Supply:
99,722,595.126325 veDYST

Holders:
525 addresses
 
Balance
0.000000000000000001 veDYST
0x4A170561126a1D765A6B80a081dB639945a8b53b
Loading
[ Download CSV Export  ] 
Loading
[ Download CSV Export  ] 
Loading

Click here to update the token ICO / general information
# Exchange Pair Price  24H Volume % Volume
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Ve

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 13 : Ve.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

import "../../lib/Base64.sol";
import "../../interface/IERC20.sol";
import "../../interface/IERC721.sol";
import "../../interface/IERC721Metadata.sol";
import "../../interface/IVe.sol";
import "../../interface/IERC721Receiver.sol";
import "../../interface/IController.sol";
import "../Reentrancy.sol";
import "../../lib/SafeERC20.sol";
import "../../lib/Math.sol";

contract Ve is IERC721, IERC721Metadata, IVe, Reentrancy {
  using SafeERC20 for IERC20;

  uint internal constant WEEK = 1 weeks;
  uint internal constant MAX_TIME = 4 * 365 * 86400;
  int128 internal constant I_MAX_TIME = 4 * 365 * 86400;
  uint internal constant MULTIPLIER = 1 ether;

  address immutable public override token;
  uint public supply;
  mapping(uint => LockedBalance) public locked;

  mapping(uint => uint) public ownershipChange;

  uint public override epoch;
  /// @dev epoch -> unsigned point
  mapping(uint => Point) internal _pointHistory;
  /// @dev user -> Point[userEpoch]
  mapping(uint => Point[1000000000]) internal _userPointHistory;

  mapping(uint => uint) public override userPointEpoch;
  mapping(uint => int128) public slopeChanges; // time -> signed slope change

  mapping(uint => uint) public attachments;
  mapping(uint => bool) public voted;
  address public controller;

  string constant public override name = "veDYST";
  string constant public override symbol = "veDYST";
  string constant public version = "1.0.0";
  uint8 constant public decimals = 18;

  /// @dev Current count of token
  uint internal tokenId;

  /// @dev Mapping from NFT ID to the address that owns it.
  mapping(uint => address) internal idToOwner;

  /// @dev Mapping from NFT ID to approved address.
  mapping(uint => address) internal idToApprovals;

  /// @dev Mapping from owner address to count of his tokens.
  mapping(address => uint) internal ownerToNFTokenCount;

  /// @dev Mapping from owner address to mapping of index to tokenIds
  mapping(address => mapping(uint => uint)) internal ownerToNFTokenIdList;

  /// @dev Mapping from NFT ID to index of owner
  mapping(uint => uint) internal tokenToOwnerIndex;

  /// @dev Mapping from owner address to mapping of operator addresses.
  mapping(address => mapping(address => bool)) internal ownerToOperators;

  /// @dev Mapping of interface id to bool about whether or not it's supported
  mapping(bytes4 => bool) internal supportedInterfaces;

  /// @dev ERC165 interface ID of ERC165
  bytes4 internal constant ERC165_INTERFACE_ID = 0x01ffc9a7;

  /// @dev ERC165 interface ID of ERC721
  bytes4 internal constant ERC721_INTERFACE_ID = 0x80ac58cd;

  /// @dev ERC165 interface ID of ERC721Metadata
  bytes4 internal constant ERC721_METADATA_INTERFACE_ID = 0x5b5e139f;

  event Deposit(
    address indexed provider,
    uint tokenId,
    uint value,
    uint indexed locktime,
    DepositType depositType,
    uint ts
  );
  event Withdraw(address indexed provider, uint tokenId, uint value, uint ts);
  event Supply(uint prevSupply, uint supply);

  /// @notice Contract constructor
  /// @param token_ `ERC20CRV` token address
  constructor(address token_, address controller_) {
    token = token_;
    controller = controller_;
    _pointHistory[0].blk = block.number;
    _pointHistory[0].ts = block.timestamp;

    supportedInterfaces[ERC165_INTERFACE_ID] = true;
    supportedInterfaces[ERC721_INTERFACE_ID] = true;
    supportedInterfaces[ERC721_METADATA_INTERFACE_ID] = true;

    // mint-ish
    emit Transfer(address(0), address(this), tokenId);
    // burn-ish
    emit Transfer(address(this), address(0), tokenId);
  }

  function _voter() internal view returns (address) {
    return IController(controller).voter();
  }

  /// @dev Interface identification is specified in ERC-165.
  /// @param _interfaceID Id of the interface
  function supportsInterface(bytes4 _interfaceID) external view override returns (bool) {
    return supportedInterfaces[_interfaceID];
  }

  /// @notice Get the most recently recorded rate of voting power decrease for `_tokenId`
  /// @param _tokenId token of the NFT
  /// @return Value of the slope
  function getLastUserSlope(uint _tokenId) external view returns (int128) {
    uint uEpoch = userPointEpoch[_tokenId];
    return _userPointHistory[_tokenId][uEpoch].slope;
  }

  /// @notice Get the timestamp for checkpoint `_idx` for `_tokenId`
  /// @param _tokenId token of the NFT
  /// @param _idx User epoch number
  /// @return Epoch time of the checkpoint
  function userPointHistoryTs(uint _tokenId, uint _idx) external view returns (uint) {
    return _userPointHistory[_tokenId][_idx].ts;
  }

  /// @notice Get timestamp when `_tokenId`'s lock finishes
  /// @param _tokenId User NFT
  /// @return Epoch time of the lock end
  function lockedEnd(uint _tokenId) external view returns (uint) {
    return locked[_tokenId].end;
  }

  /// @dev Returns the number of NFTs owned by `_owner`.
  ///      Throws if `_owner` is the zero address. NFTs assigned to the zero address are considered invalid.
  /// @param _owner Address for whom to query the balance.
  function _balance(address _owner) internal view returns (uint) {
    return ownerToNFTokenCount[_owner];
  }

  /// @dev Returns the number of NFTs owned by `_owner`.
  ///      Throws if `_owner` is the zero address. NFTs assigned to the zero address are considered invalid.
  /// @param _owner Address for whom to query the balance.
  function balanceOf(address _owner) external view override returns (uint) {
    return _balance(_owner);
  }

  /// @dev Returns the address of the owner of the NFT.
  /// @param _tokenId The identifier for an NFT.
  function ownerOf(uint _tokenId) public view override returns (address) {
    return idToOwner[_tokenId];
  }

  /// @dev Get the approved address for a single NFT.
  /// @param _tokenId ID of the NFT to query the approval of.
  function getApproved(uint _tokenId) external view override returns (address) {
    return idToApprovals[_tokenId];
  }

  /// @dev Checks if `_operator` is an approved operator for `_owner`.
  /// @param _owner The address that owns the NFTs.
  /// @param _operator The address that acts on behalf of the owner.
  function isApprovedForAll(address _owner, address _operator) external view override returns (bool) {
    return (ownerToOperators[_owner])[_operator];
  }

  /// @dev  Get token by index
  function tokenOfOwnerByIndex(address _owner, uint _tokenIndex) external view returns (uint) {
    return ownerToNFTokenIdList[_owner][_tokenIndex];
  }

  /// @dev Returns whether the given spender can transfer a given token ID
  /// @param _spender address of the spender to query
  /// @param _tokenId uint ID of the token to be transferred
  /// @return bool whether the msg.sender is approved for the given token ID, is an operator of the owner, or is the owner of the token
  function _isApprovedOrOwner(address _spender, uint _tokenId) internal view returns (bool) {
    address owner = idToOwner[_tokenId];
    bool spenderIsOwner = owner == _spender;
    bool spenderIsApproved = _spender == idToApprovals[_tokenId];
    bool spenderIsApprovedForAll = (ownerToOperators[owner])[_spender];
    return spenderIsOwner || spenderIsApproved || spenderIsApprovedForAll;
  }

  function isApprovedOrOwner(address _spender, uint _tokenId) external view override returns (bool) {
    return _isApprovedOrOwner(_spender, _tokenId);
  }

  /// @dev Add a NFT to an index mapping to a given address
  /// @param _to address of the receiver
  /// @param _tokenId uint ID Of the token to be added
  function _addTokenToOwnerList(address _to, uint _tokenId) internal {
    uint currentCount = _balance(_to);

    ownerToNFTokenIdList[_to][currentCount] = _tokenId;
    tokenToOwnerIndex[_tokenId] = currentCount;
  }

  /// @dev Remove a NFT from an index mapping to a given address
  /// @param _from address of the sender
  /// @param _tokenId uint ID Of the token to be removed
  function _removeTokenFromOwnerList(address _from, uint _tokenId) internal {
    // Delete
    uint currentCount = _balance(_from) - 1;
    uint currentIndex = tokenToOwnerIndex[_tokenId];

    if (currentCount == currentIndex) {
      // update ownerToNFTokenIdList
      ownerToNFTokenIdList[_from][currentCount] = 0;
      // update tokenToOwnerIndex
      tokenToOwnerIndex[_tokenId] = 0;
    } else {
      uint lastTokenId = ownerToNFTokenIdList[_from][currentCount];

      // Add
      // update ownerToNFTokenIdList
      ownerToNFTokenIdList[_from][currentIndex] = lastTokenId;
      // update tokenToOwnerIndex
      tokenToOwnerIndex[lastTokenId] = currentIndex;

      // Delete
      // update ownerToNFTokenIdList
      ownerToNFTokenIdList[_from][currentCount] = 0;
      // update tokenToOwnerIndex
      tokenToOwnerIndex[_tokenId] = 0;
    }
  }

  /// @dev Add a NFT to a given address
  ///      Throws if `_tokenId` is owned by someone.
  function _addTokenTo(address _to, uint _tokenId) internal {
    // assume always call on new tokenId or after _removeTokenFrom() call
    // Change the owner
    idToOwner[_tokenId] = _to;
    // Update owner token index tracking
    _addTokenToOwnerList(_to, _tokenId);
    // Change count tracking
    ownerToNFTokenCount[_to] += 1;
  }

  /// @dev Remove a NFT from a given address
  ///      Throws if `_from` is not the current owner.
  function _removeTokenFrom(address _from, uint _tokenId) internal {
    require(idToOwner[_tokenId] == _from, "!owner remove");
    // Change the owner
    idToOwner[_tokenId] = address(0);
    // Update owner token index tracking
    _removeTokenFromOwnerList(_from, _tokenId);
    // Change count tracking
    ownerToNFTokenCount[_from] -= 1;
  }

  /// @dev Execute transfer of a NFT.
  ///      Throws unless `msg.sender` is the current owner, an authorized operator, or the approved
  ///      address for this NFT. (NOTE: `msg.sender` not allowed in internal function so pass `_sender`.)
  ///      Throws if `_to` is the zero address.
  ///      Throws if `_from` is not the current owner.
  ///      Throws if `_tokenId` is not a valid NFT.
  function _transferFrom(
    address _from,
    address _to,
    uint _tokenId,
    address _sender
  ) internal {
    require(attachments[_tokenId] == 0 && !voted[_tokenId], "attached");
    require(_isApprovedOrOwner(_sender, _tokenId), "!owner sender");
    require(_to != address(0), "dst is zero");
    // from address will be checked in _removeTokenFrom()

    if (idToApprovals[_tokenId] != address(0)) {
      // Reset approvals
      idToApprovals[_tokenId] = address(0);
    }
    _removeTokenFrom(_from, _tokenId);
    _addTokenTo(_to, _tokenId);
    // Set the block of ownership transfer (for Flash NFT protection)
    ownershipChange[_tokenId] = block.number;
    // Log the transfer
    emit Transfer(_from, _to, _tokenId);
  }

  /* TRANSFER FUNCTIONS */
  /// @dev Throws unless `msg.sender` is the current owner, an authorized operator, or the approved address for this NFT.
  ///      Throws if `_from` is not the current owner.
  ///      Throws if `_to` is the zero address.
  ///      Throws if `_tokenId` is not a valid NFT.
  /// @notice The caller is responsible to confirm that `_to` is capable of receiving NFTs or else
  ///        they maybe be permanently lost.
  /// @param _from The current owner of the NFT.
  /// @param _to The new owner.
  /// @param _tokenId The NFT to transfer.
  function transferFrom(
    address _from,
    address _to,
    uint _tokenId
  ) external override {
    _transferFrom(_from, _to, _tokenId, msg.sender);
  }

  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.
    uint size;
    assembly {
      size := extcodesize(account)
    }
    return size > 0;
  }

  /// @dev Transfers the ownership of an NFT from one address to another address.
  ///      Throws unless `msg.sender` is the current owner, an authorized operator, or the
  ///      approved address for this NFT.
  ///      Throws if `_from` is not the current owner.
  ///      Throws if `_to` is the zero address.
  ///      Throws if `_tokenId` is not a valid NFT.
  ///      If `_to` is a smart contract, it calls `onERC721Received` on `_to` and throws if
  ///      the return value is not `bytes4(keccak256("onERC721Received(address,address,uint,bytes)"))`.
  /// @param _from The current owner of the NFT.
  /// @param _to The new owner.
  /// @param _tokenId The NFT to transfer.
  /// @param _data Additional data with no specified format, sent in call to `_to`.
  function safeTransferFrom(
    address _from,
    address _to,
    uint _tokenId,
    bytes memory _data
  ) public override {
    _transferFrom(_from, _to, _tokenId, msg.sender);

    if (_isContract(_to)) {
      // Throws if transfer destination is a contract which does not implement 'onERC721Received'
      try IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) returns (bytes4) {} catch (
        bytes memory reason
      ) {
        if (reason.length == 0) {
          revert('ERC721: transfer to non ERC721Receiver implementer');
        } else {
          assembly {
            revert(add(32, reason), mload(reason))
          }
        }
      }
    }
  }

  /// @dev Transfers the ownership of an NFT from one address to another address.
  ///      Throws unless `msg.sender` is the current owner, an authorized operator, or the
  ///      approved address for this NFT.
  ///      Throws if `_from` is not the current owner.
  ///      Throws if `_to` is the zero address.
  ///      Throws if `_tokenId` is not a valid NFT.
  ///      If `_to` is a smart contract, it calls `onERC721Received` on `_to` and throws if
  ///      the return value is not `bytes4(keccak256("onERC721Received(address,address,uint,bytes)"))`.
  /// @param _from The current owner of the NFT.
  /// @param _to The new owner.
  /// @param _tokenId The NFT to transfer.
  function safeTransferFrom(
    address _from,
    address _to,
    uint _tokenId
  ) external override {
    safeTransferFrom(_from, _to, _tokenId, '');
  }

  /// @dev Set or reaffirm the approved address for an NFT. The zero address indicates there is no approved address.
  ///      Throws unless `msg.sender` is the current NFT owner, or an authorized operator of the current owner.
  ///      Throws if `_tokenId` is not a valid NFT. (NOTE: This is not written the EIP)
  ///      Throws if `_approved` is the current owner. (NOTE: This is not written the EIP)
  /// @param _approved Address to be approved for the given NFT ID.
  /// @param _tokenId ID of the token to be approved.
  function approve(address _approved, uint _tokenId) public override {
    address owner = idToOwner[_tokenId];
    // Throws if `_tokenId` is not a valid NFT
    require(owner != address(0), "invalid id");
    // Throws if `_approved` is the current owner
    require(_approved != owner, "self approve");
    // Check requirements
    bool senderIsOwner = (idToOwner[_tokenId] == msg.sender);
    bool senderIsApprovedForAll = (ownerToOperators[owner])[msg.sender];
    require(senderIsOwner || senderIsApprovedForAll, "!owner");
    // Set the approval
    idToApprovals[_tokenId] = _approved;
    emit Approval(owner, _approved, _tokenId);
  }

  /// @dev Enables or disables approval for a third party ("operator") to manage all of
  ///      `msg.sender`'s assets. It also emits the ApprovalForAll event.
  ///      Throws if `_operator` is the `msg.sender`. (NOTE: This is not written the EIP)
  /// @notice This works even if sender doesn't own any tokens at the time.
  /// @param _operator Address to add to the set of authorized operators.
  /// @param _approved True if the operators is approved, false to revoke approval.
  function setApprovalForAll(address _operator, bool _approved) external override {
    // Throws if `_operator` is the `msg.sender`
    require(_operator != msg.sender, "operator is sender");
    ownerToOperators[msg.sender][_operator] = _approved;
    emit ApprovalForAll(msg.sender, _operator, _approved);
  }

  /// @dev Function to mint tokens
  ///      Throws if `_to` is zero address.
  ///      Throws if `_tokenId` is owned by someone.
  /// @param _to The address that will receive the minted tokens.
  /// @param _tokenId The token id to mint.
  /// @return A boolean that indicates if the operation was successful.
  function _mint(address _to, uint _tokenId) internal returns (bool) {
    // Throws if `_to` is zero address
    require(_to != address(0), "zero dst");
    // Add NFT. Throws if `_tokenId` is owned by someone
    _addTokenTo(_to, _tokenId);
    emit Transfer(address(0), _to, _tokenId);
    return true;
  }

  /// @notice Record global and per-user data to checkpoint
  /// @param _tokenId NFT token ID. No user checkpoint if 0
  /// @param oldLocked Pevious locked amount / end lock time for the user
  /// @param newLocked New locked amount / end lock time for the user
  function _checkpoint(
    uint _tokenId,
    LockedBalance memory oldLocked,
    LockedBalance memory newLocked
  ) internal {
    Point memory uOld;
    Point memory uNew;
    int128 oldDSlope = 0;
    int128 newDSlope = 0;
    uint _epoch = epoch;

    if (_tokenId != 0) {
      // Calculate slopes and biases
      // Kept at zero when they have to
      if (oldLocked.end > block.timestamp && oldLocked.amount > 0) {
        uOld.slope = oldLocked.amount / I_MAX_TIME;
        uOld.bias = uOld.slope * int128(int256(oldLocked.end - block.timestamp));
      }
      if (newLocked.end > block.timestamp && newLocked.amount > 0) {
        uNew.slope = newLocked.amount / I_MAX_TIME;
        uNew.bias = uNew.slope * int128(int256(newLocked.end - block.timestamp));
      }

      // 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
      oldDSlope = slopeChanges[oldLocked.end];
      if (newLocked.end != 0) {
        if (newLocked.end == oldLocked.end) {
          newDSlope = oldDSlope;
        } else {
          newDSlope = slopeChanges[newLocked.end];
        }
      }
    }

    Point memory lastPoint = Point({bias : 0, slope : 0, ts : block.timestamp, blk : block.number});
    if (_epoch > 0) {
      lastPoint = _pointHistory[_epoch];
    }
    uint 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 = lastPoint;
    uint blockSlope = 0;
    // dblock/dt
    if (block.timestamp > lastPoint.ts) {
      blockSlope = (MULTIPLIER * (block.number - lastPoint.blk)) / (block.timestamp - 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
    {
      uint ti = (lastCheckpoint / WEEK) * WEEK;
      // 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
      for (uint i = 0; i < 255; ++i) {
        ti += WEEK;
        int128 dSlope = 0;
        if (ti > block.timestamp) {
          ti = block.timestamp;
        } else {
          dSlope = slopeChanges[ti];
        }
        lastPoint.bias = Math.positiveInt128(lastPoint.bias - lastPoint.slope * int128(int256(ti - lastCheckpoint)));
        lastPoint.slope = Math.positiveInt128(lastPoint.slope + dSlope);
        lastCheckpoint = ti;
        lastPoint.ts = ti;
        lastPoint.blk = initialLastPoint.blk + (blockSlope * (ti - initialLastPoint.ts)) / MULTIPLIER;
        _epoch += 1;
        if (ti == block.timestamp) {
          lastPoint.blk = block.number;
          break;
        } else {
          _pointHistory[_epoch] = lastPoint;
        }
      }
    }

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

    if (_tokenId != 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 = Math.positiveInt128(lastPoint.slope + (uNew.slope - uOld.slope));
      lastPoint.bias = Math.positiveInt128(lastPoint.bias + (uNew.bias - uOld.bias));
    }

    // Record the changed point into history
    _pointHistory[_epoch] = lastPoint;

    if (_tokenId != 0) {
      // Schedule the slope changes (slope is going down)
      // We subtract newUserSlope from [newLocked.end]
      // and add old_user_slope to [old_locked.end]
      if (oldLocked.end > block.timestamp) {
        // old_dslope was <something> - u_old.slope, so we cancel that
        oldDSlope += uOld.slope;
        if (newLocked.end == oldLocked.end) {
          oldDSlope -= uNew.slope;
          // It was a new deposit, not extension
        }
        slopeChanges[oldLocked.end] = oldDSlope;
      }

      if (newLocked.end > block.timestamp) {
        if (newLocked.end > oldLocked.end) {
          newDSlope -= uNew.slope;
          // old slope disappeared at this point
          slopeChanges[newLocked.end] = newDSlope;
        }
        // else: we recorded it already in oldDSlope
      }
      // Now handle user history
      uint userEpoch = userPointEpoch[_tokenId] + 1;

      userPointEpoch[_tokenId] = userEpoch;
      uNew.ts = block.timestamp;
      uNew.blk = block.number;
      _userPointHistory[_tokenId][userEpoch] = uNew;
    }
  }

  /// @notice Deposit and lock tokens for a user
  /// @param _tokenId NFT that holds lock
  /// @param _value Amount to deposit
  /// @param unlockTime New time when to unlock the tokens, or 0 if unchanged
  /// @param lockedBalance Previous locked amount / timestamp
  /// @param depositType The type of deposit
  function _depositFor(
    uint _tokenId,
    uint _value,
    uint unlockTime,
    LockedBalance memory lockedBalance,
    DepositType depositType
  ) internal {
    LockedBalance memory _locked = lockedBalance;
    uint supplyBefore = supply;

    supply = supplyBefore + _value;
    LockedBalance memory oldLocked;
    (oldLocked.amount, oldLocked.end) = (_locked.amount, _locked.end);
    // Adding to existing lock, or if a lock is expired - creating a new one
    _locked.amount += int128(int256(_value));
    if (unlockTime != 0) {
      _locked.end = unlockTime;
    }
    locked[_tokenId] = _locked;

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

    address from = msg.sender;
    if (_value != 0 && depositType != DepositType.MERGE_TYPE) {
      IERC20(token).safeTransferFrom(from, address(this), _value);
    }

    emit Deposit(from, _tokenId, _value, _locked.end, depositType, block.timestamp);
    emit Supply(supplyBefore, supplyBefore + _value);
  }

  function voting(uint _tokenId) external override {
    require(msg.sender == _voter(), "!voter");
    voted[_tokenId] = true;
  }

  function abstain(uint _tokenId) external override {
    require(msg.sender == _voter(), "!voter");
    voted[_tokenId] = false;
  }

  function attachToken(uint _tokenId) external override {
    require(msg.sender == _voter(), "!voter");
    attachments[_tokenId] = attachments[_tokenId] + 1;
  }

  function detachToken(uint _tokenId) external override {
    require(msg.sender == _voter(), "!voter");
    attachments[_tokenId] = attachments[_tokenId] - 1;
  }

  function merge(uint _from, uint _to) external {
    require(attachments[_from] == 0 && !voted[_from], "attached");
    require(_from != _to, "the same");
    require(_isApprovedOrOwner(msg.sender, _from), "!owner from");
    require(_isApprovedOrOwner(msg.sender, _to), "!owner to");

    LockedBalance memory _locked0 = locked[_from];
    LockedBalance memory _locked1 = locked[_to];
    uint value0 = uint(int256(_locked0.amount));
    uint end = _locked0.end >= _locked1.end ? _locked0.end : _locked1.end;

    locked[_from] = LockedBalance(0, 0);
    _checkpoint(_from, _locked0, LockedBalance(0, 0));
    _burn(_from);
    _depositFor(_to, value0, end, _locked1, DepositType.MERGE_TYPE);
  }

  function block_number() external view returns (uint) {
    return block.number;
  }

  /// @notice Record global data to checkpoint
  function checkpoint() external override {
    _checkpoint(0, LockedBalance(0, 0), LockedBalance(0, 0));
  }

  /// @notice Deposit `_value` tokens for `_tokenId` and add to the lock
  /// @dev Anyone (even a smart contract) can deposit for someone else, but
  ///      cannot extend their locktime and deposit for a brand new user
  /// @param _tokenId lock NFT
  /// @param _value Amount to add to user's lock
  function depositFor(uint _tokenId, uint _value) external lock override {
    require(_value > 0, "zero value");
    LockedBalance memory _locked = locked[_tokenId];
    require(_locked.amount > 0, 'No existing lock found');
    require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw');
    _depositFor(_tokenId, _value, 0, _locked, DepositType.DEPOSIT_FOR_TYPE);
  }

  /// @notice Deposit `_value` tokens for `_to` and lock for `_lock_duration`
  /// @param _value Amount to deposit
  /// @param _lockDuration Number of seconds to lock tokens for (rounded down to nearest week)
  /// @param _to Address to deposit
  function _createLock(uint _value, uint _lockDuration, address _to) internal returns (uint) {
    require(_value > 0, "zero value");
    // Lock time is rounded down to weeks
    uint unlockTime = (block.timestamp + _lockDuration) / WEEK * WEEK;
    require(unlockTime > block.timestamp, 'Can only lock until time in the future');
    require(unlockTime <= block.timestamp + MAX_TIME, 'Voting lock can be 4 years max');

    ++tokenId;
    uint _tokenId = tokenId;
    _mint(_to, _tokenId);

    _depositFor(_tokenId, _value, unlockTime, locked[_tokenId], DepositType.CREATE_LOCK_TYPE);
    return _tokenId;
  }

  /// @notice Deposit `_value` tokens for `_to` and lock for `_lock_duration`
  /// @param _value Amount to deposit
  /// @param _lockDuration Number of seconds to lock tokens for (rounded down to nearest week)
  /// @param _to Address to deposit
  function createLockFor(uint _value, uint _lockDuration, address _to)
  external lock override returns (uint) {
    return _createLock(_value, _lockDuration, _to);
  }

  /// @notice Deposit `_value` tokens for `msg.sender` and lock for `_lock_duration`
  /// @param _value Amount to deposit
  /// @param _lockDuration Number of seconds to lock tokens for (rounded down to nearest week)
  function createLock(uint _value, uint _lockDuration) external lock returns (uint) {
    return _createLock(_value, _lockDuration, msg.sender);
  }

  /// @notice Deposit `_value` additional tokens for `_tokenId` without modifying the unlock time
  /// @param _value Amount of tokens to deposit and add to the lock
  function increaseAmount(uint _tokenId, uint _value) external lock {
    LockedBalance memory _locked = locked[_tokenId];
    require(_locked.amount > 0, 'No existing lock found');
    require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw');
    require(_isApprovedOrOwner(msg.sender, _tokenId), "!owner");
    require(_value > 0, "zero value");

    _depositFor(_tokenId, _value, 0, _locked, DepositType.INCREASE_LOCK_AMOUNT);
  }

  /// @notice Extend the unlock time for `_tokenId`
  /// @param _lockDuration New number of seconds until tokens unlock
  function increaseUnlockTime(uint _tokenId, uint _lockDuration) external lock {
    LockedBalance memory _locked = locked[_tokenId];
    // Lock time is rounded down to weeks
    uint unlockTime = (block.timestamp + _lockDuration) / WEEK * WEEK;
    require(_locked.amount > 0, 'Nothing is locked');
    require(_locked.end > block.timestamp, 'Lock expired');
    require(unlockTime > _locked.end, 'Can only increase lock duration');
    require(unlockTime <= block.timestamp + MAX_TIME, 'Voting lock can be 4 years max');
    require(_isApprovedOrOwner(msg.sender, _tokenId), "!owner");

    _depositFor(_tokenId, 0, unlockTime, _locked, DepositType.INCREASE_UNLOCK_TIME);
  }

  /// @notice Withdraw all tokens for `_tokenId`
  /// @dev Only possible if the lock has expired
  function withdraw(uint _tokenId) external lock {
    require(_isApprovedOrOwner(msg.sender, _tokenId), "!owner");
    require(attachments[_tokenId] == 0 && !voted[_tokenId], "attached");
    LockedBalance memory _locked = locked[_tokenId];
    require(block.timestamp >= _locked.end, "The lock did not expire");

    uint value = uint(int256(_locked.amount));
    locked[_tokenId] = LockedBalance(0, 0);
    uint supplyBefore = supply;
    supply = supplyBefore - value;

    // old_locked can have either expired <= timestamp or zero end
    // _locked has only 0 end
    // Both can have >= 0 amount
    _checkpoint(_tokenId, _locked, LockedBalance(0, 0));

    IERC20(token).safeTransfer(msg.sender, value);

    // Burn the NFT
    _burn(_tokenId);

    emit Withdraw(msg.sender, _tokenId, value, block.timestamp);
    emit Supply(supplyBefore, supplyBefore - value);
  }

  // The following ERC20/minime-compatible methods are not real balanceOf and supply!
  // They measure the weights for the purpose of voting, so they don't represent
  // real coins.

  /// @notice Binary search to estimate timestamp for block number
  /// @param _block Block to find
  /// @param maxEpoch Don't go beyond this epoch
  /// @return Approximate timestamp for block
  function _findBlockEpoch(uint _block, uint maxEpoch) internal view returns (uint) {
    // Binary search
    uint _min = 0;
    uint _max = maxEpoch;
    for (uint i = 0; i < 128; ++i) {
      // Will be always enough for 128-bit numbers
      if (_min >= _max) {
        break;
      }
      uint _mid = (_min + _max + 1) / 2;
      if (_pointHistory[_mid].blk <= _block) {
        _min = _mid;
      } else {
        _max = _mid - 1;
      }
    }
    return _min;
  }

  /// @notice Get the current voting power for `_tokenId`
  /// @dev Adheres to the ERC20 `balanceOf` interface for Aragon compatibility
  /// @param _tokenId NFT for lock
  /// @param _t Epoch time to return voting power at
  /// @return User voting power
  function _balanceOfNFT(uint _tokenId, uint _t) internal view returns (uint) {
    uint _epoch = userPointEpoch[_tokenId];
    if (_epoch == 0) {
      return 0;
    } else {
      Point memory lastPoint = _userPointHistory[_tokenId][_epoch];
      lastPoint.bias -= lastPoint.slope * int128(int256(_t) - int256(lastPoint.ts));
      if (lastPoint.bias < 0) {
        lastPoint.bias = 0;
      }
      return uint(int256(lastPoint.bias));
    }
  }

  /// @dev Returns current token URI metadata
  /// @param _tokenId Token ID to fetch URI for.
  function tokenURI(uint _tokenId) external view override returns (string memory) {
    require(idToOwner[_tokenId] != address(0), "Query for nonexistent token");
    LockedBalance memory _locked = locked[_tokenId];
    return
    _tokenURI(
      _tokenId,
      _balanceOfNFT(_tokenId, block.timestamp),
      _locked.end,
      uint(int256(_locked.amount))
    );
  }

  function balanceOfNFT(uint _tokenId) external view override returns (uint) {
    // flash NFT protection
    if (ownershipChange[_tokenId] == block.number) {
      return 0;
    }
    return _balanceOfNFT(_tokenId, block.timestamp);
  }

  function balanceOfNFTAt(uint _tokenId, uint _t) external view returns (uint) {
    return _balanceOfNFT(_tokenId, _t);
  }

  /// @notice Measure voting power of `_tokenId` at block height `_block`
  /// @dev Adheres to MiniMe `balanceOfAt` interface: https://github.com/Giveth/minime
  /// @param _tokenId User's wallet NFT
  /// @param _block Block to calculate the voting power at
  /// @return Voting power
  function _balanceOfAtNFT(uint _tokenId, uint _block) internal view returns (uint) {
    // Copying and pasting totalSupply code because Vyper cannot pass by
    // reference yet
    require(_block <= block.number, "only old block");

    // Binary search
    uint _min = 0;
    uint _max = userPointEpoch[_tokenId];
    for (uint i = 0; i < 128; ++i) {
      // Will be always enough for 128-bit numbers
      if (_min >= _max) {
        break;
      }
      uint _mid = (_min + _max + 1) / 2;
      if (_userPointHistory[_tokenId][_mid].blk <= _block) {
        _min = _mid;
      } else {
        _max = _mid - 1;
      }
    }

    Point memory uPoint = _userPointHistory[_tokenId][_min];

    uint maxEpoch = epoch;
    uint _epoch = _findBlockEpoch(_block, maxEpoch);
    Point memory point0 = _pointHistory[_epoch];
    uint dBlock = 0;
    uint dt = 0;
    if (_epoch < maxEpoch) {
      Point memory point1 = _pointHistory[_epoch + 1];
      dBlock = point1.blk - point0.blk;
      dt = point1.ts - point0.ts;
    } else {
      dBlock = block.number - point0.blk;
      dt = block.timestamp - point0.ts;
    }
    uint blockTime = point0.ts;
    if (dBlock != 0 && _block > point0.blk) {
      blockTime += (dt * (_block - point0.blk)) / dBlock;
    }

    uPoint.bias -= uPoint.slope * int128(int256(blockTime - uPoint.ts));
    return uint(uint128(Math.positiveInt128(uPoint.bias)));
  }

  function balanceOfAtNFT(uint _tokenId, uint _block) external view returns (uint) {
    return _balanceOfAtNFT(_tokenId, _block);
  }

  /// @notice Calculate total voting power at some point in the past
  /// @param point The point (bias/slope) to start search from
  /// @param t Time to calculate the total voting power at
  /// @return Total voting power at that time
  function _supplyAt(Point memory point, uint t) internal view returns (uint) {
    Point memory lastPoint = point;
    uint ti = (lastPoint.ts / WEEK) * WEEK;
    for (uint i = 0; i < 255; ++i) {
      ti += WEEK;
      int128 dSlope = 0;
      if (ti > t) {
        ti = t;
      } else {
        dSlope = slopeChanges[ti];
      }
      lastPoint.bias -= lastPoint.slope * int128(int256(ti - lastPoint.ts));
      if (ti == t) {
        break;
      }
      lastPoint.slope += dSlope;
      lastPoint.ts = ti;
    }
    return uint(uint128(Math.positiveInt128(lastPoint.bias)));
  }

  /// @notice Calculate total voting power
  /// @dev Adheres to the ERC20 `totalSupply` interface for Aragon compatibility
  /// @return Total voting power
  function totalSupplyAtT(uint t) public view returns (uint) {
    uint _epoch = epoch;
    Point memory lastPoint = _pointHistory[_epoch];
    return _supplyAt(lastPoint, t);
  }

  function totalSupply() external view returns (uint) {
    return totalSupplyAtT(block.timestamp);
  }

  /// @notice Calculate total voting power at some point in the past
  /// @param _block Block to calculate the total voting power at
  /// @return Total voting power at `_block`
  function totalSupplyAt(uint _block) external view returns (uint) {
    require(_block <= block.number, "only old blocks");
    uint _epoch = epoch;
    uint targetEpoch = _findBlockEpoch(_block, _epoch);

    Point memory point = _pointHistory[targetEpoch];
    // it is possible only for a block before the launch
    // return 0 as more clear answer than revert
    if (point.blk > _block) {
      return 0;
    }
    uint dt = 0;
    if (targetEpoch < _epoch) {
      Point memory point_next = _pointHistory[targetEpoch + 1];
      // next point block can not be the same or lower
      dt = ((_block - point.blk) * (point_next.ts - point.ts)) / (point_next.blk - point.blk);
    } else {
      if (point.blk != block.number) {
        dt = ((_block - point.blk) * (block.timestamp - point.ts)) / (block.number - point.blk);
      }
    }
    // Now dt contains info on how far are we beyond point
    return _supplyAt(point, point.ts + dt);
  }

  function _tokenURI(uint _tokenId, uint _balanceOf, uint _locked_end, uint _value) internal pure returns (string memory output) {
    output = '<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 350 350"><style>.base { fill: black; font-family: Impact; font-size: 50px; }</style><rect width="100%" height="100%" fill="#aaaaff" /><text x="10" y="60" class="base">';
    output = string(abi.encodePacked(output, "token ", _toString(_tokenId), '</text><text x="10" y="150" class="base">'));
    output = string(abi.encodePacked(output, "balanceOf ", _toString(_balanceOf), '</text><text x="10" y="230" class="base">'));
    output = string(abi.encodePacked(output, "locked_end ", _toString(_locked_end), '</text><text x="10" y="310" class="base">'));
    output = string(abi.encodePacked(output, "value ", _toString(_value), '</text></svg>'));

    string memory json = Base64.encode(bytes(string(abi.encodePacked('{"name": "lock #', _toString(_tokenId), '", "description": "Dystopia locks, can be used to boost gauge yields, vote on token emission, and receive bribes", "image": "data:image/svg+xml;base64,', Base64.encode(bytes(output)), '"}'))));
    output = string(abi.encodePacked('data:application/json;base64,', json));
  }

  function _toString(uint value) internal pure returns (string memory) {
    // Inspired by OraclizeAPI's implementation - MIT license
    // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

    if (value == 0) {
      return "0";
    }
    uint temp = value;
    uint digits;
    while (temp != 0) {
      digits++;
      temp /= 10;
    }
    bytes memory buffer = new bytes(digits);
    while (value != 0) {
      digits -= 1;
      buffer[digits] = bytes1(uint8(48 + uint(value % 10)));
      value /= 10;
    }
    return string(buffer);
  }

  function _burn(uint _tokenId) internal {
    address owner = ownerOf(_tokenId);
    // Clear approval
    approve(address(0), _tokenId);
    // Remove token
    _removeTokenFrom(msg.sender, _tokenId);
    emit Transfer(owner, address(0), _tokenId);
  }

  function userPointHistory(uint _tokenId, uint _loc) external view override returns (Point memory) {
    return _userPointHistory[_tokenId][_loc];
  }

  function pointHistory(uint _loc) external view override returns (Point memory) {
    return _pointHistory[_loc];
  }
}

File 2 of 13 : Base64.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

/// @title Base64
/// @notice Provides a function for encoding some bytes in base64
/// @author Brecht Devos <[email protected]>
library Base64 {
  bytes internal constant TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

  /// @notice Encodes some bytes to the base64 representation
  function encode(bytes memory data) internal pure returns (string memory) {
    uint len = data.length;
    if (len == 0) return "";

    // multiply by 4/3 rounded up
    uint encodedLen = 4 * ((len + 2) / 3);

    // Add some extra buffer at the end
    bytes memory result = new bytes(encodedLen + 32);

    bytes memory table = TABLE;

    assembly {
      let tablePtr := add(table, 1)
      let resultPtr := add(result, 32)

      for {
        let i := 0
      } lt(i, len) {

      } {
        i := add(i, 3)
        let input := and(mload(add(data, i)), 0xffffff)

        let out := mload(add(tablePtr, and(shr(18, input), 0x3F)))
        out := shl(8, out)
        out := add(out, and(mload(add(tablePtr, and(shr(12, input), 0x3F))), 0xFF))
        out := shl(8, out)
        out := add(out, and(mload(add(tablePtr, and(shr(6, input), 0x3F))), 0xFF))
        out := shl(8, out)
        out := add(out, and(mload(add(tablePtr, and(input, 0x3F))), 0xFF))
        out := shl(224, out)

        mstore(resultPtr, out)

        resultPtr := add(resultPtr, 4)
      }

      switch mod(len, 3)
      case 1 {
        mstore(sub(resultPtr, 2), shl(240, 0x3d3d))
      }
      case 2 {
        mstore(sub(resultPtr, 1), shl(248, 0x3d))
      }

      mstore(result, encodedLen)
    }

    return string(result);
  }
}

File 3 of 13 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

/**
 * @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 4 of 13 : IERC721.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

import "./IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
  /**
   * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
   */
  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

  /**
   * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
   */
  event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

  /**
   * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
   */
  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

  /**
   * @dev Returns the number of tokens in ``owner``'s account.
   */
  function balanceOf(address owner) external view returns (uint256 balance);

  /**
   * @dev Returns the owner of the `tokenId` token.
   *
   * Requirements:
   *
   * - `tokenId` must exist.
   */
  function ownerOf(uint256 tokenId) external view returns (address owner);

  /**
   * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
   * are aware of the ERC721 protocol to prevent tokens from being forever locked.
   *
   * Requirements:
   *
   * - `from` cannot be the zero address.
   * - `to` cannot be the zero address.
   * - `tokenId` token must exist and be owned by `from`.
   * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
   *
   * Emits a {Transfer} event.
   */
  function safeTransferFrom(
    address from,
    address to,
    uint256 tokenId
  ) external;

  /**
   * @dev Transfers `tokenId` token from `from` to `to`.
   *
   * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
   *
   * Requirements:
   *
   * - `from` cannot be the zero address.
   * - `to` cannot be the zero address.
   * - `tokenId` token must be owned by `from`.
   * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
   *
   * Emits a {Transfer} event.
   */
  function transferFrom(
    address from,
    address to,
    uint256 tokenId
  ) external;

  /**
   * @dev Gives permission to `to` to transfer `tokenId` token to another account.
   * The approval is cleared when the token is transferred.
   *
   * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
   *
   * Requirements:
   *
   * - The caller must own the token or be an approved operator.
   * - `tokenId` must exist.
   *
   * Emits an {Approval} event.
   */
  function approve(address to, uint256 tokenId) external;

  /**
   * @dev Returns the account approved for `tokenId` token.
   *
   * Requirements:
   *
   * - `tokenId` must exist.
   */
  function getApproved(uint256 tokenId) external view returns (address operator);

  /**
   * @dev Approve or remove `operator` as an operator for the caller.
   * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
   *
   * Requirements:
   *
   * - The `operator` cannot be the caller.
   *
   * Emits an {ApprovalForAll} event.
   */
  function setApprovalForAll(address operator, bool _approved) external;

  /**
   * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
   *
   * See {setApprovalForAll}
   */
  function isApprovedForAll(address owner, address operator) external view returns (bool);

  /**
   * @dev Safely transfers `tokenId` token from `from` to `to`.
   *
   * Requirements:
   *
   * - `from` cannot be the zero address.
   * - `to` cannot be the zero address.
   * - `tokenId` token must exist and be owned by `from`.
   * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
   *
   * Emits a {Transfer} event.
   */
  function safeTransferFrom(
    address from,
    address to,
    uint256 tokenId,
    bytes calldata data
  ) external;
}

File 5 of 13 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

import "./IERC721.sol";

/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Metadata is IERC721 {
  /**
  * @dev Returns the token collection name.
  */
  function name() external view returns (string memory);

  /**
  * @dev Returns the token collection symbol.
  */
  function symbol() external view returns (string memory);

  /**
  * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
  */
  function tokenURI(uint tokenId) external view returns (string memory);
}

File 6 of 13 : IVe.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

interface IVe {

  enum DepositType {
    DEPOSIT_FOR_TYPE,
    CREATE_LOCK_TYPE,
    INCREASE_LOCK_AMOUNT,
    INCREASE_UNLOCK_TIME,
    MERGE_TYPE
  }

  struct Point {
    int128 bias;
    int128 slope; // # -dweight / dt
    uint ts;
    uint blk; // block
  }
  /* We cannot really do block numbers per se b/c slope is per time, not per block
  * and per block could be fairly bad b/c Ethereum changes blocktimes.
  * What we can do is to extrapolate ***At functions */

  struct LockedBalance {
    int128 amount;
    uint end;
  }

  function token() external view returns (address);

  function balanceOfNFT(uint) external view returns (uint);

  function isApprovedOrOwner(address, uint) external view returns (bool);

  function createLockFor(uint, uint, address) external returns (uint);

  function userPointEpoch(uint tokenId) external view returns (uint);

  function epoch() external view returns (uint);

  function userPointHistory(uint tokenId, uint loc) external view returns (Point memory);

  function pointHistory(uint loc) external view returns (Point memory);

  function checkpoint() external;

  function depositFor(uint tokenId, uint value) external;

  function attachToken(uint tokenId) external;

  function detachToken(uint tokenId) external;

  function voting(uint tokenId) external;

  function abstain(uint tokenId) external;
}

File 7 of 13 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
  /**
   * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
   * by `operator` from `from`, this function is called.
   *
   * It must return its Solidity selector to confirm the token transfer.
   * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
   *
   * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
   */
  function onERC721Received(
    address operator,
    address from,
    uint256 tokenId,
    bytes calldata data
  ) external returns (bytes4);
}

File 8 of 13 : IController.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

interface IController {

  function veDist() external view returns (address);

  function voter() external view returns (address);

}

File 9 of 13 : Reentrancy.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

abstract contract Reentrancy {

  /// @dev simple re-entrancy check
  uint internal _unlocked = 1;

  modifier lock() {
    require(_unlocked == 1, "Reentrant call");
    _unlocked = 2;
    _;
    _unlocked = 1;
  }

}

File 10 of 13 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.13;

import "../interface/IERC20.sol";
import "./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 Address for address;

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

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

  function safeIncreaseAllowance(
    IERC20 token,
    address spender,
    uint value
  ) internal {
    uint newAllowance = token.allowance(address(this), spender) + value;
    _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
      require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }
  }

}

File 11 of 13 : Math.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

library Math {

  function max(uint a, uint b) internal pure returns (uint) {
    return a >= b ? a : b;
  }

  function min(uint a, uint b) internal pure returns (uint) {
    return a < b ? a : b;
  }

  function positiveInt128(int128 value) internal pure returns (int128) {
    return value < 0 ? int128(0) : value;
  }

  function closeTo(uint a, uint b, uint target) internal pure returns (bool) {
    if (a > b) {
      if (a - b <= target) {
        return true;
      }
    } else {
      if (b - a <= target) {
        return true;
      }
    }
    return false;
  }

  function sqrt(uint y) internal pure returns (uint z) {
    if (y > 3) {
      z = y;
      uint x = y / 2 + 1;
      while (x < z) {
        z = x;
        x = (y / x + x) / 2;
      }
    } else if (y != 0) {
      z = 1;
    }
  }

}

File 12 of 13 : IERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
  /**
   * @dev Returns true if this contract implements the interface defined by
   * `interfaceId`. See the corresponding
   * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
   * to learn more about how these ids are created.
   *
   * This function call must use less than 30 000 gas.
   */
  function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 13 of 13 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.13;

/**
 * @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
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
  function isContract(address account) internal view returns (bool) {
    // This method relies on extcodesize/address.code.length, which returns 0
    // for contracts in construction, since the code is only stored at the end
    // of the constructor execution.

    return account.code.length > 0;
  }

  function functionCall(
    address target,
    bytes memory data,
    string memory errorMessage
  ) internal returns (bytes memory) {
    require(isContract(target), "Address: call to non-contract");
    (bool success, bytes memory returndata) = target.call(data);
    return verifyCallResult(success, returndata, errorMessage);
  }

  /**
   * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
  function verifyCallResult(
    bool success,
    bytes memory returndata,
    string memory errorMessage
  ) internal 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
        assembly {
          let returndata_size := mload(returndata)
          revert(add(32, returndata), returndata_size)
        }
      } else {
        revert(errorMessage);
      }
    }
  }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"address","name":"controller_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"locktime","type":"uint256"},{"indexed":false,"internalType":"enum IVe.DepositType","name":"depositType","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"prevSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"supply","type":"uint256"}],"name":"Supply","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"abstain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_approved","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"attachToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"attachments","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":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_block","type":"uint256"}],"name":"balanceOfAtNFT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"balanceOfNFT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_t","type":"uint256"}],"name":"balanceOfNFTAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"block_number","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"controller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_lockDuration","type":"uint256"}],"name":"createLock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_lockDuration","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"createLockFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"depositFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"detachToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getLastUserSlope","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"increaseAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_lockDuration","type":"uint256"}],"name":"increaseUnlockTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"isApprovedOrOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"locked","outputs":[{"internalType":"int128","name":"amount","type":"int128"},{"internalType":"uint256","name":"end","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"lockedEnd","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_from","type":"uint256"},{"internalType":"uint256","name":"_to","type":"uint256"}],"name":"merge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"ownershipChange","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loc","type":"uint256"}],"name":"pointHistory","outputs":[{"components":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"uint256","name":"ts","type":"uint256"},{"internalType":"uint256","name":"blk","type":"uint256"}],"internalType":"struct IVe.Point","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setApprovalForAll","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":"supply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_tokenIndex","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","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":"_block","type":"uint256"}],"name":"totalSupplyAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"t","type":"uint256"}],"name":"totalSupplyAtT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"userPointEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_loc","type":"uint256"}],"name":"userPointHistory","outputs":[{"components":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"uint256","name":"ts","type":"uint256"},{"internalType":"uint256","name":"blk","type":"uint256"}],"internalType":"struct IVe.Point","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"userPointHistoryTs","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"voted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"voting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a060405260016000553480156200001657600080fd5b50604051620042a3380380620042a383398101604081905262000039916200019d565b6001600160a01b03828116608052600b80546001600160a01b031916918316919091179055437f05b8ccbb9d4d8fb16ea74ce3c29a41f1b461fbdaff4714a0d9a8eb05499746be55427f05b8ccbb9d4d8fb16ea74ce3c29a41f1b461fbdaff4714a0d9a8eb05499746bd5560136020527f317681100331673b27c2499894e78912f138ae85f2dd790e454ff96a89cf2d77805460ff1990811660019081179092557f5829b1f18259a9cdcd4268c3b072804d2f8e3dedbb1b883db1fa2055bb26d84f8054821683179055635b5e139f60e01b60009081527f65cb089fa41783e5af12e88573d882f263c09532a373334f0b809825d5a904ab80549092169092179055600c546040519091309160008051602062004283833981519152908290a4600c54604051600090309060008051602062004283833981519152908390a45050620001d5565b80516001600160a01b03811681146200019857600080fd5b919050565b60008060408385031215620001b157600080fd5b620001bc8362000180565b9150620001cc6020840162000180565b90509250929050565b608051614084620001ff6000396000818161083701528181610caf0152611bc801526140846000f3fe608060405234801561001057600080fd5b50600436106102f15760003560e01c80638ad4c4471161019d578063c1f0fb9f116100e9578063e7e242d4116100a2578063f52a36f71161007c578063f52a36f7146107fc578063f77c47911461081f578063fc0c546a14610832578063fd4a77f11461085957600080fd5b8063e7e242d41461079a578063e985e9c5146107ad578063ec32e6df146107e957600080fd5b8063c1f0fb9f14610726578063c2c4c5c114610739578063c87b56dd14610741578063d1c2babb14610754578063e0514aba14610767578063e58f59471461077a57600080fd5b806398782b4e11610156578063b2383e5511610130578063b2383e55146106a5578063b45a3c0e146106b8578063b52c05fe14610700578063b88d4fde1461071357600080fd5b806398782b4e1461066c5780639d507b8b1461067f578063a22cb4651461069257600080fd5b80638ad4c447146106075780638c2c9baf1461061a5780638fbb38ff1461062d578063900cf0cf1461065057806395d89b4114610362578063981b24d01461065957600080fd5b80632e1a7d4d1161025c57806346b3bd0011610215578063626944df116101ef578063626944df146105955780636352211e146105b857806370a08231146105e15780637116c60c146105f457600080fd5b806346b3bd001461053857806354fd4d501461054b5780635633e0a61461056f57600080fd5b80632e1a7d4d1461045e5780632f745c5914610471578063313ce567146104a757806342842e0e146104c1578063430c2081146104d457806344acb42a146104e757600080fd5b80630d6a2033116102ae5780630d6a2033146103ea5780630ec84dda1461040a57806318160ddd1461041d57806323857d511461042557806323b872dd1461044557806325a58b561461045857600080fd5b806301ffc9a7146102f6578063047fc9aa1461033857806305ae4f8c1461034f57806306fdde0314610362578063081812fc14610394578063095ea7b3146103d5575b600080fd5b61032361030436600461345e565b6001600160e01b03191660009081526013602052604090205460ff1690565b60405190151581526020015b60405180910390f35b61034160015481565b60405190815260200161032f565b61034161035d36600461347b565b61086c565b610387604051806040016040528060068152602001651d99511654d560d21b81525081565b60405161032f91906134f5565b6103bd6103a2366004613508565b6000908152600e60205260409020546001600160a01b031690565b6040516001600160a01b03909116815260200161032f565b6103e86103e3366004613536565b61089f565b005b6103416103f8366004613508565b60096020526000908152604090205481565b6103e861041836600461347b565b610a01565b610341610af6565b610341610433366004613508565b60036020526000908152604090205481565b6103e8610453366004613562565b610b06565b43610341565b6103e861046c366004613508565b610b17565b61034161047f366004613536565b6001600160a01b03919091166000908152601060209081526040808320938352929052205490565b6104af601281565b60405160ff909116815260200161032f565b6103e86104cf366004613562565b610d70565b6103236104e2366004613536565b610d8b565b6104fa6104f536600461347b565b610d9e565b60405161032f919060006080820190508251600f0b82526020830151600f0b6020830152604083015160408301526060830151606083015292915050565b6103e8610546366004613508565b610e12565b610387604051806040016040528060058152602001640312e302e360dc1b81525081565b61058261057d366004613508565b610e76565b604051600f9190910b815260200161032f565b6103416105a3366004613508565b60009081526002602052604090206001015490565b6103bd6105c6366004613508565b6000908152600d60205260409020546001600160a01b031690565b6103416105ef3660046135a3565b610eb9565b610341610602366004613508565b610ed7565b6104fa610615366004613508565b610f37565b61034161062836600461347b565b610f8e565b61032361063b366004613508565b600a6020526000908152604090205460ff1681565b61034160045481565b610341610667366004613508565b610f9a565b6103e861067a366004613508565b611178565b6103e861068d36600461347b565b6111cb565b6103e86106a03660046135ce565b6113bd565b6103e86106b336600461347b565b611476565b6106e66106c6366004613508565b60026020526000908152604090208054600190910154600f9190910b9082565b60408051600f9390930b835260208301919091520161032f565b61034161070e36600461347b565b611588565b6103e861072136600461361d565b6115c7565b6103e8610734366004613508565b6116f7565b6103e8611747565b61038761074f366004613508565b611787565b6103e861076236600461347b565b611834565b61034161077536600461347b565b611a2f565b610341610788366004613508565b60076020526000908152604090205481565b6103416107a8366004613508565b611a3b565b6103236107bb3660046136fd565b6001600160a01b03918216600090815260126020908152604080832093909416825291909152205460ff1690565b6103416107f736600461372b565b611a63565b61058261080a366004613508565b600860205260009081526040902054600f0b81565b600b546103bd906001600160a01b031681565b6103bd7f000000000000000000000000000000000000000000000000000000000000000081565b6103e8610867366004613508565b611aa3565b600082815260066020526040812082633b9aca00811061088e5761088e613764565b600302016001015490505b92915050565b6000818152600d60205260409020546001600160a01b0316806108f65760405162461bcd60e51b815260206004820152600a6024820152691a5b9d985b1a59081a5960b21b60448201526064015b60405180910390fd5b806001600160a01b0316836001600160a01b0316036109465760405162461bcd60e51b815260206004820152600c60248201526b73656c6620617070726f766560a01b60448201526064016108ed565b6000828152600d60209081526040808320546001600160a01b0385811685526012845282852033808752945291909320549216149060ff1681806109875750805b6109a35760405162461bcd60e51b81526004016108ed9061377a565b6000848152600e602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918716917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a45050505050565b600054600114610a235760405162461bcd60e51b81526004016108ed9061379a565b600260005580610a455760405162461bcd60e51b81526004016108ed906137c2565b600082815260026020908152604080832081518083019092528054600f0b808352600190910154928201929092529112610aba5760405162461bcd60e51b8152602060048201526016602482015275139bc8195e1a5cdd1a5b99c81b1bd8dac8199bdd5b9960521b60448201526064016108ed565b42816020015111610add5760405162461bcd60e51b81526004016108ed906137e6565b610aec83836000846000611af6565b5050600160005550565b6000610b0142610ed7565b905090565b610b1283838333611c8b565b505050565b600054600114610b395760405162461bcd60e51b81526004016108ed9061379a565b6002600055610b483382611df8565b610b645760405162461bcd60e51b81526004016108ed9061377a565b600081815260096020526040902054158015610b8f57506000818152600a602052604090205460ff16155b610bab5760405162461bcd60e51b81526004016108ed9061382a565b60008181526002602090815260409182902082518084019093528054600f0b835260010154908201819052421015610c255760405162461bcd60e51b815260206004820152601760248201527f546865206c6f636b20646964206e6f742065787069726500000000000000000060448201526064016108ed565b8051604080518082018252600080825260208083018281528783526002909152929020905181546001600160801b0319166001600160801b03909116178155905160019182015554600f9190910b90610c7e8282613862565b6001556040805180820190915260008082526020820152610ca29085908590611e5e565b610cd66001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633846123ee565b610cdf84612451565b60408051858152602081018490524281830152905133917f02f25270a4d87bea75db541cdfe559334a275b4a233520ed6c0a2429667cca94919081900360600190a27f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c81610d4d8482613862565b6040805192835260208301919091520160405180910390a1505060016000555050565b610b12838383604051806020016040528060008152506115c7565b6000610d978383611df8565b9392505050565b610da6613417565b600083815260066020526040902082633b9aca008110610dc857610dc8613764565b60408051608081018252600392909202929092018054600f81810b8452600160801b909104900b602083015260018101549282019290925260029091015460608201529392505050565b610e1a6124ba565b6001600160a01b0316336001600160a01b031614610e4a5760405162461bcd60e51b81526004016108ed90613879565b600081815260096020526040902054610e64906001613899565b60009182526009602052604090912055565b6000818152600760209081526040808320546006909252822081633b9aca008110610ea357610ea3613764565b6003020154600160801b9004600f0b9392505050565b6001600160a01b0381166000908152600f6020526040812054610899565b600454600081815260056020908152604080832081516080810183528154600f81810b8352600160801b909104900b93810193909352600181015491830191909152600201546060820152909190610f2f8185612528565b949350505050565b610f3f613417565b5060009081526005602090815260409182902082516080810184528154600f81810b8352600160801b909104900b92810192909252600181015492820192909252600290910154606082015290565b6000610d97838361261f565b600043821115610fde5760405162461bcd60e51b815260206004820152600f60248201526e6f6e6c79206f6c6420626c6f636b7360881b60448201526064016108ed565b6004546000610fed8483612922565b60008181526005602090815260409182902082516080810184528154600f81810b8352600160801b909104900b92810192909252600181015492820192909252600290910154606082018190529192509085101561105057506000949350505050565b60008383101561110657600060058161106a866001613899565b8152602080820192909252604090810160002081516080810183528154600f81810b8352600160801b909104900b938101939093526001810154918301919091526002015460608083018290528501519192506110c79190613862565b836040015182604001516110db9190613862565b60608501516110ea908a613862565b6110f491906138b1565b6110fe91906138e6565b915050611155565b438260600151146111555760608201516111209043613862565b604083015161112f9042613862565b606084015161113e9089613862565b61114891906138b1565b61115291906138e6565b90505b61116e828284604001516111699190613899565b612528565b9695505050505050565b6111806124ba565b6001600160a01b0316336001600160a01b0316146111b05760405162461bcd60e51b81526004016108ed90613879565b600081815260096020526040902054610e6490600190613862565b6000546001146111ed5760405162461bcd60e51b81526004016108ed9061379a565b600260008181558381526020918252604080822081518083019092528054600f0b8252600101549281019290925262093a808061122a8542613899565b61123491906138e6565b61123e91906138b1565b905060008260000151600f0b1361128b5760405162461bcd60e51b8152602060048201526011602482015270139bdd1a1a5b99c81a5cc81b1bd8dad959607a1b60448201526064016108ed565b428260200151116112cd5760405162461bcd60e51b815260206004820152600c60248201526b131bd8dac8195e1c1a5c995960a21b60448201526064016108ed565b816020015181116113205760405162461bcd60e51b815260206004820152601f60248201527f43616e206f6e6c7920696e637265617365206c6f636b206475726174696f6e0060448201526064016108ed565b61132e630784ce0042613899565b81111561137d5760405162461bcd60e51b815260206004820152601e60248201527f566f74696e67206c6f636b2063616e2062652034207965617273206d6178000060448201526064016108ed565b6113873385611df8565b6113a35760405162461bcd60e51b81526004016108ed9061377a565b6113b284600083856003611af6565b505060016000555050565b336001600160a01b0383160361140a5760405162461bcd60e51b815260206004820152601260248201527137b832b930ba37b91034b99039b2b73232b960711b60448201526064016108ed565b3360008181526012602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b6000546001146114985760405162461bcd60e51b81526004016108ed9061379a565b600260008181558381526020918252604080822081518083019092528054600f0b8083526001909101549382019390935291136115105760405162461bcd60e51b8152602060048201526016602482015275139bc8195e1a5cdd1a5b99c81b1bd8dac8199bdd5b9960521b60448201526064016108ed565b428160200151116115335760405162461bcd60e51b81526004016108ed906137e6565b61153d3384611df8565b6115595760405162461bcd60e51b81526004016108ed9061377a565b600082116115795760405162461bcd60e51b81526004016108ed906137c2565b610aec83836000846002611af6565b600080546001146115ab5760405162461bcd60e51b81526004016108ed9061379a565b60026000556115bb8383336129a8565b60016000559392505050565b6115d384848433611c8b565b823b156116f157604051630a85bd0160e11b81526001600160a01b0384169063150b7a029061160c9033908890879087906004016138fa565b6020604051808303816000875af1925050508015611647575060408051601f3d908101601f191682019092526116449181019061392d565b60015b6116ef573d808015611675576040519150601f19603f3d011682016040523d82523d6000602084013e61167a565b606091505b5080516000036116e75760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b60648201526084016108ed565b805181602001fd5b505b50505050565b6116ff6124ba565b6001600160a01b0316336001600160a01b03161461172f5760405162461bcd60e51b81526004016108ed90613879565b6000908152600a60205260409020805460ff19169055565b611785600060405180604001604052806000600f0b8152602001600081525060405180604001604052806000600f0b81526020016000815250611e5e565b565b6000818152600d60205260409020546060906001600160a01b03166117ee5760405162461bcd60e51b815260206004820152601b60248201527f517565727920666f72206e6f6e6578697374656e7420746f6b656e000000000060448201526064016108ed565b60008281526002602090815260409182902082518084019093528054600f0b83526001015490820152610d97836118258142612b12565b60208401518451600f0b612be6565b60008281526009602052604090205415801561185f57506000828152600a602052604090205460ff16155b61187b5760405162461bcd60e51b81526004016108ed9061382a565b8082036118b55760405162461bcd60e51b81526020600482015260086024820152677468652073616d6560c01b60448201526064016108ed565b6118bf3383611df8565b6118f95760405162461bcd60e51b815260206004820152600b60248201526a216f776e65722066726f6d60a81b60448201526064016108ed565b6119033382611df8565b61193b5760405162461bcd60e51b8152602060048201526009602482015268216f776e657220746f60b81b60448201526064016108ed565b6000828152600260208181526040808420815180830183528154600f90810b825260019283015482860190815288885295855283872084518086019095528054820b855290920154938301849052805194519095929490910b9211156119a55782602001516119ab565b83602001515b604080518082018252600080825260208083018281528b835260028252848320935184546001600160801b0319166001600160801b0390911617845551600190930192909255825180840190935280835290820152909150611a109087908690611e5e565b611a1986612451565b611a27858383866004611af6565b505050505050565b6000610d978383612b12565b600081815260036020526040812054439003611a5957506000919050565b6108998242612b12565b60008054600114611a865760405162461bcd60e51b81526004016108ed9061379a565b6002600055611a968484846129a8565b6001600055949350505050565b611aab6124ba565b6001600160a01b0316336001600160a01b031614611adb5760405162461bcd60e51b81526004016108ed90613879565b6000908152600a60205260409020805460ff19166001179055565b6001548290611b058682613899565b6001556040805180820190915260008082526020820152825160208085015190830152600f0b8152825187908490611b3e90839061394a565b600f0b9052508515611b5257602083018690525b6000888152600260209081526040909120845181546001600160801b0319166001600160801b0390911617815590840151600190910155611b94888285611e5e565b338715801590611bb657506004856004811115611bb357611bb3613999565b14155b15611bf057611bf06001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001682308b612d1f565b8360200151816001600160a01b03167fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6248b8b8942604051611c3494939291906139af565b60405180910390a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c83611c688a82613899565b6040805192835260208301919091520160405180910390a1505050505050505050565b600082815260096020526040902054158015611cb657506000828152600a602052604090205460ff16155b611cd25760405162461bcd60e51b81526004016108ed9061382a565b611cdc8183611df8565b611d185760405162461bcd60e51b815260206004820152600d60248201526c10b7bbb732b91039b2b73232b960991b60448201526064016108ed565b6001600160a01b038316611d5c5760405162461bcd60e51b815260206004820152600b60248201526a647374206973207a65726f60a81b60448201526064016108ed565b6000828152600e60205260409020546001600160a01b031615611d96576000828152600e6020526040902080546001600160a01b03191690555b611da08483612d57565b611daa8383612e08565b6000828152600360205260408082204390555183916001600160a01b0380871692908816917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a450505050565b6000818152600d6020908152604080832054600e8352818420546001600160a01b039182168086526012855283862088841680885295529285205492938085149392909116149060ff168280611e4b5750815b80611e535750805b979650505050505050565b611e66613417565b611e6e613417565b60045460009081908715611f9157428760200151118015611e96575060008760000151600f0b135b15611edb578651611eac90630784ce00906139ed565b600f0b602080870191909152870151611ec6904290613862565b8560200151611ed59190613a2b565b600f0b85525b428660200151118015611ef5575060008660000151600f0b135b15611f3a578551611f0b90630784ce00906139ed565b600f0b602080860191909152860151611f25904290613862565b8460200151611f349190613a2b565b600f0b84525b602080880151600090815260088252604090205490870151600f9190910b935015611f91578660200151866020015103611f7657829150611f91565b602080870151600090815260089091526040902054600f0b91505b604080516080810182526000808252602082015242918101919091524360608201528115612006575060008181526005602090815260409182902082516080810184528154600f81810b8352600160801b909104900b9281019290925260018101549282019290925260029091015460608201525b6040810151816000428310156120535760408401516120259042613862565b60608501516120349043613862565b61204690670de0b6b3a76400006138b1565b61205091906138e6565b90505b600062093a8061206381866138e6565b61206d91906138b1565b905060005b60ff8110156121c05761208862093a8083613899565b915060004283111561209c574292506120b0565b50600082815260086020526040902054600f0b5b6120dd6120bd8785613862565b88602001516120cc9190613a2b565b88516120d89190613ac0565b612e79565b600f0b875260208701516120f6906120d890839061394a565b600f0b602088015260408088018490528501519295508592670de0b6b3a7640000906121229085613862565b61212c90866138b1565b61213691906138e6565b85606001516121459190613899565b6060880152612155600189613899565b975042830361216a57504360608701526121c0565b6000888152600560209081526040918290208951918a01516001600160801b03908116600160801b0292169190911781559088015160018201556060880151600290910155506121b981613b10565b9050612072565b505060048590558b1561221e576121f3896020015189602001516121e49190613ac0565b85602001516120d8919061394a565b600f0b6020850152885188516122189161220c91613ac0565b85516120d8919061394a565b600f0b84525b6000858152600560209081526040918290208651918701516001600160801b03908116600160801b02921691909117815590850151600182015560608501516002909101558b156123e057428b6020015111156122d5576020890151612284908861394a565b96508a602001518a60200151036122a75760208801516122a49088613ac0565b96505b60208b810151600090815260089091526040902080546001600160801b0319166001600160801b0389161790555b428a602001511115612330578a602001518a6020015111156123305760208801516123009087613ac0565b60208b810151600090815260089091526040902080546001600160801b0319166001600160801b03831617905595505b60008c81526007602052604081205461234a906001613899565b905080600760008f815260200190815260200160002081905550428960400181815250504389606001818152505088600660008f815260200190815260200160002082633b9aca0081106123a0576123a0613764565b825160208401516001600160801b03908116600160801b029116176003919091029190910190815560408201516001820155606090910151600290910155505b505050505050505050505050565b6040516001600160a01b038316602482015260448101829052610b1290849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612e93565b6000818152600d60205260408120546001600160a01b031690612474908361089f565b61247e3383612d57565b60405182906000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b600b54604080516311b25aab60e21b815290516000926001600160a01b0316916346c96aac9160048083019260209291908290030181865afa158015612504573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b019190613b29565b600080839050600062093a8080836040015161254491906138e6565b61254e91906138b1565b905060005b60ff8110156126015761256962093a8083613899565b915060008583111561257d57859250612591565b50600082815260086020526040902054600f0b5b60408401516125a09084613862565b84602001516125af9190613a2b565b845185906125be908390613ac0565b600f0b9052508583036125d15750612601565b80846020018181516125e3919061394a565b600f0b90525050604083018290526125fa81613b10565b9050612553565b50815161260d90612e79565b6001600160801b031695945050505050565b6000438211156126625760405162461bcd60e51b815260206004820152600e60248201526d6f6e6c79206f6c6420626c6f636b60901b60448201526064016108ed565b600083815260076020526040812054815b6080811015612702578183101561270257600060026126928486613899565b61269d906001613899565b6126a791906138e6565b6000888152600660205260409020909150869082633b9aca0081106126ce576126ce613764565b6003020160020154116126e3578093506126f1565b6126ee600182613862565b92505b506126fb81613b10565b9050612673565b50600085815260066020526040812083633b9aca00811061272557612725613764565b60408051608081018252600392909202929092018054600f81810b8452600160801b909104900b60208301526001810154928201929092526002909101546060820152600454909150600061277a8783612922565b600081815260056020908152604080832081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606082015291925080848410156128595760006005816127de876001613899565b8152602080820192909252604090810160002081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606080830182905286015191925061283b9190613862565b9250836040015181604001516128519190613862565b91505061287d565b60608301516128689043613862565b915082604001514261287a9190613862565b90505b60408301518215801590612894575083606001518b115b156128cb578284606001518c6128aa9190613862565b6128b490846138b1565b6128be91906138e6565b6128c89082613899565b90505b60408701516128da9082613862565b87602001516128e99190613a2b565b875188906128f8908390613ac0565b600f0b905250865161290990612e79565b6001600160801b03169c9b505050505050505050505050565b60008082815b608081101561299e578183101561299e57600060026129478486613899565b612952906001613899565b61295c91906138e6565b600081815260056020526040902060020154909150871061297f5780935061298d565b61298a600182613862565b92505b5061299781613b10565b9050612928565b5090949350505050565b60008084116129c95760405162461bcd60e51b81526004016108ed906137c2565b600062093a80806129da8642613899565b6129e491906138e6565b6129ee91906138b1565b9050428111612a4e5760405162461bcd60e51b815260206004820152602660248201527f43616e206f6e6c79206c6f636b20756e74696c2074696d6520696e207468652060448201526566757475726560d01b60648201526084016108ed565b612a5c630784ce0042613899565b811115612aab5760405162461bcd60e51b815260206004820152601e60248201527f566f74696e67206c6f636b2063616e2062652034207965617273206d6178000060448201526064016108ed565b600c60008154612aba90613b10565b90915550600c54612acb8482612f65565b5060008181526002602090815260409182902082518084019093528054600f0b835260019081015491830191909152612b0991839189918691611af6565b95945050505050565b600082815260076020526040812054808203612b32576000915050610899565b600084815260066020526040812082633b9aca008110612b5457612b54613764565b60408051608081018252600392909202929092018054600f81810b8452600160801b909104900b602083015260018101549282018390526002015460608201529150612ba09085613b46565b8160200151612baf9190613a2b565b81518290612bbe908390613ac0565b600f90810b90915282516000910b12159050612bd957600081525b51600f0b91506108999050565b60606040518061012001604052806101008152602001613f4f6101009139905080612c1086612ff3565b604051602001612c21929190613b85565b604051602081830303815290604052905080612c3c85612ff3565b604051602001612c4d929190613c02565b604051602081830303815290604052905080612c6884612ff3565b604051602001612c79929190613c83565b604051602081830303815290604052905080612c9483612ff3565b604051602001612ca5929190613d05565b60405160208183030381529060405290506000612cf2612cc487612ff3565b612ccd846130f4565b604051602001612cde929190613d60565b6040516020818303038152906040526130f4565b905080604051602001612d059190613e7c565b604051602081830303815290604052915050949350505050565b6040516001600160a01b03808516602483015283166044820152606481018290526116f19085906323b872dd60e01b9060840161241a565b6000818152600d60205260409020546001600160a01b03838116911614612db05760405162461bcd60e51b815260206004820152600d60248201526c216f776e65722072656d6f766560981b60448201526064016108ed565b6000818152600d6020526040902080546001600160a01b0319169055612dd6828261325e565b6001600160a01b0382166000908152600f60205260408120805460019290612dff908490613862565b90915550505050565b6000818152600d6020908152604080832080546001600160a01b0319166001600160a01b038716908117909155808452600f80845282852080546010865284872081885286528487208890558787526011865293862093909355908452909152805460019290612dff908490613899565b60008082600f0b12612e8b5781610899565b600092915050565b6000612ee8826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661331d9092919063ffffffff16565b805190915015610b125780806020019051810190612f069190613ec1565b610b125760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016108ed565b60006001600160a01b038316612fa85760405162461bcd60e51b81526020600482015260086024820152671e995c9bc8191cdd60c21b60448201526064016108ed565b612fb28383612e08565b60405182906001600160a01b038516906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a450600192915050565b60608160000361301a5750506040805180820190915260018152600360fc1b602082015290565b8160005b8115613044578061302e81613b10565b915061303d9050600a836138e6565b915061301e565b60008167ffffffffffffffff81111561305f5761305f613607565b6040519080825280601f01601f191660200182016040528015613089576020820181803683370190505b5090505b8415610f2f5761309e600183613862565b91506130ab600a86613ede565b6130b6906030613899565b60f81b8183815181106130cb576130cb613764565b60200101906001600160f81b031916908160001a9053506130ed600a866138e6565b945061308d565b80516060906000819003613118575050604080516020810190915260008152919050565b60006003613127836002613899565b61313191906138e6565b61313c9060046138b1565b9050600061314b826020613899565b67ffffffffffffffff81111561316357613163613607565b6040519080825280601f01601f19166020018201604052801561318d576020820181803683370190505b5090506000604051806060016040528060408152602001613f0f604091399050600181016020830160005b86811015613219576003818a01810151603f601282901c8116860151600c83901c8216870151600684901c831688015192909316870151600891821b60ff94851601821b92841692909201901b91160160e01b8352600490920191016131b8565b506003860660018114613233576002811461324457613250565b613d3d60f01b600119830152613250565b603d60f81b6000198301525b505050918152949350505050565b6001600160a01b0382166000908152600f602052604081205461328390600190613862565b6000838152601160205260409020549091508082036132d2576001600160a01b0384166000908152601060209081526040808320858452825280832083905585835260119091528120556116f1565b6001600160a01b039390931660009081526010602090815260408083209383529281528282208054868452848420819055835260119091528282209490945592839055908252812055565b60606001600160a01b0384163b6133765760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016108ed565b600080856001600160a01b0316856040516133919190613ef2565b6000604051808303816000865af19150503d80600081146133ce576040519150601f19603f3d011682016040523d82523d6000602084013e6133d3565b606091505b509150915061116e828286606083156133ed575081610d97565b8251156133fd5782518084602001fd5b8160405162461bcd60e51b81526004016108ed91906134f5565b60405180608001604052806000600f0b81526020016000600f0b815260200160008152602001600081525090565b6001600160e01b03198116811461345b57600080fd5b50565b60006020828403121561347057600080fd5b8135610d9781613445565b6000806040838503121561348e57600080fd5b50508035926020909101359150565b60005b838110156134b85781810151838201526020016134a0565b838111156116f15750506000910152565b600081518084526134e181602086016020860161349d565b601f01601f19169290920160200192915050565b602081526000610d9760208301846134c9565b60006020828403121561351a57600080fd5b5035919050565b6001600160a01b038116811461345b57600080fd5b6000806040838503121561354957600080fd5b823561355481613521565b946020939093013593505050565b60008060006060848603121561357757600080fd5b833561358281613521565b9250602084013561359281613521565b929592945050506040919091013590565b6000602082840312156135b557600080fd5b8135610d9781613521565b801515811461345b57600080fd5b600080604083850312156135e157600080fd5b82356135ec81613521565b915060208301356135fc816135c0565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b6000806000806080858703121561363357600080fd5b843561363e81613521565b9350602085013561364e81613521565b925060408501359150606085013567ffffffffffffffff8082111561367257600080fd5b818701915087601f83011261368657600080fd5b81358181111561369857613698613607565b604051601f8201601f19908116603f011681019083821181831017156136c0576136c0613607565b816040528281528a60208487010111156136d957600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b6000806040838503121561371057600080fd5b823561371b81613521565b915060208301356135fc81613521565b60008060006060848603121561374057600080fd5b8335925060208401359150604084013561375981613521565b809150509250925092565b634e487b7160e01b600052603260045260246000fd5b60208082526006908201526510b7bbb732b960d11b604082015260600190565b6020808252600e908201526d1499595b9d1c985b9d0818d85b1b60921b604082015260600190565b6020808252600a90820152697a65726f2076616c756560b01b604082015260600190565b60208082526024908201527f43616e6e6f742061646420746f2065787069726564206c6f636b2e20576974686040820152636472617760e01b606082015260800190565b602080825260089082015267185d1d1858da195960c21b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b6000828210156138745761387461384c565b500390565b60208082526006908201526510bb37ba32b960d11b604082015260600190565b600082198211156138ac576138ac61384c565b500190565b60008160001904831182151516156138cb576138cb61384c565b500290565b634e487b7160e01b600052601260045260246000fd5b6000826138f5576138f56138d0565b500490565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061116e908301846134c9565b60006020828403121561393f57600080fd5b8151610d9781613445565b600081600f0b83600f0b600082128260016001607f1b03038213811516156139745761397461384c565b8260016001607f1b03190382128116156139905761399061384c565b50019392505050565b634e487b7160e01b600052602160045260246000fd5b8481526020810184905260808101600584106139db57634e487b7160e01b600052602160045260246000fd5b60408201939093526060015292915050565b600081600f0b83600f0b80613a0457613a046138d0565b60016001607f1b0319821460001982141615613a2257613a2261384c565b90059392505050565b600081600f0b83600f0b60016001607f1b03600082136000841383830485118282161615613a5b57613a5b61384c565b60016001607f1b03196000851282811687830587121615613a7e57613a7e61384c565b60008712925085820587128484161615613a9a57613a9a61384c565b85850587128184161615613ab057613ab061384c565b5050509290910295945050505050565b600081600f0b83600f0b600081128160016001607f1b031901831281151615613aeb57613aeb61384c565b8160016001607f1b03018313811615613b0657613b0661384c565b5090039392505050565b600060018201613b2257613b2261384c565b5060010190565b600060208284031215613b3b57600080fd5b8151610d9781613521565b60008083128015600160ff1b850184121615613b6457613b6461384c565b6001600160ff1b0384018313811615613b7f57613b7f61384c565b50500390565b60008351613b9781846020880161349d565b6503a37b5b2b7160d51b9083019081528351613bba81600684016020880161349d565b7f3c2f746578743e3c7465787420783d2231302220793d223135302220636c61736006929091019182015268399e913130b9b2911f60b91b6026820152602f01949350505050565b60008351613c1481846020880161349d565b6903130b630b731b2a7b3160b51b9083019081528351613c3b81600a84016020880161349d565b7f3c2f746578743e3c7465787420783d2231302220793d223233302220636c6173600a929091019182015268399e913130b9b2911f60b91b602a820152603301949350505050565b60008351613c9581846020880161349d565b6a03637b1b5b2b22fb2b732160ad1b9083019081528351613cbd81600b84016020880161349d565b7f3c2f746578743e3c7465787420783d2231302220793d223331302220636c6173600b929091019182015268399e913130b9b2911f60b91b602b820152603401949350505050565b60008351613d1781846020880161349d565b6503b30b63ab2960d51b9083019081528351613d3a81600684016020880161349d565b6c1e17ba32bc3a1f1e17b9bb339f60991b60069290910191820152601301949350505050565b6f7b226e616d65223a20226c6f636b202360801b81528251600090613d8c81601085016020880161349d565b7f222c20226465736372697074696f6e223a2022447973746f706961206c6f636b6010918401918201527f732c2063616e206265207573656420746f20626f6f737420676175676520796960308201527f656c64732c20766f7465206f6e20746f6b656e20656d697373696f6e2c20616e60508201527f64207265636569766520627269626573222c2022696d616765223a202264617460708201527f613a696d6167652f7376672b786d6c3b6261736536342c00000000000000000060908201528351613e618160a784016020880161349d565b61227d60f01b60a7929091019182015260a901949350505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c000000815260008251613eb481601d85016020870161349d565b91909101601d0192915050565b600060208284031215613ed357600080fd5b8151610d97816135c0565b600082613eed57613eed6138d0565b500690565b60008251613f0481846020870161349d565b919091019291505056fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323030302f73766722207072657365727665417370656374526174696f3d22784d696e594d696e206d656574222076696577426f783d223020302033353020333530223e3c7374796c653e2e62617365207b2066696c6c3a20626c61636b3b20666f6e742d66616d696c793a20496d706163743b20666f6e742d73697a653a20353070783b207d3c2f7374796c653e3c726563742077696474683d223130302522206865696768743d2231303025222066696c6c3d222361616161666622202f3e3c7465787420783d2231302220793d2236302220636c6173733d2262617365223ea2646970667358221220001e123b68262d2c4c86e513431aa5b208bdad60dcb0c670be75993d14b3c1cb64736f6c634300080d0033ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef00000000000000000000000039ab6574c289c3ae4d88500eec792ab5b947a5eb0000000000000000000000007377ea6afb77ba013b23306154691c231824522a

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000039ab6574c289c3ae4d88500eec792ab5b947a5eb0000000000000000000000007377ea6afb77ba013b23306154691c231824522a

-----Decoded View---------------
Arg [0] : token_ (address): 0x39ab6574c289c3ae4d88500eec792ab5b947a5eb
Arg [1] : controller_ (address): 0x7377ea6afb77ba013b23306154691c231824522a

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000039ab6574c289c3ae4d88500eec792ab5b947a5eb
Arg [1] : 0000000000000000000000007377ea6afb77ba013b23306154691c231824522a


Loading